一、简单ORM的SQL
# 补充知识点:
# ret=Emp.objects.all()
# print(ret) # select * from emp
# ret=Emp.objects.values("name")
# print(ret) # select name from emp
二、基于对象的跨表查询(子查询)
一对多查询
- 正向查询:按字段
反向查询:表名小写_set
# 一对多查询的正向查询 : 查询金瓶梅这本书的出版社的名字
book_obj=Book.objects.filter(title="金瓶梅").first()
print(book_obj.publish) # 与这本书关联出版社对象
print(book_obj.publish.name)
# 对应sql:
# select publish_id from Book where title="金瓶梅"
# select name from Publish where id=1
# 一对多查询的反向查询 : 查询人民出版社出版过的书籍名称
publish=Publish.objects.filter(name="人民出版社").first()
ret=publish.book_set.all()
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)
<a name="jWkmB"></a>
## 一对一查询
- 正向查询:按字段
- 反向查询:表名小写
```python
# 一对一查询的正向查询 : 查询ecithy的手机号
ecithy=Author.objects.filter(name="ecithy").first()
print(ecithy.authordetail.telephone)
# 一对一查询的反向查询 : 查询手机号为110的作者的名字和年龄
ad=AuthorDetail.objects.filter(telephone="110").first()
print(ad.author.name)
print(ad.author.age)
return HttpResponse("OK")
三、基于双下划线的跨表查询(join查询)
原理:联表select…join…。
- 正向查询按字段
- 反向查询按表名小写(用来告诉ORM引擎join哪张表)
一对多查询
# 一对多查询 : 查询金瓶梅这本书的出版社的名字
# 方式1:
ret=Book.objects.filter(title="金瓶梅").values("publish__name")
print(ret) # <QuerySet [{'publish__name': '南京出版社'}]>
# 方式2: 如果有related_name,下面的book用related_name值
ret=Publish.objects.filter(book__title="金瓶梅").values("name")
print(ret)
return HttpResponse("OK")
多对多查询
# 多对多查询 : 查询金瓶梅这本书的所有作者的名字
# 方式1:
# 需求: 通过Book表join与其关联的Author表,属于正向查询:按字段authors通知ORM引擎join book_authors与author
ret=Book.objects.filter(title="金瓶梅").values("authors__name")
print(ret) # <QuerySet [{'authors__name': 'ecithy'}, {'authors__name': 'egon'}]>
# 方式2:
# 需求: 通过Author表join与其关联的Book表,属于反向查询:按表名小写book通知ORM引擎join book_authors与book表
ret=Author.objects.filter(book__title="金瓶梅").values("name")
print(ret) # <QuerySet [{'name': 'ecithy'}, {'name': 'egon'}]>
一对一查询
# 一对一查询的查询 : 查询ecithy的手机号
# 方式1:
# 需求: 通过Author表join与其关联的AuthorDetail表,属于正向查询:按字段authordetail通知ORM引擎join Authordetail表
ret = Author.objects.filter(name="ecithy").values( "authordetail__telephone")
print(ret) # <QuerySet [{'authordetail__telephone': 110}]>
# 方式2:
# 需求: 通过AuthorDetail表join与其关联的Author表,属于反向查询:按表名小写author通知ORM引擎join Author表
ret = AuthorDetail.objects.filter(author__name="ecithy").values("telephone")
print(ret) # <QuerySet [{'telephone': 110}]>
return HttpResponse("OK")
连续跨表
只要有外键关系,就能跨表。
# 进阶练习:
# 练习: 手机号以110开头的作者出版过的所有书籍名称以及书籍出版社名称
# 方式1:
# 需求: 通过Book表join AuthorDetail表, Book与AuthorDetail无关联,所以必需连续跨表
ret = Book.objects.filter(authors__authordetail__telephone__startswith="110").values("title", "publish__name")
print(ret)
#
# 方式2:
ret = Author.objects.filter(authordetail__telephone__startswith="110").values("book__title", "book__publish__name")
print(ret)
四、聚合与分组查询
class Emp(models.Model):
name=models.CharField(max_length=32)
age=models.IntegerField()
salary=models.DecimalField(max_digits=8,decimal_places=2)
dep=models.CharField(max_length=32)
province=models.CharField(max_length=32)
聚合查询
聚合 aggregate:返回值是一个字典,不是queryset
from django.db.models import Avg, Max, Min, Count
ret = Book.objects.all().aggregate(avg_price=Avg("price"), max_price=Max("price"))
print(ret) # {'avg_price': 151.0, 'max_price': Decimal('301.00')}
分组查询
单表分组查询
分组查询 annotate ,返回值依然是queryset
from django.db.models import Avg, Max, Min, Count
# 示例1
# 查询每一个部门的名称以及员工的平均薪水
# select dep,Avg(salary) from emp group by dep
ret = Emp.objects.values("dep").annotate(avg_salary=Avg("salary"))
print(ret) # <QuerySet [{'avg_salary': 5000.0, 'dep': '保安部'}, {'avg_salary': 51000.0, 'dep': '教学部'}]>
# 单表分组查询的ORM语法: 单表模型.objects.values("group by的字段").annotate(聚合函数("统计字段"))
# 示例2
# 查询每一个省份的名称以及员工数
ret = Emp.objects.values("province").annotate(c=Count("id"))
print(ret) # <QuerySet [{'province': '北京市', 'c': 1}, {'province': '广东省', 'c': 1}, {'province': '浙江省', 'c': 1}]>
多表分组查询
from django.db.models import Avg, Max, Min, Count
# 示例1 查询每一个出版社的名称以及出版的书籍个数
ret = Publish.objects.values("nid").annotate(c=Count("book__title"))
print(ret) # <QuerySet [{'nid': 1, 'c': 3}, {'nid': 2, 'c': 1}]>
ret = Publish.objects.values("name").annotate(c=Count("book__title"))
print(ret) # <QuerySet [{'name': '人民出版社', 'c': 3}, {'name': '南京出版社', 'c': 1}]>
ret = Publish.objects.values("nid").annotate(c=Count("book__title")).values("name", "c")
print(ret) # 可自定义输出字段 : <QuerySet [{'name': '人民出版社', 'c': 3}, {'name': '南京出版社', 'c': 1}]>
# 示例2 查询每一个作者的名字以及出版过的书籍的最高价格
ret = Author.objects.values("pk").annotate(max_price=Max("book__price")).values("name", "max_price")
print(ret)
# 总结 跨表的分组查询的模型:
# 每一个后表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段))
# 示例3 查询每一个书籍的名称以及对应的作者个数
ret = Book.objects.values("pk").annotate(c=Count("authors__name")).values("title", "c")
print(ret)
多表分组查询进阶
# 示例1 查询每一个出版社的名称以及出版的书籍个数
# ret=Publish.objects.values("nid").annotate(c=Count("book__title")).values("name","email","c")
# ret=Publish.objects.all().annotate(c=Count("book__title")).values("name","c","city") # 按照所有字段group by
ret = Publish.objects.annotate(c=Count("book__title")).values("name", "c", "city")
print(ret)
##################### 练习 ####################
# 统计每一本以py开头的书籍的作者个数:
# 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
ret = Book.objects.filter(title__startswith="py").values("pk").annotate(c=Count("authors__name")).values("title",
"c")
# 统计不止一个作者的图书
ret = Book.objects.values("pk").annotate(c=Count("authors__name")).filter(c__gt=1).values("title", "c")
print(ret)