• Django--ORM 多表查询


    目录

    数据准备

    正向查询与反向查询

    基于对象的跨表查询

    正向查询

    一对多

    多对多

    一对一

    反向查询

    一对多

    多对多

    一对一

    正向查询

    反向查询

    基于双下线的跨表查询

    正向查询

    一对一

    一对多

    多对多

    反向查询

    一对一

    一对多

    多对多

    双下高阶正反向查询

    使用filter()的双下线查询

    连续跨多张表查询


    数据准备

    moduls.py

    1. # 构建表结构
    2. from django.db import models
    3. # 表app01_publish
    4. class Publish(models.Model):
    5. name = models.CharField(max_length=20)
    6. addr = models.CharField(max_length=20)
    7. # 表app01_author_detail
    8. class Author_Detail(models.Model):
    9. tel = models.CharField(max_length=20)
    10. # 表app01_author
    11. class Author(models.Model):
    12. name = models.CharField(max_length=20)
    13. age = models.IntegerField()
    14. # 表app01_author一对一表app01_authordetail
    15. detail = models.OneToOneField(to='Author_Detail',to_field='id',unique=True,on_delete=models.CASCADE)
    16. # 表app01_book
    17. class Book(models.Model):
    18. title = models.CharField(max_length=20)
    19. price = models.DecimalField(max_digits=8, decimal_places=2)
    20. pub_date = models.DateField(auto_now_add=True)
    21. # 表app01_book多对一表app01_publish,参数to指定模型名,参数to_field指定要关联的那个字段
    22. publish = models.ForeignKey(to='Publish',to_field='id',on_delete=models.CASCADE)
    23. # 我们自己写sql时,针对书籍表与作者表的多对关系,需要自己创建新表,而基于django的orm,下面这一行代码可以帮我们自动创建那张关系表
    24. authors=models.ManyToManyField(to='Author')
    25. # 变量名为authors,则新表名为app01_book_authors,若变量名为xxx,则新表名为app01_book_xxx

    tests.py

    1. # 添加数据
    2. import os
    3. if __name__ == "__main__":
    4. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "book_sys.settings")
    5. import django
    6. django.setup()
    7. from app01.models import *
    8. # 1、先添加没有外键字段的 author_detail表 pubulish表
    9. Author_Detail.objects.create(tel='123456789')
    10. Author_Detail.objects.create(tel='987654321')
    11. Author_Detail.objects.create(tel='000000000')
    12. Publish.objects.create(name='北方出版社',addr='北京')
    13. Publish.objects.create(name='南方出版社',addr='南京')
    14. Publish.objects.create(name='东方出版社',addr='上海')
    15. Publish.objects.create(name='西方出版社',addr='西安')
    16. # 2、添加 author表 book表
    17. Author.objects.create(name='frank',age=31 ,detail_id=1)
    18. Author.objects.create(name='lili',age=29 ,detail_id=2)
    19. Author.objects.create(name='tank',age=42 ,detail_id=3)
    20. Book.objects.create(title='三国演义',price=200 ,publish_id=1)
    21. Book.objects.create(title='三国志',price=198.5 ,publish_id=2)
    22. Book.objects.create(title='红楼梦',price=255.43 ,publish_id=2)
    23. Book.objects.create(title='西游记',price=300.5 ,publish_id=3)
    24. Book.objects.create(title='西厢记',price=213.4 ,publish_id=4)
    25. Book.objects.create(title='水浒传',price=199 ,publish_id=1)
    26. # 3、最后操作 author_book表,由于使用的是 ManyToMany 字段自动生成的,所以要基于外键所在的表进行操作
    27. book_obj1=Book.objects.filter(pk=1).first()
    28. book_obj1.authors.add(1,2)
    29. book_obj2 = Book.objects.filter(pk=2).first()
    30. book_obj2.authors.add(1)
    31. book_obj3 = Book.objects.filter(pk=3).first()
    32. author_obj1 = Author.objects.filter(pk=1).first()
    33. author_obj2 = Author.objects.filter(pk=2).first()
    34. book_obj3.authors.add(author_obj1,author_obj2)
    35. book_obj4 = Book.objects.filter(pk=4).first()
    36. book_obj4.authors.add(3,2)
    37. book_obj5 = Book.objects.filter(pk=5).first()
    38. book_obj5.authors.add(3)
    39. book_obj6 = Book.objects.filter(pk=6).first()
    40. book_obj6.authors.add(1,3)

    正向查询与反向查询

    一出版社和书籍为例, 书记表含有出版社表的外键字段

    正向查询>>> 书籍查询出版社

    反向查询>>> 出版社查询书籍

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

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

    基于对象的跨表查询

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

    正向查询

    一对多

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

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

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

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

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

    多对多

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

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

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

    3. 获取作者对象的姓名

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

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

    1. book_obj = Book.objects.filter(pk=3).first()
    2. res = book_obj.authors
    3. res1 = book_obj.authors.all()
    4. print(res1)
    5. # , ]>

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

    一对一

    查询作者lili的号码

    1.查询作者对象

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

    3. 获取详情对象的tel

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

    反向查询

    一对多

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

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

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

    3. 表名小写_set.all()

    4. 获取书籍名称

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

    多对多

    查询作者lili写过的书籍

    1.获取作者对象

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

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

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

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

    一对一

    查询号码为0000000的作者

    1. 查询作者对象详情

    2.外键字段在作者表中, 同样是证词昂查询, 那么只需要按照字段detail 查询即可

    3. 获取详情对象的tel 

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

    方法总结

    正向查询

    一对一, 一对多的方法是一样的, 按照字段查询就能够直接找到对象 --- 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的手机号

    1. res = Author.objects.filter(name='frank').values('detail__tel')
    2. print(res)
    3. #
    4. print(res.first())
    5. # {'detail__tel': '123456789'}

    一对多

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

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

    多对多

    查询三国演义的所有作者

    1. res = Book.objects.filter(title='三国演义').values('authors__name')
    2. print(res)
    3. #

    反向查询

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

    一对一

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

    1. res = Author_Detail.objects.filter(tel='123456789').values('author__name')
    2. print(res)
    3. #

    一对多

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

    1. res = Publish.objects.filter(name='北方出版社').values('book__title')
    2. print(res)
    3. #

    多对多

    查询lili出版的所有书籍

    1. res = Author.objects.filter(name='lili').values('book__title')
    2. print(res)
    3. #

    方法总结:

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

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

    双下高阶正反向查询

    使用filter()的双下线查询

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

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

    查询书籍主键为反向, 使用.filter('表名小写__被关联表中的字段')

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

    连续跨多张表查询

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

    1. # 需求1:查询北京出版社出版过的所有书籍的名字以及作者的姓名、手机号
    2. # 方式一:基表为Publish
    3. res=Publish.objects.filter(name='北方出版社').values_list('book__title','book__authors__name','book__authors__author_detail__tel')
    4. # 方式二:基表为Book
    5. res=Book.objects.filter(publish__name='北方出版社').values_list('title','authors__name','authors__author_detail__tel')
    6. # 循环打印结果均为
    7. for obj in res:
    8. print(obj)
    9. # 需求2:查询手机号以186开头的作者出版过的所有书籍名称以及出版社名称
    10. # 方式一:基表为AuthorDetail
    11. res=AuthorDetail.objects.filter(tel__startswith='186').values_list('author__book__title','author__book__publish__name')
    12. # 方式二:基表为Book
    13. res=Book.objects.filter(authors__author_detail__tel__startswith='186').values_list('title','publish__name')
    14. # 方式三:基表为Publish
    15. res=Publish.objects.filter(book__authors__author_detail__tel__startswith='186').values_list('book__title','name')
    16. # 循环打印结果均为
    17. for obj in res:
    18. print(obj)

  • 相关阅读:
    网络——IPv6(一)
    Java 的四种引用方式
    避坑必看:C++移植C语言结构体char短整形导致生产事故分析
    SpringBoot集成ES、使用Java API对其进行简单的测试
    HiAI Foundation助力端侧音视频AI能力,高性能低功耗释放云侧成本
    MyBatis-Plus(二)- 进阶使用
    基于多目标粒子群优化算法的冷热电联供型综合能源系统运行优化(Matlab代码实现)
    Spring源码解析——IOC之bean 的初始化
    主动降噪耳机排行榜10强,主动降噪耳机十大品牌
    算力获得计划——gpu系列说明
  • 原文地址:https://blog.csdn.net/weixin_67531112/article/details/126681844