• Django--ORM 多表查询


    数据准备

    moduls.py

    # 构建表结构
    from django.db import models
    
    # 表app01_publish
    class Publish(models.Model):
        name = models.CharField(max_length=20)
        addr = models.CharField(max_length=20)
    
    # 表app01_author_detail
    class Author_Detail(models.Model):
        tel = models.CharField(max_length=20)
    
    # 表app01_author
    class Author(models.Model):
        name = models.CharField(max_length=20)
        age = models.IntegerField()
    
        # 表app01_author一对一表app01_authordetail
        detail = models.OneToOneField(to='Author_Detail',to_field='id',unique=True,on_delete=models.CASCADE)
    
    
    # 表app01_book
    class Book(models.Model):
        title = models.CharField(max_length=20)
        price = models.DecimalField(max_digits=8, decimal_places=2)
        pub_date = models.DateField(auto_now_add=True)
    
        # 表app01_book多对一表app01_publish,参数to指定模型名,参数to_field指定要关联的那个字段
        publish = models.ForeignKey(to='Publish',to_field='id',on_delete=models.CASCADE)
    
        # 我们自己写sql时,针对书籍表与作者表的多对关系,需要自己创建新表,而基于django的orm,下面这一行代码可以帮我们自动创建那张关系表
        authors=models.ManyToManyField(to='Author')
        # 变量名为authors,则新表名为app01_book_authors,若变量名为xxx,则新表名为app01_book_xxx
    
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    tests.py

    # 添加数据
    
    import os
    
    if __name__ == "__main__":
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "book_sys.settings")
        import django
        django.setup()
    
        from app01.models import *
        # 1、先添加没有外键字段的 author_detail表 pubulish表
        Author_Detail.objects.create(tel='123456789')
        Author_Detail.objects.create(tel='987654321')
        Author_Detail.objects.create(tel='000000000')
    
        Publish.objects.create(name='北方出版社',addr='北京')
        Publish.objects.create(name='南方出版社',addr='南京')
        Publish.objects.create(name='东方出版社',addr='上海')
        Publish.objects.create(name='西方出版社',addr='西安')
    
        # 2、添加 author表 book表
        Author.objects.create(name='frank',age=31 ,detail_id=1)
        Author.objects.create(name='lili',age=29 ,detail_id=2)
        Author.objects.create(name='tank',age=42 ,detail_id=3)
    
        Book.objects.create(title='三国演义',price=200 ,publish_id=1)
        Book.objects.create(title='三国志',price=198.5 ,publish_id=2)
        Book.objects.create(title='红楼梦',price=255.43 ,publish_id=2)
        Book.objects.create(title='西游记',price=300.5 ,publish_id=3)
        Book.objects.create(title='西厢记',price=213.4 ,publish_id=4)
        Book.objects.create(title='水浒传',price=199 ,publish_id=1)
    
        # 3、最后操作 author_book表,由于使用的是 ManyToMany 字段自动生成的,所以要基于外键所在的表进行操作
        book_obj1=Book.objects.filter(pk=1).first()
        book_obj1.authors.add(1,2)
    
        book_obj2 = Book.objects.filter(pk=2).first()
        book_obj2.authors.add(1)
    
        book_obj3 = Book.objects.filter(pk=3).first()
        author_obj1 = Author.objects.filter(pk=1).first()
        author_obj2 = Author.objects.filter(pk=2).first()
        book_obj3.authors.add(author_obj1,author_obj2)
    
        book_obj4 = Book.objects.filter(pk=4).first()
        book_obj4.authors.add(3,2)
    
        book_obj5 = Book.objects.filter(pk=5).first()
        book_obj5.authors.add(3)
    
        book_obj6 = Book.objects.filter(pk=6).first()
        book_obj6.authors.add(1,3)
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

     

    一、正向查询与反向查询

    以出版社和书籍表为例:书籍表含有出版社表的外键字段

    正向查询:由书籍 查询 出版社

    反向查询:由 出版社 查询 书籍

    总结:当前查询对象是否含有外键字段,有就是正向,没有就是反向

    正向查询按字段,反向查询按表名


     

    二、基于对象的跨表查询

    相当于 MySQL 中的子查询:将一张表的查询结果用括号括起来,当做另一条 SQL 语句的条件。

    正向查询

     

    一对多

    查询书籍主键为 5 的出版社名称

    1、查主键为 5 的书籍对象

    2、根据书籍对象的外键字段 publish 获取到出版社对象

    3、由出版社对象获取到名称

    book_obj = Book.objects.filter(pk=5).first()
    res = book_obj.publish
    print(res)
    # Publish object
    print(res.name)
    # 西方出版社
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

     

    多对多

    查询书籍主键为 3 的作者姓名

    1、查询书籍主键为 3 的书籍对象

    2、外键字段在书籍表中,同样是正向查询,那么只需要按照字段 authors 查询即可

    3、获取作者对象的姓名

    book_obj = Book.objects.filter(pk=3).first()
    res = book_obj.authors
    print(res)
    # app01.Author.None
    
    • 1
    • 2
    • 3
    • 4

    注意:由于字段 authors 是多对多的外键字段,此时拿到的对象还需要进一步的操作

    book_obj = Book.objects.filter(pk=3).first()
    res = book_obj.authors
    
    res1 = book_obj.authors.all()
    print(res1)
    # <QuerySet [<Author: Author object>, <Author: Author object>]>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后再 for 循环取各自的姓名即可

     

    一对一

    查询作者 lili 的号码

    1、查询作者对象

    2、外键字段在作者表中,同样是正向查询,那么只需要按照字段 detail 查询即可

    3、获取详情对象的tel

    author_obj = Author.objects.filter(name='lili').first()
    res = author_obj.detail
    print(res)
    print(res.tel)
    
    • 1
    • 2
    • 3
    • 4

     

    反向查询

     

    一对多

    查询东方出版社出版的书籍

    1、先获取东方出版社的对象

    2、出版社没有外键字段,去查书籍是反向查询

    3、表名小写_set.all()

    4、获取书籍名称

    publish_obj=Publish.objects.filter(name='东方出版社').first()
    print(publish_obj)
    # Publish object
    
    res = publish_obj.book_set
    print(res)
    # app01.Book.None
    
    res1 = res.all()
    print(res1)
    # <QuerySet [<Book: Book object>]>
    for obj in res1:
      print(obj.title)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

     

    多对多

    查询作者 lili 写过的书籍

    1、获取作者对象

    2、作者表中没有书籍表的外键,所以是反向查询

    3、.book_set.all()获取书籍对象

    4、再获取书籍对象的名称

    author_obj = Author.objects.filter(name='lili').first()
    res = author_obj.book_set
    print(res)
    # app01.Book.None
    res1 = res.all()
    print(res1)
    # <QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>]>
    for obj in res1:
      print(obj.title, end='')
    # 三国演义,红楼梦,西游记,
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

     

    一对一

    查询号码为 000000000 的作者

    1、查询作者详情对象

    2、外键字段在作者表中,同样是正向查询,那么只需要按照字段 detail 查询即可

    3、获取详情对象的tel

    detail_obj = Author_Detail.objects.filter(tel='000000000').first()
    print(detail_obj)
    # Author_Detail object
    res = detail_obj.author
    print(res)
    # Author object
    print(res.name)
    # tank
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

     

    方法总结:

    正向查询:

    一对一,一对多的方法是一样的,按照字段查询就能够直接找到对象----author_obj.detail

    一对多 按照字段查询后会返回一个.None的对象,需要在字段后面再加上.all()----book_obj.authors.all()

    反向查询:

    一对多,多对多的方法是一样的,按照表名小写 在跟上_set.all()----author_obj.book_set.all()

    一对一 则直接按照表名小写 能够直接拿到----detail_obj.author

    当查询到的结果中最后以.None结尾,都需要在原来的查询方法后面再跟上.all()才能获得想要的结果


     

    三、基于双下划线的跨表查询

    相当于 MySQL 中的连表查询:将两张表或多张表连接成一张表进行查询。

    正向查询

    正向查询,按关联字段+双下划线:.values('关联字段__被关联表中的字段'),返回的是一个QuerySet 对象

    一对一

    查询作者 frank 的手机号

    res = Author.objects.filter(name='frank').values('detail__tel')
    print(res)
    # <QuerySet [{'detail__tel': '123456789'}]>
    
    print(res.first())
    # {'detail__tel': '123456789'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

     

    一对多

    查询三国演义的出版社名字

    res = Book.objects.filter(title='三国演义').values('publish__name')
    print(res)
    print(res.first())
    
    # <QuerySet [{'publish__name': '北方出版社'}]>
    # {'publish__name': '北方出版社'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

     

    多对多

    查询三国演义的所有作者

    res = Book.objects.filter(title='三国演义').values('authors__name')
    print(res)
    # <QuerySet [{'authors__name': 'frank'}, {'authors__name': 'lili'}]>
    
    • 1
    • 2
    • 3

     

    反向查询

    按模型名(小写)+双下划线:.values('表名小写__被关联表中的字段'),返回的是一个QuerySet 对象

    一对一

    查询手机号为’123456789’的作者名

    res = Author_Detail.objects.filter(tel='123456789').values('author__name')
    print(res)
    # <QuerySet [{'author__name': 'frank'}]>
    
    • 1
    • 2
    • 3

     

    一对多

    查询北方出版社出版的所有书籍名字

    res = Publish.objects.filter(name='北方出版社').values('book__title')
    print(res)
    # <QuerySet [{'book__title': '三国演义'}, {'book__title': '水浒传'}]>
    
    • 1
    • 2
    • 3

     

    多对多

    查询作者lili出版的所有书籍

    res = Author.objects.filter(name='lili').values('book__title')
    print(res)
    # <QuerySet [{'book__title': '三国演义'}, {'book__title': '红楼梦'}, {'book__title': '西游记'}]>
    
    • 1
    • 2
    • 3

    方法总结:

    正向查询,按关联字段+双下划线:.values('关联字段__被关联表中的字段'),返回的是一个QuerySet 对象

    按模型名(小写)+双下划线:.values('表名小写__被关联表中的字段'),返回的是一个QuerySet 对象

    对象中保存的是字典类型


     

    四、双下高阶正反向查询

    使用filter()的双下划线查询

    首先需要考虑是正向查询还是反向查询,确定括号内使用的方法,但是括号外面不是使用values,而是使用filter!!!

    注意:使用 filter 方法字段是不能加引号的,values 需要加引号

    查询书籍主键为 4 的出版社名称

    以出版社为基表,出版社查书籍为反向,使用.filter('表名小写__被关联表中的字段')

    res = Publish.objects.filter(book__pk= 4)
    print(res)
    
    # <QuerySet [<Publish: Publish object>]>
    res = Publish.objects.filter(book__title= '西游记')
    print(res)
    # 结果相同
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

     

    连续跨多张表查询

    套路与上面的案例都是一样的,可以连续接n个双下划线,只需要在每次连双下划线时,确定是每个双下划线后面是正向查询还是反向查询即可

    # 需求1:查询北京出版社出版过的所有书籍的名字以及作者的姓名、手机号
    # 方式一:基表为Publish
    res=Publish.objects.filter(name='北方出版社').values_list('book__title','book__authors__name','book__authors__author_detail__tel')
    
    # 方式二:基表为Book
    res=Book.objects.filter(publish__name='北方出版社').values_list('title','authors__name','authors__author_detail__tel')
    
    # 循环打印结果均为
    for obj in res:
        print(obj)
    
    
    
    # 需求2:查询手机号以186开头的作者出版过的所有书籍名称以及出版社名称
    # 方式一:基表为AuthorDetail
    res=AuthorDetail.objects.filter(tel__startswith='186').values_list('author__book__title','author__book__publish__name')
    
    # 方式二:基表为Book
    res=Book.objects.filter(authors__author_detail__tel__startswith='186').values_list('title','publish__name')
    
    # 方式三:基表为Publish
    res=Publish.objects.filter(book__authors__author_detail__tel__startswith='186').values_list('book__title','name')
    
    # 循环打印结果均为
    for obj in res:
        print(obj)
    
    
    • 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
  • 相关阅读:
    【Paper】2013_Attitude and Altitude Controller Design for Quad-Rotor Type MAVs
    【环境配置】使用Docker搭建LAMP环境
    实时数据仓库
    Comparison method violates its general contract解决办法
    精选大厂10道常考python面试题!
    怎么在电脑上多屏播放和实时视频输入,ProVideoPlayer 功能介绍
    01创建型设计模式——单例模式
    Openssl生成证书-nginx使用ssl
    java面试题
    PG数据库字符截取
  • 原文地址:https://blog.csdn.net/weixin_43988680/article/details/125450161