21 Jun 18
一、ORM查询练习题
https://www.cnblogs.com/liwenzhou/articles/8337352.html
- Django终端打印SQL语句
在Django项目的settings.py文件中,在最后复制粘贴如下代码:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
- 练习题
脚本.py
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day68_yq.settings")
import django
django.setup()
from app01 import models
# 要先导入环境,才能导入app01中的models,否则报错
# 查找所有书名里包含番茄的书
print(models.Book.objects.filter(title__contains='番茄'))
# 查找出版日期是2017年的书
print(models.Book.objects.filter(publish_date__year=2017))
# 查找出版日期是2017年的书名
print(models.Book.objects.filter(publish_date__year=2017).values('title'))
# 查找价格大于10元的书
print(models.Book.objects.filter(price__gt=10))
# 查找价格大于10元的书名和价格
print(models.Book.objects.filter(price__gt=10).values('title','price'))
# 查找memo字段是空的书
print(models.Book.objects.filter(memo__isnull=True))
# 查找在北京的出版社
print(models.Publisher.objects.filter(city='北京'))
# 查找名字以沙河开头的出版社
print(models.Publisher.objects.filter(name__startswith='沙河'))
# 查找作者名字里面带“小”字的作者
print(models.Author.objects.filter(name__contains='小'))
# 查找年龄大于30岁的作者
print(models.Author.objects.filter(age__gt=30))
# 查找手机号是155开头的作者
print(models.Author.objects.filter(phone__startswith='155'))
# 查找手机号是155开头的作者的姓名和年龄
print(models.Author.objects.filter(phone__startswith='155').values('name','age'))
# 查找书名是“番茄物语”的书的出版社
print(models.Book.objects.get(title='番茄物语').publisher)
# 查找书名是“番茄物语”的书的出版社所在的城市
print(models.Book.objects.get(title='番茄物语').publisher.city)
# 查找书名是“番茄物语”的书的出版社的名称
print(models.Book.objects.get(title='番茄物语').publisher.name)
# 查找书名是“番茄物语”的书的所有作者
print(models.Book.objects.filter(title='番茄物语').values('author'))
# 查找书名是“番茄物语”的书的作者的年龄
print(models.Book.objects.filter(title='番茄物语').values('author__age'))
# 查找书名是“番茄物语”的书的作者的手机号码
print(models.Book.objects.filter(title='番茄物语').values('author__phone'))
# 查找书名是“番茄物语”的书的作者的地址
print(models.Book.objects.filter(title='番茄物语').values('author__detail__addr'))
# 查找书名是“番茄物语”的书的作者的邮箱
print(models.Book.objects.filter(title='番茄物语').values('author__detail__email'))
二、ORM连表查询之ForeignKey
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day68_yq.settings")
import django
django.setup()
from app01 import models
# 外键的正向查找
# 基于对象查找(第一本书的出版社名称) (SQL:子查询)
# first()得到一个对象,通过.属性的方法查找
print(models.Book.objects.first().publisher.name)
# 基于双下划线的跨表查询(书所关联的第一个出版社名称和所在城市)(SQL:JOIN)
# 通过FK直接__跨表查询
print(models.Book.objects.values('publisher__name','publisher__city').first())
# 练习:查找书名是"番茄物语"的书的出版社的城市
# 基于对象的查找方式
print(models.Book.objects.get(title='番茄物语').publisher.city)
# 基于双下划线的跨表查询
print(models.Book.objects.filter(title='番茄物语').values('publisher__city'))
# 外键的反向查找
# 基于对象查找
# 没有设置related_name时,first()得到一个对象,用表名_set反向查找
print(models.Publisher.objects.first().book_set.all())
# 在FK中设置了related_name = 'books'时,用设置的books反向查找 # publisher = models.ForeignKey(to="Publisher", related_name='books')
print(models.Publisher.objects.first().books.all())
# 在FK中设置了related_query_name = 'bookss'时,用设置的表名_set反向查找 # publisher = models.ForeignKey(to="Publisher",related_query_name="bookss")
# related_query_name使用频率较小,可能于动态关联(当所关联的表不固定时)
# print(models.Publisher.objects.first().book_set.all())
# 基于双下划线的跨表查询
# 没有设置related_name时,用表名反向查找
# print(models.Publisher.objects.filter(id=1).values('book__title'))
# 在FK中设置了related_name = 'books'时,用设置的books反向查找 # publisher = models.ForeignKey(to="Publisher", related_name='books')
# print(models.Publisher.objects.filter(id=1).values('books__title'))
# 在FK中设置了related_query_name = 'bookss'时,用设置的bookss反向查找 # publisher = models.ForeignKey(to="Publisher",related_query_name="bookss")
# print(models.Publisher.objects.filter(id=1).values('bookss__title'))
总结:
- 正向查找之对象查找(跨表):obj.关联字段.字段
- 正向查找之字段查找(跨表):关联字段__字段
- 反向操作之对象查找:obj.表名_set;如果设置了related_name=abc, 用obj.abc
- 方向操作之字段查找:表名__字段; 如果设置了related_name=abc, 用abc__字段,如果设置了related_query_name=def, 用def__字段
- 表名_set只有在反向+对象查找+没有设置related_name时使用
三、ORM连表查询之ManyToManyField
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。存在于下面两种情况:外键关系的反向查询and多对多关联关系
简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day68_yq.settings")
import django
django.setup()
from app01 import models # 要先导入环境,才能导入app01中的models,否则报错
#1. create:a. 创建了一个新的作者,b. 将新创建的作者和第一本书做关联
ret = models.Book.objects.first().author.create(name='author11',age=18,phone='12345678901',detail_id=5)
# author的detail字段为OneToOne,不能和已用的重复
print(models.Book.objects.first().author.all().values('name'))
#2. set:用set设置值,必须放在一个列表中
print(models.Book.objects.first().author.all().values('id')) # <QuerySet [{'id': 8}]>
models.Book.objects.first().author.set([2,3])
# 用set设置值,必须放在一个列表中
print(models.Book.objects.first().author.all().values('id')) # <QuerySet [{'id': 2}, {'id': 3}]>
#3. add: 用add添加值,不需放在列表中,只要把所要添加的值用,隔开即可;或*[a,b]
print(models.Book.objects.first().author.all().values('id')) # <QuerySet [{'id': 2}, {'id': 3}]>
models.Book.objects.first().author.add(1,4) # 用add添加值,不需放在列表中,只要把所要添加的值用,隔开即可
models.Book.objects.first().author.add(*[1, 4])
print(models.Book.objects.first().author.all().values('id')) # <QuerySet [{'id': 1}, {'id': 2}, {'id': 3}, {'id': 4}]>
#4. remove: 同add, 不需放在列表中,只要把所要添加的值用,隔开即可
print(models.Book.objects.first().author.all().values('id')) # <QuerySet [{'id': 1}, {'id': 2}, {'id': 3}, {'id': 4}]>
models.Book.objects.first().author.remove(3,4) # 同add, 不需放在列表中,只要把所要添加的值用,隔开即可
print(models.Book.objects.first().author.all().values('id')) # <QuerySet [{'id': 1}, {'id': 2}]>
#5. clear: 清空
print(models.Book.objects.first().author.all().values('id')) # <QuerySet [{'id': 1}, {'id': 2}]>
models.Book.objects.first().author.clear()
print(models.Book.objects.first().author.all().values('id')) # <QuerySet []>
#6. all: 查询所有
print(models.Book.objects.first().author.all()) # <QuerySet [<Author: author1小>, <Author: author4>]>
# 对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。
# 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
# 对于手动创建第三张关联表的情况,可以使用all(),但不能使用add()、create()、remove()和clear(),set()
四、聚合查询和分组查询
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day68_yq.settings")
import django
django.setup()
from app01 import models
# aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。
from django.db.models import Avg, Sum, Max, Min, Count
# 查询所有书的总价格
print(models.Book.objects.all().aggregate(Avg("price"))) # {'price__avg': 36.25}
# 如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
print(models.Book.objects.aggregate(sum_price=Sum('price'))) # {'sum_price': Decimal('145.00')}
# 如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。print(models.Book.objects.aggregate(sum_price=Sum('price'),min_price=Min('price'),max_price=Max('price'))) # {'sum_price': Decimal('145.00'), 'min_price': Decimal('10.00'), 'max_price': Decimal('100.00')}
# 求每一本书的作者个数
print(models.Book.objects.annotate(c=Count('author')).values('title','c')) # <QuerySet [{'title': 'book1', 'c': 2}, {'title': 'book2', 'c': 0}, {'title': 'book3', 'c': 0}, {'title': '番茄物语', 'c': 2}]>
# 按照什么分组就models.什么;以什么分组就annotate(什么)
# 求部门的平均薪资
models.Employee.objects.values("dept").annotate(salary_avg=Avg("salary")).values("dept", "salary_avg")
# annotate()前的是分组的依据(以Employee表中的dept字段分组);annotate()相当于新建了一个column(salary_avg)
# 统计出每个出版社买的最便宜的书的价格
print(models.Publisher.objects.annotate(min=Min('book__price')).values('name','min')) # <QuerySet [{'name': 'publisher1', 'min': Decimal('10.00')}, {'name': '沙河出版社', 'min': Decimal('100.00')}, {'name': 'publisher3', 'min': Decimal('20.00')}, {'name': 'publisher4', 'min': Decimal('15.00')}]>
# 如果为该FK设置related_name='books'
print(models.Publisher.objects.annotate(min=Min('books__price')).values('name', 'min'))
# 统计不止一个作者的图书 (书作者的数量大于1)
print(models.Book.objects.annotate(c=Count('author')).filter(c__gt=1)) # <QuerySet [<Book: book1>, <Book: 番茄物语>]>
# 按照书作者的数量做排序
print(models.Book.objects.annotate(c=Count('author')).order_by('c')) # <QuerySet [<Book: book2>, <Book: book3>, <Book: book1>, <Book: 番茄物语>]>
# 查询各个作者出的书的总价格
print(models.Author.objects.annotate(sum=Sum('book__price')).values('name','sum')) # <QuerySet [{'name': 'author1小', 'sum': Decimal('25.00')}, {'name': 'author4', 'sum': Decimal('10.00')}, {'name': 'author2', 'sum': Decimal('15.00')}, {'name': 'author3', 'sum': None}]>
五、不使用ManyToManyField,通过手动创建第三张表,关联两个外键
model.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
# 自己创建第三张表,分别通过外键关联书和作者
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
class Meta:
unique_together = ("author", "book") # 联合唯一
脚本.py
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day68_yq.settings")
import django
django.setup()
from app03 import models
# 基于对象查找
print(models.Book.objects.first().author2book_set.all().values('author__name'))
# <QuerySet [{'author__name': 'a1'}, {'author__name': 'a2'}]>
# 基于双下划线的跨表查询
print(models.Book.objects.filter(id=1).values('author2book__author__name'))
#. <QuerySet [{'author2book__author__name': 'a1'}, {'author2book__author__name': 'a2'}]>
first_book=models.Book.objects.first()
# 直接通过操作第三张表建立关系
models.Author2Book.objects.create(book=first_book,author_id=3)
# 直接通过操作第三张表删除关系
models.Author2Book.objects.filter(book=first_book).delete()
六、自己创建第三张表,并通过ManyToManyField指定关联(中介者模型)
# ManyToManyField建在哪张表中:根据业务逻辑;如果经常由Author表查Book,推荐将ManyToManyField加在Author表中;如果经常由Book表查Author,推荐将ManyToManyField加到Book表中。
models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
# 自己创建第三张表,并通过ManyToManyField指定关联
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者姓名")
books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
# through_fields接受一个2元组('field1','field2'):
# 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。
class Author2Book(models.Model):
author = models.ForeignKey(to="Author")
book = models.ForeignKey(to="Book")
class Meta:
unique_together = ("author", "book")
脚本.py
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day68_yq.settings")
import django
django.setup()
from app04 import models
# 基于对象查找
print(models.Book.objects.first().author2book_set.all().values('author__name'))
#. <QuerySet [{'author__name': 'a1'}]>
#基于双下划线的跨表查询
print(models.Book.objects.filter(id=1).values('author__name'))
# <QuerySet [{'author__name': 'a1'}]>
first_book=models.Book.objects.first()
# 直接通过操作第三张表建立关系
models.Author2Book.objects.create(book=first_book,author_id=2)
# 直接通过操作第三张表删除关系
models.Author2Book.objects.filter(book=first_book,author_id=2).delete()
七、其他
models.Employee2.objects.all() 《==》 select * from employee2;
models.Employee2.objects.values("name") 《==》 select name from employee2;