• Django-ORM-3:orm跨表查询,子查询(基于对象的跨表查询)和联表查询(基于双下划线的跨表查询)


    正反向的概念:

    外键在哪里,哪里就是正向。

    1、由外键所在的表去查其他表,这就是正向

    2、由没有外键所在的表去查,这就是反向

    例子

    书和出版社:外键设置在书表中
    
    1、通过书表去查对应的出版社,这是正向查询
    2、通过出版社表去查出版的所有书,这就是反向
    
    正向查询按字段
    	在多对多关系表中,正方向也要加all()
    
    反向查询按表名小写.__set
    	查询结果是多个时,再加上  .all()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    子查询口诀

    • 正向查询:点外键
      • 返回结果是多个,需要加 .all()
    • 反向查询:表名小写
      • 多个结果时,需要加 _set.all()
    • 两行代码实现
      • 先拿到当前表指定的数据对象
      • 通过该数据对象+.all()或_set.all()实现跨表查,拿到另一张的整张表数据

    联表查询口诀:

    • 正向查询:外键__字段
    • 反向查询:表名小写__字段
    • 一行代码实现
      • 通过filter拿到当前表指定的数据
      • 再通过values ,获取当前表字段和跨表字段

    子查询:基于对象的跨表查询,分步骤

    正向查询:点外键字段

    ​ 特殊:查询结果 是多个时加 .all(), (多对多关系表的正向查询要加all())

    反向查询:点表名小写_set.all()

    ​ 特殊:查询结果只有一个时,直接点表名(一对一关系表)

    联表查询:基于双下划线的跨表查询,一行代码搞定

    多表查询

    一、子查询(基于对象的跨表查询)

    1、一对一关系表:

    表数据,人与人的详细信息,外键设置在人表上

    
    class Person(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=20)
        persondetail = models.OneToOneField(to='PersonDetail',on_delete=models.CASCADE,db_constraint=False)
    
        def __str__(self):
            return self.name
    
    
    class PersonDetail(models.Model):
        id = models.AutoField(primary_key=True)
        phone = models.CharField(max_length=13)
        address = models.CharField(max_length=100,null=True)
    
        def __str__(self):
            return self.phone
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1.1、子查询的反向查询
    #外键在person表中,通过个人详细信息找到人
    persondetail = models.PersonDetail.objects.get(id=1)
    
    #反向查询,一对一的反向查询,直接点Person表名小写
    person = persondetail.person
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1.2、子查询的正向查询
    #外键在person表中
    person = models.Person.objects.get(id=1)
    #正向查询,直接点外键字段获取表对象
    persondetail = person.persondetail
    print(persondetail)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、一对多关系表:

    表数据:书与出版社,一对多关系

    class Book(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=20)
        publish = models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING,db_constraint=False)
        
        def __str__(self):
            return self.name
        
        
    class Publish(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=20)
        
        def __str__(self):
            return self.name
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.1、子查询的反向查询

    '''
    反向查询,是给没有外键的表使用
    1、一对多关系表中,外键在多上,所以这种方式的反向查询,一定要加上all()
    2、对于多对多关系表中,反向查询也一定要加上all()
    3、对于一对一关系表中,反向查询就不需要加all()
    '''
    
    #外键设置在book上,查询id=1的出版社出版的所有书
    
    publish = models.Publish.objects.filter(id=1).first()
    #反向查询使用对方表小写+_set,数据有多条再加 .all()
    books = publish.book_set.all()
    for book in books:
    	print(book)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2、子查询的正向查询

    #书与出版社是一对多,外键在书上,查询某本书是哪个出版社出版的,是正向查询
    book = models.Books.objects.filter(id=3).first()
    #通过外键字段获取数据对象
    publish = book.publish
    print(publish)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3、多对多的关系表

    数据准备:书和作者,是多对多的关系,是通过第三张表实现多对多关系,哪个表的字段设置了ManyToManyField的,该字段就算是外键了。

    外键设置到书上了

    半自动生成第三张表
    class Book(models.Model):
        id = models.AutoField(primary_key=True,verbose_name='主键')
        name = models.CharField(max_length=100,verbose_name='书名')
        price = models.DecimalField(max_digits=10,decimal_places=2,verbose_name='价格')
        authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
    
    
    class Author(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=50,verbose_name='作者')
        address = models.CharField(max_length=100,verbose_name='作者地址')
    
    
    class Book2Author(models.Model):
        id = models.AutoField(primary_key=True,verbose_name='书作者关系主键')
        book = models.ForeignKey(to='Book',on_delete=models.DO_NOTHING,db_constraint=False)
        author = models.ForeignKey(to='Author',on_delete=models.DO_NOTHING,db_constraint=False)
        create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    
        class Meta:
            #书与作者联合唯一,book=1  和 author=1在表中只记录一次
            unique_together=['book','author']
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    3.1、子查询的反向查询
    #查看某个作者的写过的所有书,外键在书表中
    author = models.Author.objects.get(id=1)
    #多对多都是:表名小写_set.all()
    books = author.book_set.all()
    print(books)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    3.2、子查询的正向查询
    book = models.Book.objects.get(id=2)
    #多对多的正向查询,需要加all()  因为是多对多关系,系统默认就是一本书有多个作者的,即使已经知道该书只有一个作者也是需要使用all()的
    authors = book.authors.all()
    print(authors)
    
    • 1
    • 2
    • 3
    • 4
    注意,输入数据对象时:输出了index.Author.None

    你的查询没有错误,只是少了.all() , 因为你这个查询默认是返回多条数据的,所以需要使用all()方法来拿。

    子查询的总结

    1、正向查询时,通过外键跨表,返回多个数据时,要使用.all()获取。[只有多对多关系需要.all(),系统默认会拿到多个数据]
    
    2、反向查询时,通过表名小写[_set.all()],返回多个数据时就必须加_set.all(). [在一对一关系表中,就直接表名小写就可以了]、
    
    3、带all()的结果是列表套字典
    4、不带all()的结果就是query对象了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    二、联表查询(基于双下划线的跨表查询)

    1、一对一关系表查询

    正向查询:通过 外键__字段 获取另一张的字段数据

    反向查询:通过 表名小写__字段 获取另一张的字段数据

    数据准备:人和人的详细信息,一对一关系,外键在人表上。

    1.1、正向查询

    #查询id=1的人的姓名,地址和电话(在另一张表上),正向查询直接外键跨到另一张表中,通过__字段,获取另一张表的字段数据
    person = models.Person.objects.filter(id=1).values('persondetail__address','persondetail__phone','name')
    #下面拿到的是一个字典
    print(person.first())
    print(person.first().get('persondetail__address'))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.2、反向查询

    #通过个人详细信息找到对应的名字,反向查询,通过表名小写进行跨表,__字段获取指定字段数据
    persondetail = models.PersonDetail.objects.filter(id=2).values('person__name','address','phone')
    #拿到的是一个列表套字段,values决定的
    print(persondetail)
    #拿到个人的名字  print(persondetail.first().get('person__name'))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、一对多关系的联表查询,基于双下划线

    数据准备:books 和publish,多对一关系

    class Books(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=20)
        publish = models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING,db_constraint=False)
    
        def __str__(self):
            return self.name
    
    
    class Publish(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=20)
    
        def __str__(self):
            return self.name
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    1、正向查询

    '''拿到聊斋书的出版社信息和聊斋书的信息'''
    #正向查询通过外键__字段,拿到另一张表的字段数据,多查一,返回一个数据,
    books = models.Books.objects.filter(name='聊斋').values('name','id','publish__name','publish__id')
    print(books)
    
    结果:格式列表套字典,vlaues决定
    <QuerySet [{'name': '聊斋', 'id': 3, 'publish__name': '南京出版社', 'publish__id': 1}]>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、反向查询

    '''拿到南京出版社出版的所有书籍'''
    #反向查询,通过表名小写__字段,获取另一张表名中的字段数据
    publish = models.Publish.objects.filter(name='南京出版社').values('name','id','books__name','books__id')
    print(publish)
    #结果:列表套字典,values决定
    <QuerySet [{'name': '南京出版社', 'id': 1, 'books__name': '水浒传', 'books__id': 5}, {'name': '南京出版社', 'id': 1, 'books__name': '西游记', 'books__id': 4}, {'name': '南京出版社', 'id': 1, 'books__name': '聊斋', 'books__id': 3}]>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3、多对多关系的联表查询,基于双下划线

    数据准备: book和author,外键authors在book上

    class Book(models.Model):
        id = models.AutoField(primary_key=True,verbose_name='主键')
        name = models.CharField(max_length=100,verbose_name='书名')
        price = models.DecimalField(max_digits=10,decimal_places=2,verbose_name='价格')
        authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
        def __str__(self):
            return self.name
    
    
    class Author(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=50,verbose_name='作者')
        address = models.CharField(max_length=100,verbose_name='作者地址')
    
        def __str__(self):
            return self.name
    
    
    class Book2Author(models.Model):
        id = models.AutoField(primary_key=True,verbose_name='书作者关系主键')
        book = models.ForeignKey(to='Book',on_delete=models.DO_NOTHING,db_constraint=False)
        author = models.ForeignKey(to='Author',on_delete=models.DO_NOTHING,db_constraint=False)
        create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
    
        class Meta:
            #书与作者联合唯一,book=1  和 author=1在表中只记录一次
            unique_together=['book','author']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    1、正向查询

    #正向查询,通过  外键__字段  ,返回的是一个列表套字典,即使拿到的一个数据,也是这种格式
    '''拿到西游记的所以作者'''
    book = models.Book.objects.filter(name__startswith='西游').values('name','authors__name','price')
        print(book)
    
    打印:
    <QuerySet [{'name': '西游记', 'authors__name': 'lhz', 'price': Decimal('58.40')}, {'name': '西游记', 'authors__name': 'zzh', 'price': Decimal('58.40')}]>
    
        print(book.first().get('authors_name'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、反向查询

    '''找到lhz作者的所有书籍'''
    #外键在book中,不在author中
    #反向查询通过 表名小写__字段名
    author = models.Author.objects.filter(name__startswith='lhz').values('name','book__name','book__price')
    
    print(author)
    打印:
    <QuerySet [{'name': 'lhz', 'book__name': '水浒传', 'book__price': Decimal('56.40')}, {'name': 'lhz', 'book__name': '西游记', 'book__price': Decimal('58.40')}]>
       
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    联表查询总结:

    1、通过filter过滤当前表数据

    2、通过values或values_list 来获取指定 的字段数据 (一般使用values,结果列表套字典),拿到当前表字段或通过外键/表名小写__字段拿到跨表的字段数据。

    3、正向查询,直接通过外键__字段,获取跨表的数据

    4、反向查询,直接通过表名小写__字段,获取跨表的数据

    5、无论正向还是反向,结果都是被列表套住的字典或元组。

  • 相关阅读:
    【Mapbox基础功能】01、前置学习资料(文档地址、accesstoken生成)
    Shelby American 汽车 NFT 系列来袭!
    LeetCode | 876. Middle of the Linked List
    什么是泛型?Java基础之泛型详细知识点总结
    Java | Leetcode Java题解之第200题岛屿数量
    Linux 利用 iostat 和 iotop 进行 IO 分析
    【计网】(二)MAC地址与IP地址
    【已拿offer】最新AI产品经理大厂面经(含百度&腾讯&科大讯飞&商汤&蚂蚁金服)
    LeetCode 算法:LRU 缓存 c++
    Day20_9 前端入门之CSS样式
  • 原文地址:https://blog.csdn.net/weixin_46371752/article/details/126375988