一、简单ORM的SQL

  1. # 补充知识点:
  2. # ret=Emp.objects.all()
  3. # print(ret) # select * from emp
  4. # ret=Emp.objects.values("name")
  5. # print(ret) # select name from emp

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

原理:多条select语句。

一对多查询

  • 正向查询:按字段
  • 反向查询:表名小写_set

    1. # 一对多查询的正向查询 : 查询金瓶梅这本书的出版社的名字
    2. book_obj=Book.objects.filter(title="金瓶梅").first()
    3. print(book_obj.publish) # 与这本书关联出版社对象
    4. print(book_obj.publish.name)
    5. # 对应sql:
    6. # select publish_id from Book where title="金瓶梅"
    7. # select name from Publish where id=1
    8. # 一对多查询的反向查询 : 查询人民出版社出版过的书籍名称
    9. publish=Publish.objects.filter(name="人民出版社").first()
    10. ret=publish.book_set.all()
    11. print(ret)

    多对多查询

  • 正向查询:按字段

  • 反向查询:表名小写_set ```python

    多对多查询的正向查询 : 查询金瓶梅这本书的所有作者的名字

    book_obj=Book.objects.filter(title=”金瓶梅”).first() author_list=book_obj.authors.all() # queryset对象 [author_obj1,…]

for author in author_list: print(author.name)

多对多查询的反向查询 : 查询ecithy出版过的所有书籍名称

ecithy=Author.objects.filter(name=”ecithy”).first()

book_list=ecithy.book_set.all() for book in book_list: print(book.title)

  1. <a name="jWkmB"></a>
  2. ## 一对一查询
  3. - 正向查询:按字段
  4. - 反向查询:表名小写
  5. ```python
  6. # 一对一查询的正向查询 : 查询ecithy的手机号
  7. ecithy=Author.objects.filter(name="ecithy").first()
  8. print(ecithy.authordetail.telephone)
  9. # 一对一查询的反向查询 : 查询手机号为110的作者的名字和年龄
  10. ad=AuthorDetail.objects.filter(telephone="110").first()
  11. print(ad.author.name)
  12. print(ad.author.age)
  13. return HttpResponse("OK")

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

原理:联表select…join…。

  • 正向查询按字段
  • 反向查询按表名小写(用来告诉ORM引擎join哪张表)

不是本对象字段,就要加下划线,跟正、逆向没关系。

一对多查询

  1. # 一对多查询 : 查询金瓶梅这本书的出版社的名字
  2. # 方式1:
  3. ret=Book.objects.filter(title="金瓶梅").values("publish__name")
  4. print(ret) # <QuerySet [{'publish__name': '南京出版社'}]>
  5. # 方式2: 如果有related_name,下面的book用related_name值
  6. ret=Publish.objects.filter(book__title="金瓶梅").values("name")
  7. print(ret)
  8. return HttpResponse("OK")

多对多查询

  1. # 多对多查询 : 查询金瓶梅这本书的所有作者的名字
  2. # 方式1:
  3. # 需求: 通过Book表join与其关联的Author表,属于正向查询:按字段authors通知ORM引擎join book_authors与author
  4. ret=Book.objects.filter(title="金瓶梅").values("authors__name")
  5. print(ret) # <QuerySet [{'authors__name': 'ecithy'}, {'authors__name': 'egon'}]>
  6. # 方式2:
  7. # 需求: 通过Author表join与其关联的Book表,属于反向查询:按表名小写book通知ORM引擎join book_authors与book表
  8. ret=Author.objects.filter(book__title="金瓶梅").values("name")
  9. print(ret) # <QuerySet [{'name': 'ecithy'}, {'name': 'egon'}]>

一对一查询

  1. # 一对一查询的查询 : 查询ecithy的手机号
  2. # 方式1:
  3. # 需求: 通过Author表join与其关联的AuthorDetail表,属于正向查询:按字段authordetail通知ORM引擎join Authordetail表
  4. ret = Author.objects.filter(name="ecithy").values( "authordetail__telephone")
  5. print(ret) # <QuerySet [{'authordetail__telephone': 110}]>
  6. # 方式2:
  7. # 需求: 通过AuthorDetail表join与其关联的Author表,属于反向查询:按表名小写author通知ORM引擎join Author表
  8. ret = AuthorDetail.objects.filter(author__name="ecithy").values("telephone")
  9. print(ret) # <QuerySet [{'telephone': 110}]>
  10. return HttpResponse("OK")

连续跨表

