Django ORM必会的查询方法
基于双下划线的查询
# 价格 大于 小于 大于等于 小于等于 filter(price__gt='90') # 筛选出大于90 filter(price__lt='90') # 筛选出小于90 filter(price_gte='90') # 筛选出大于等于90的 filter(price_lte='90') # 筛选出小于等于90的 # 存在与几个条件中 filter(price__in=['11','22','33']) #在某个返回中 filter(price__range=[50,90]) # 模糊查询 filter(title__contains='p') # 获取包含p的title filter(title__icontains='P') # 同上,但对大小写不敏感 filter(title__startswith='三') # 以"三"开头的 filter(title__endswith='p') # 以"p"结尾的 #日期查询 filter(create_time__year='2017') # 查询日期是2017的年
注意:__是双下滑线
关于多表查询的操作
一、关系字段
ForeignKey
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。
字段参数
to
设置要关联的表
OneToOneField
一对一字段。
通常一对一字段用来扩展已有字段。(通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联)
字段参数
to
设置要关联的表。
(一对一可以用ForeignKey代替,就是ForeignKey(unique=True))
ManyToManyField
该字段并不会真正的在表中展示出来 它仅仅是一个虚拟字段
1.告诉orm自动创建第三种表
2.帮助orm跨表查询
一对多字段的增删改
#增 #publish_id传数字 models.Book.objects.create(title='三国演义',price=189.99,publish_id=1) #publish直接传出版社对象 publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.create(title='红楼梦',price=999.99,publish=publish_obj) # 改 # 传数字的 models.Book.objects.filter(pk=1).update(publish_id=3) # 传对象的 publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.filter(pk=1).update(publish=publish_obj) # 删 models.Publish.objects.filter(pk=2).delete() # 默认都是级联更新 级联删除
多对多字段的增删改
# 增 # 要给主键为1的书籍添加两个作者 book_obj = models.Book.objects.filter(pk=1).first() print(book_obj.authors) # 对象点击多对多虚拟字段 会直接跨到多对多的第三张表book_obj.authors.add(1) book_obj.authors.add(2,3) # 支持传多个值 author_obj1 = models.Author.objects.filter(pk=2).first() author_obj2 = models.Author.objects.filter(pk=3).first() book_obj.authors.add(author_obj1) book_obj.authors.add(author_obj1,author_obj2) """ add() 把指定的model对象添加到关联对象集中。 但是一对多中add里面的实参(位置参数)是对象 而多对多中add里面的实参(位置参数)可以是id也可以是对象 给书籍添加作者 括号内既可以传数字也可以传对象 并且支持一次性传多个 逗号隔开即可 """ # 改 # 将主键为1的书籍对象 作者修改为2,3 book_obj = models.Book.objects.filter(pk=1).first() book_obj.authors.set([2,]) book_obj.authors.set([2,3]) # author_obj1 = models.Author.objects.filter(pk=2).first() # author_obj2 = models.Author.objects.filter(pk=3).first() # book_obj.authors.set([author_obj1,]) # book_obj.authors.set([author_obj1, author_obj2]) """ set()括号内 需要传一个可迭代对象 可迭代对象中 可以是多个数字组合 也可以是多个对象组合 但是不要混着用!!! 更新model对象的关联对象。 但是一对多中set里面的实参(列表)是对象 而多对多中add里面的实参(列表)可以是id也可以是对象 """ # 删 book_obj = models.Book.objects.filter(pk=1).first() book_obj.authors.remove(3) book_obj.authors.remove(1,2) author_obj = models.Author.objects.filter(pk=1).first() author_obj1 = models.Author.objects.filter(pk=2).first() author_obj2 = models.Author.objects.filter(pk=3).first() book_obj.authors.remove(author_obj) book_obj.authors.remove(author_obj1,author_obj2) """ remove()括号内既可以传数字 也可以传对象 并且支持传对个 逗号隔开即可 """ # 将某本书跟作者的关系全部清空 book_obj = models.Book.objects.filter(pk=1).first() book_obj.authors.clear() # 清空当前书籍与作者的所有关系 #总结 """ add() set() remove() 上面三个都支持传数字 或者对象 并且可以传多个 但是set需要传可迭代对象 clear() clear括号内不需要传任何参数 """
正向查询和反向查询的概念
正向与方向的概念解释:
举个栗子,在author表中有一个与authordetail的关联字段
那么在一对一的情况下就是
正向:author---关联字段在author表里--->authordetail 按字段
反向:authordetail---关联字段在author表里--->author 按表名小写
再举个例子,如果book与publish的关联字段在book表里
那么一对多的情况下就是:
正向:book---关联字段在book表里--->publish 按字段
反向:publish---关联字段在book表里--->book 按表名小写_set.all() 因为一个出版社对应着多个图书
最后一个例子book与author的关联字段在book里
那么多对多的情况下是:
正向:book---关联字段在book表里--->author 按字段
反向:author---关联字段在book表里--->book 按表名小写
_set.all() 因为一个作者对应着多个图书
小总结:正向查询按外键字段,反向查询按表名小写
当你反向查询的结果是多个的时候,就需要加_set,否则直接表名小写即可
连续跨表
查询图书是三国演义的作者的手机号,先查书,再正向查到作者,在正向查手机号
总结:基于对象的查询都是子查询,这里可以用django配置文件自动打印sql语句的配置做演示
基于双下划线的查询
# 正向 # 1.查询jason作者的手机号 # res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr') # print(res)
# # res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone') # print(res1) # # # 查询jason这个作者的年龄和手机号 # 正向 # res = models.Author.objects.filter(name='jason').values('age','author_detail__phone') # print(res) # 反向 # res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age') # print(res1) # # # 查询手机号是130的作者年龄 # 正向 # res = models.AuthorDetail.objects.filter(phone=130).values('author__age') # print(res) # 反向 # res1 = models.Author.objects.filter(author_detail__phone=130).values('age') # print(res1) # # 查询书籍id是1 的作者的电话号码 # res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone') # res1 = models.Book.objects.filter(pk=1).values('外键字段1__外键字段2__外键字段3__普通字段') # print(res) """只要表里面有外键字段 你就可以无限制跨多张表"""
聚合查询和分组查询
聚合查询(利用聚合函数)
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。
用到的内置函数:
from django.db.models import Avg, Sum, Max, Min, Count
示例:
from django.db.models import Avg, Sum, Max, Min, Count models.Book.objects.all().aggregate(Avg("price")) >>>{'price__avg': 13.233333}
如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
models.Book.objects.aggregate(average_price=Avg('price')) >>>{'average_price': 13.233333}
如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price")) >>> {'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}
分组查询
# 统计每一本书的作者个数
# res = models.Book.objects.annotate(author_num = Count('authors')).values('author_num','title') # print(res) # 统计出每个出版社卖的最便宜的书的价格 # res = models.Publish.objects.annotate(mmp = Min('book__price')).values('name','mmp') # print(res) # 统计不止一个作者的图书 # res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1) # print(res)
补充:只要是queryset对象 就可以无限制的调用queryset对象的方法!!!最最常用的就是对一个已经filter过滤完的数据 再进行更细化的筛选。
F查询和Q查询
F查询
在上面所有的例子中,我们构造的过滤器都只是将字段值与某个我们自己设定的常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
示例1:
查询出卖出数大于库存数的商品
from django.db.models import F ret1=models.Product.objects.filter(maichu__gt=F('kucun')) print(ret1)
F可以帮我们取到表中某个字段对应的值来当作我的筛选条件,而不是我认为自定义常量的条件了,实现了动态比较的效果
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。基于此可以对表中的数值类型进行数学运算
将每个商品的价格提高50块
models.Product.objects.update(price=F('price')+50)
引申:
如果要修改char字段咋办(千万不能用上面对数值类型的操作!!!)?
如:把所有书名后面加上'新款',(这个时候需要对字符串进行拼接Concat操作,并且要加上拼接值Value)
from django.db.models.functions import Concat from django.db.models import Value ret3=models.Product.objects.update(name=Concat(F('name'),Value('新款')))
Concat表示进行字符串的拼接操作,参数位置决定了拼接是在头部拼接还是尾部拼接,Value里面是要新增的拼接值
Q查询
filter() 等方法中逗号隔开的条件是与的关系。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。
示例1:
查询 卖出数大于100 或者 价格小于100块的
from django.db.models import Q models.Product.objects.filter(Q(maichu__gt=100)|Q(price__lt=100))
对条件包裹一层Q时候,filter即可支持交叉并的比较符
示例2:
查询 库存数是100 并且 卖出数不是0 的产品
models.Product.objects.filter(Q(kucun=100)&~Q(maichu=0))
我们可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。
同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
示例3:
查询产品名包含新款, 并且库存数大于60的
models.Product.objects.filter(Q(kucun__gt=60), name__contains="新款")
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。