表关系:
from django.db import models # Create your models here. class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() # 与AuthorDetail建立一对一的关系 authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE) class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday = models.DateField() telephone = models.BigIntegerField() addr = models.CharField(max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) publishDate = models.DateTimeField() price = models.DecimalField(max_digits=5, decimal_places=2) # 与Publish建立一对多的关系,外键字段建立在多的一方 publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE) # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表 authors = models.ManyToManyField(to='Author', )
练习:
一、基于对象查询(子查询)
1、正向查询:出版社与书籍属于一对多关系,关系字段在书籍表中,通过书籍表找出版社属于正向查询
# 查询书籍名称是python的出版社名称,对象点字段的形式查询即可(obj.publish.name) ret = models.Book.objects.filter(title='python').first().publish.name print(ret)
2、反向查询: 出版社与书籍属于一对多关系,关系字段在书籍表中,通过出版社表找书籍表属于反向查询
# 查询出版社id为1的出版社,出版过哪些书籍。表名加__set ret = models.Publish.objects.filter(nid=1).first().book_set.values('title') print(ret)
二、基于QuerySet和"__"查询(连表查询)
1、正向查询:出版社与书籍属于一对多关系,关系字段在书籍表中,通过书籍表找出版社属于正向查询
# 查询python这本书籍的出版社的邮箱:values('关联字段__字段') ret = models.Book.objects.filter(title='python').values('publish__email') print(ret)
ret = ret = models.Publish.objects.filter(book__title='python').values('email')
print(ret)
2、反向查询:同理,通过出版社表找书籍表属于反向查询
# 查询永丰出版社出版过的书籍名称:values('表名__字段') # 方法一: ret = models.Publish.objects.filter(name='永丰出版社').values('book__title') print(ret) # 方法二 ret = models.Book.objects.filter(publish__name='永丰出版社').values('title') print(ret)
三、聚合查询
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典,而不是QuerySet和一个具体的对象
from django.db.models import Count, Avg, Sum, Max, Min # 查询所有书籍的价格总和 ret = models.Book.objects.aggregate(Sum('price')) print(ret)
四、分组查询:annotate前面是什么,就按照什么分组
# 查询永丰出版社的所有书籍的平均价格 ret = models.Publish.objects.values('name').annotate(avg=Avg('book__price')).values('name', 'avg').filter(name='永丰出版社') print(ret)
ORM语句与SQL语句对照:
gt大于、lt小于、order_by排序
# 查询书籍平均价格低于100的出版社:filter('字段名__lt') ret = models.Publish.objects.values('name').annotate(avg=Avg('book__price')).values('name', 'avg').filter(avg__lt=100) print(ret)
# 按照书籍平均价格对出版社进行排序 ret = models.Publish.objects.values('name').annotate(avg=Avg('book__price')).values('name', 'avg').order_by('avg') print(ret)
在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
示例1:
查询评论数大于收藏数的书籍
from django.db.models import F models.Book.objects.filter(commnet_num__gt=F('keep_num'))
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)
修改操作也可以使用F函数,比如将每一本书的价格提高30元
models.Book.objects.all().update(price=F("price")+30)
引申:
如果要修改char字段咋办?
如:把所有书名后面加上(第一版)
>>> from django.db.models.functions import Concat >>> from django.db.models import Value >>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))
Q查询:
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。
示例1:
查询作者名是小仙女或小魔女的
models.Book.objects.filter(Q(authors__name="小仙女")|Q(authors__name="小魔女"))
你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
示例:查询作者名字是小仙女并且不是2018年出版的书的书名。
>>> models.Book.objects.filter(Q(author__name="小仙女") & ~Q(publish_date__year=2018)).values_list("title") <QuerySet [('番茄物语',)]>
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。
例如:查询出版年份是2017或2018,书名中带物语的所有书。
>>> models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="物语") <QuerySet [<Book: 番茄物语>, <Book: 香蕉物语>, <Book: 橘子物语>]>
q = Q() q.connector = 'or' # 修改查询条件的关系 默认是and q.children.append(('title__contains','三国演义')) # 往列表中添加筛选条件 ,接收一个元祖(字段,值) q.children.append(('price__gt',444)) # 往列表中添加筛选条件 res = models.Book.objects.filter(q) # filter支持你直接传q对象 但是默认还是and关系 print(res)
双下条件:
__exact 精确等于 like 'aaa' __iexact 精确等于 忽略大小写 ilike 'aaa' __contains 包含 like '%aaa%' __icontains 包含 忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains。 __gt 大于 __gte 大于等于 __lt 小于 __lte 小于等于 __in 存在于一个list范围内 __startswith 以...开头 __istartswith 以...开头 忽略大小写 __endswith 以...结尾 __iendswith 以...结尾,忽略大小写 __range 在...范围内 __year 日期字段的年份 __month 日期字段的月份 __day 日期字段的日 __isnull=True/False