只要有外键关系,就能跨表。

  1. # 进阶练习:
  2. # 练习: 手机号以110开头的作者出版过的所有书籍名称以及书籍出版社名称
  3. # 方式1:
  4. # 需求: 通过Book表join AuthorDetail表, Book与AuthorDetail无关联,所以必需连续跨表
  5. ret = Book.objects.filter(authors__authordetail__telephone__startswith="110").values("title", "publish__name")
  6. print(ret)
  7. #
  8. # 方式2:
  9. ret = Author.objects.filter(authordetail__telephone__startswith="110").values("book__title", "book__publish__name")
  10. print(ret)

四、聚合与分组查询

  1. class Emp(models.Model):
  2. name=models.CharField(max_length=32)
  3. age=models.IntegerField()
  4. salary=models.DecimalField(max_digits=8,decimal_places=2)
  5. dep=models.CharField(max_length=32)
  6. province=models.CharField(max_length=32)

image.png

聚合查询

聚合 aggregate:返回值是一个字典,不是queryset

  1. from django.db.models import Avg, Max, Min, Count
  2. ret = Book.objects.all().aggregate(avg_price=Avg("price"), max_price=Max("price"))
  3. print(ret) # {'avg_price': 151.0, 'max_price': Decimal('301.00')}

分组查询

单表分组查询

分组查询 annotate ,返回值依然是queryset

  1. from django.db.models import Avg, Max, Min, Count
  2. # 示例1
  3. # 查询每一个部门的名称以及员工的平均薪水
  4. # select dep,Avg(salary) from emp group by dep
  5. ret = Emp.objects.values("dep").annotate(avg_salary=Avg("salary"))
  6. print(ret) # <QuerySet [{'avg_salary': 5000.0, 'dep': '保安部'}, {'avg_salary': 51000.0, 'dep': '教学部'}]>
  7. # 单表分组查询的ORM语法: 单表模型.objects.values("group by的字段").annotate(聚合函数("统计字段"))
  8. # 示例2
  9. # 查询每一个省份的名称以及员工数
  10. ret = Emp.objects.values("province").annotate(c=Count("id"))
  11. print(ret) # <QuerySet [{'province': '北京市', 'c': 1}, {'province': '广东省', 'c': 1}, {'province': '浙江省', 'c': 1}]>

多表分组查询

  1. from django.db.models import Avg, Max, Min, Count
  2. # 示例1 查询每一个出版社的名称以及出版的书籍个数
  3. ret = Publish.objects.values("nid").annotate(c=Count("book__title"))
  4. print(ret) # <QuerySet [{'nid': 1, 'c': 3}, {'nid': 2, 'c': 1}]>
  5. ret = Publish.objects.values("name").annotate(c=Count("book__title"))
  6. print(ret) # <QuerySet [{'name': '人民出版社', 'c': 3}, {'name': '南京出版社', 'c': 1}]>
  7. ret = Publish.objects.values("nid").annotate(c=Count("book__title")).values("name", "c")
  8. print(ret) # 可自定义输出字段 : <QuerySet [{'name': '人民出版社', 'c': 3}, {'name': '南京出版社', 'c': 1}]>
  9. # 示例2 查询每一个作者的名字以及出版过的书籍的最高价格
  10. ret = Author.objects.values("pk").annotate(max_price=Max("book__price")).values("name", "max_price")
  11. print(ret)
  12. # 总结 跨表的分组查询的模型:
  13. # 每一个后表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段))
  14. # 示例3 查询每一个书籍的名称以及对应的作者个数
  15. ret = Book.objects.values("pk").annotate(c=Count("authors__name")).values("title", "c")
  16. print(ret)

多表分组查询进阶

  1. # 示例1 查询每一个出版社的名称以及出版的书籍个数
  2. # ret=Publish.objects.values("nid").annotate(c=Count("book__title")).values("name","email","c")
  3. # ret=Publish.objects.all().annotate(c=Count("book__title")).values("name","c","city") # 按照所有字段group by
  4. ret = Publish.objects.annotate(c=Count("book__title")).values("name", "c", "city")
  5. print(ret)
  6. ##################### 练习 ####################
  7. # 统计每一本以py开头的书籍的作者个数:
  8. # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
  9. ret = Book.objects.filter(title__startswith="py").values("pk").annotate(c=Count("authors__name")).values("title",
  10. "c")
  11. # 统计不止一个作者的图书
  12. ret = Book.objects.values("pk").annotate(c=Count("authors__name")).filter(c__gt=1).values("title", "c")
  13. print(ret)