-
一般在Django程序中查询数据库操作都是在QuerySet里进行的:
q1 = Entry.objects.filter(headline__startswith="What") q2 = q1.exclude(pub_date__gte=datetime.date.today()) q3 = q1.filter(pub_date__gte=datetime.date.today())
-
获取ORM对应的sql:
代码:
>>> queryset = Event.objects.all() >>> str(queryset.query) SELECT "events_event"."id", "events_event"."epic_id","events_event"."details", "events_event"."years_ago" FROM "events_event"
-
multiple criteria【评价或做决定的标准,准则,原则】符合多条件【文盲笔记】
-
执行AND操作:
-
filter(<condition_1>, <condition_2>)
-
queryset_1 & queryset_2
-
filter(Q(<condition_1>) & Q(<condition_2>))
多个Q()对象之间的关系Django会自动理解成“且(and)”关系
News.objects.get( Q(question__startswith='Who'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) ) # 对应的SQL语句可以理解为: SELECT * from news WHERE question LIKE 'Who%' AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
-
-
Django filter中使用or(Django中Q)
filter(kwargs) //返回一个匹配查询参数的新的结果集.
filter(~Q(kwargs)) //返回一个不匹配查询参数的新的结果集.
exclude(kwargs) //返回一个不匹配查询参数的新的结果集.
将filter与or联系起来:
from django.db.models import Q
q=Q(question_startswith="What")
```
-
使用符号 & 或者 | 将多个Q()对象组合起来传递给filter(),exclude(),get()等函数。
当多个Q()对象组合起来时,Django会自动生成一个新的Q():user.object.filter(Q(question__startswith='Who') | Q(question__startswith='What'))
【注意】查询表中question以‘who’或者‘what’开头的用户
```
Q(question__startswith='Who') | Q(question__startswith='What')
#这条对应的sql语句如下
WHERE question LIKE 'Who%' OR question LIKE 'What%'
```
-
可以在Q()对象前面使用字符“~”来表达“非”
# 查询表中question以‘who’开头的用户或者pub_date不是2005年的数据 Q(question__startswith='Who') | ~Q(pub_date__year=2005) # 对应SQL语句可以理解为: WHERE question like "Who%" OR year(pub_date) !=2005
-
Q()对象可以结合关键字参数一起传递给查询函数,注意的是要将Q()对象放在关键字参数的前面
#正确的做法 News.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), question__startswith='Who') #错误的做法,代码将关键字参数放在了Q()对象的前面。 News.objects.get( question__startswith='Who', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
-
Q()传入条件查询
con = Q() q1 = Q() q1.connector = 'OR' q1.children.append(('name', "cox")) q1.children.append(('name', "Tom")) q1.children.append(('name', "Jeck")) q2 = Q() q2.connector = 'OR' q2.children.append(('age', 12)) con.add(q1, 'AND') con.add(q2, 'AND') models.Author.objects.filter(con) # 在Author表中,name等于cox/Tom/Jeck的 并且 满足age等于12 的所有数据
-
union组合来自相同或不同模型的两个查询集:
union运算符用于组合两个或多个查询集的结果集。
只有具有相同字段和数据类型的查询集才能执行union操作
查询集可以来自相同或不同的模型。
当它们的查询集来自不同的模型时,可截取部分字段,此时字段及其数据类型应该匹配。 -
使用F()表达式
每次获取times当前的值,再+1,这样需要将times值取出,存到内存中
obj = models.Test.objects.get(name="cox") obj.times = obj.times + 1 obj.save() obj.save() obj.save() obj.save() # 如果times的值是1,那么经过n次save()之后,times的值是2
-
虽然
obj.times = F(“times”) + 1
看起来像常规的Python为实例属性赋值,但实际上它是一个描述数据库上操作的SQL结构。 -
当Django遇到要给F()实例,它会覆盖标准的Python运算符来创建一个封装的SQL表达式;在这个例子中,指示数据库增加由 obj.times 表示的数据库字段。
-
无论 obj.times 的值是或曾是什么,Python永远不需要知道----完全由数据库来处理。Python通过Django的F()类做的所有事情仅是参考某个字段创建SQL语法来描述操作。
obj = models.Test.objects.get(name="cox") obj.times = F("times") + 1 obj.save() obj.save() obj.save() obj.save() # 如果times的值是1,那么经过n次save()之后,times的值是1+n,而不是2,就是因为F()操作在 obj.save() 后会持续存在
【性能优势:
- 直接在数据库中操作而不是Python
- 减少一些操作所需的数据库查询次数】 -
-
F()与filter的结合使用
-
获取表中收入(input_price)大于支出(output_price)的数据:
models.Test.objects.filter(input_price__gt=F("output_price"))
-
Django支持F()对象使用加、减、乘、除、取模和幂运算等算术操作,两个操作数可以是常数或F()对象
models.Test.objects.filter(input_price__gt=F("output_price")*2) models.Test.objects.filter(input_price__gt=F("output_price")+F("output_price"))
-
在F()对象中使用双下划线标记来跨越关联关系。 带有双下划线的F()对象将引入任何需要的join 操作以访问关联的对象
models.Test.objects.filter(authors__name=F('blog__name'))
-
对于date 和date/time 字段,你可以给它们加上或减去一个timedelta对象
from datetime import timedelta models.Test.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
-
-
仅选择部分字段:
- queryset上的values和values_list方法
>>> User.objects.filter(first_name__startswith='R').values('first_name', 'last_name') <QuerySet [{'first_name': 'Ricky', 'last_name': 'Dayal'}, {'first_name': 'Ritesh', 'last_name': 'Deshmukh'}, {'first_name': 'Radha', 'last_name': 'George'}, {'first_name': 'Raghu', 'last_name': 'Khan'}, {'first_name': 'Rishabh', 'last_name': 'Deol'}] >>> str(queryset.query) SELECT "auth_user"."first_name", "auth_user"."last_name" FROM "auth_user" WHERE "auth_user"."first_name"::text LIKE R%
- only方法【能够取到id】
>> queryset = User.objects.filter( first_name__startswith='R' ).only("first_name", "last_name") >>> str(queryset.query) SELECT "auth_user"."id", "auth_user"."first_name", "auth_user"."last_name" FROM "auth_user" WHERE "auth_user"."first_name"::text LIKE R%
-
Django的聚合函数和aggregate\annotate
-
聚合函数不能在Django中单独使用,要想在Django中使用这些聚合函数,就必须把这些聚合函数放到支持他们的方法内去执行。
-
支持聚合函数的方法有两种,分别是aggregate和annotate,这两种方法执行的原生SQL以及结果都有很大的区别
-
聚合 | 是aggreate(*args,**kwargs),通过QuerySet 进行计算 | 做求值运算 |
---|---|---|
分组 | 是annotate(*args,**kwargs),括号里是分组条件 | 遇到某条件的时候要分组 |
# 示例模型:
class Author(models.Model):
"""作者模型"""
name = models.CharField(max_length=100)
age = models.IntegerField()
email = models.EmailField()
class Book(models.Model):
"""图书模型"""
name = models.CharField(max_length=100)
author = models.ForeignKey('Author',on_delete=models.CASCADE)
price = models.FloatField()
class BookOrder(models.Model):
"""图书订单模型"""
book = models.ForeignKey('Book',on_delete=models.CASCADE)
sailprice = models.FloatField()
create_time = models.DateTimeField(auto_now_add=True)
-
aggregate:
-
是一个QuerySet对象的API,在执行聚合函数的时候,是对QuerySet整个对象的某个属性汇总,在汇总时不会使用该模型的主键进行group by进行分组,得到的是一个结果字典。
-
该方法支持聚合关联表(如使用ForeignKey)中的字段,在聚合连表中字段时,传递该字段的方式与查询连表时传递字段的方式相同,会使用到"__"。示例代码如下:
from django.db.models import Avg from django.db import connection
- 对当前表中数据进行聚合:
result = Author.objects.aggregate(avg_age=Avg('age')) print(connection.queries) # 打印执行时所有的查询语句
- 对连表中数据进行聚合:
result = Book.objects.aggregate(sum=Sum('bookorder__price'))
-
-
annotate:
-
可以执行聚合函数,也可以传递F、Q对象为当前QuerySet生成一个新的属性
-
这个方法一般聚合的是连表中的字段,会为当前QuerySet中的每个对象生成一个独立的摘要,为查询的模型增加一个新的属性,这个属性的值就是使用聚合函数所得到的值。
-
在使用这个聚合函数的时候annotate会使用这个模型的主键进行group by进行分组(注意这里只有在使用聚合函数生成新字段的时候会进行group by,在使用F、Q表达式增添新字段时,并不会使用group by),然后在连表中根据分组的结果进行聚合,这一点正符合为QuerySet中每个对象增加一个独立摘要的事实。
-
使用这个annotate方法执行聚合函数,得到的结果是一个QuerySet对象,结果依然能够调用filter()、order_by()甚至annotate()进行再次聚合,现在我想提取每一本书的平均销售的价格(注意销售价格在BookOrder表中):
from django.db.models import Avg from django.db import connection books = Book.objects.annotate(avg=Avg('bookorder__sailprice')) for book in books: print('%s/%s'%(book.name,book.avg)) # 注意这里的avg属性就是annotate执行聚合函数得到的 print(connection.queries)
-
-
聚合函数:
在Django中,聚合函数都是在django.db.models模块下的,具体的聚合函数有Avg、Count、Max、Min、Sum,现在我们一一介绍这些函数的作用:
- Avg:计算平均值,使用于与数值相关的字段,如果使用aggregate方法来执行这个函数,那么会得到一个字典,默认情况下,字典的键为field__avg,值为执行这个聚合函数所得到的值,示例代码如下:
# 计算所有作者的平均年龄 result = Author.objects.aggregate(Avg('age')) print(result) # 结果为:{"age__avg": 23.8} # 如果想要使用自定义的键,那么可以把aggregate中的未知参数变为关键字参数,该关键字就是得到的键,示例代码如下: result = Author.objects.aggregate(avgAge=Avg('age')) print(result) # 结果为:{"avgAge": 23.8} # 如果使用annotate方法执行这个函数,那么得到的结果就是一个QuerySet对象,只不过这个对象中的每一个都会添加一个属性,这个属性的名称其实和上面的键一样,可以使用默认也可以自定义,使用方法与在aggregate中键名的定义一样,这里就不再赘述: books = Book.objects.annotate(avg=Avg('bookorder__sailprice')) for book in books: print('%s/%s'%(book.name,book.avg)) # 注意这里的avg属性就是annotate执行聚合函数得到的 print(connection.queries)
2、Count:计算数量,基本用法与Avg相同,在使用这个聚合函数的时候可以传递一个distinct参数用来去重:
# 计算总共有多少个订单 result = BookOrder.objects.aggregate(total=Count('id',distanct=True)) print(result) # 结果为:{"total": 18} # 计算每本书的订单量 books = Book.objects.annotate(total=Count('bookorder__id')) for book in books: print('%s/%s'%(book.name,book.total))
3、Max和Min:计算某个字段的最大值和最小值,用法与Avg一样
4、Sum:计算总和,用法与Avg一样
总结一下:
- 使用aggregate时,是对QuerySet整个对象的某个属性汇总聚合,不会使用分组。
- 而使用annotate方法时,是为QuerySet中的每个对象生成一个独立的摘要,一定会使用分组,然后再聚合。
-
复杂查询:
字段 含义 exclude() 与filter相反 first() 返回第一个 last() 返回最后一个 exists() 是否存在 count() 数量 order_by() 按字段排序 __contains 包含 __icontains 包含,不分大小写 __lt,__gt,__lte,__gte
小于,大于,小于等于,大于等于 __range 范围 __in 是否在范围内 Q 逻辑组合 高级查询:
- in 通过
字段名_in=[1,2]
查询
res = models.Student.objects.filter(age__in=[12,14,42]).all()
-
not in 通过exclude(字段名__in = [1,2]) exclude就是除了的意思
res = models.Student.objects.exclude(age__in=[12,14,42]).all()
-
like 通配查询
# where name like "李%" # 代表查询名字以李开头的所有 # name__istartswith 表示不区分大小写,以什么什么为开头 res = models.Student.objects.filter(name__startswith="李").all() # where name like "%白" # 表示匹配以白结尾的所有单词 # name__iendswith 表示不区分大小写,以什么什么为结尾 res = models.Student.objects.filter(name__endswith="白").all() # where name like "%小%" 表示匹配中间有 小 字的所有单词 # name__icontains 表示不区分大小写,包含什么什么的单词 res = models.Student.objects.filter(name__contains="小").all()
-
between … and… 通过 列名__range = [开始位置,结束位置] 闭区间
res = models.Student.objects.filter(id__range=[2,5]).all()
-
limit 通过索引进行切片[10:20] 代表从10取到20 前闭后开
res = models.Student.objects.all()[5:7]
-
order by 通过order by 方法, 前面加"-"就是降序,默认升序
res = models.Student.objects.order_by("id").all() # 可以多次排序 先按age字段降序,如果age相同,就按id字段升序 res = models.Student.objects.order_by("-age","id").all()
-
group by
\# 分组需要用到django中的方法 from django.db.models import Count, Max, Min, Sum res = models.Student.objects.values("name").annotate(xxx=Count("id")) \# 表示的是搜索name字段和对表的id字段计算count,并起了别名为xxx 通过name字段进行分组 \# SELECT "app01_student"."name", COUNT("app01_student"."id") AS "xxx" FROM "app01_student" GROUP BY "app01_student"."name"
-
only 只取某列的值
res = models.Student.objects.only("name","age").all() # 取出来是QuerySet 列表里面包含对象,对象中包含name字段的值以及id列的值 # 和values取值的区别就是only取出来的是列表套对象,values取的是列表套字典 # 并且,不管你only中是否包含id字段,他都会把你的id字段一并取出来
-
defer 取出除了某列以外其他列的值
res = models.Student.objects.defer("id","name").all() # 取出来的也是列表套对象,并且,不管你里面有没有id字段,都会把你id列的值取出来
-
using
# 我们在配置数据库时,会把数据库添加到DATABASES 中 # DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } # } # 如果里面连有多个数据库,就可以通过using来确定使用哪一个数据库 res =models.Student.objects.all().using("default")
-
查询表中一共有多少条数据
res = models.Student.objects.count()
-
第一条数据
res = models.Student.objects.first()
-
最后一条数据
res = models.Student.objects.last()
-
gt 大于 lt 小于 gte 大于等于 lte 小于等于
res = models.Student.objects.filter(id__gt=8).all() res = models.Student.objects.filter(id__lte=8).all()
-
and 操作 , 在filter中用逗号隔开就是and
res = models.Student.objects.filter(id=2,age=23) # 返回的还是QuerySet 类型
-
or 操作
# or 需要从django中导入Q方法 from django.db.models import Q res = models.Student.objects.filter(Q(id=2) | Q(age=12)) res = models.Student.objects.filter(Q(Q(id=2) | Q(age=12)) & Q(name="李铁柱")) # | 表示 或 & 表示 与
-
在原有的基础上更新,比如我想让某一列的值整体 + 1
# 需要从django中导入F方法 from django.db.models import F models.Student.objects.update(F("age") + 1) # 这样age字段就会全部 + 1
-
原生sql语句
# django也给我们提供了写原生sql的方法 from django.db import connection # 导入连接 cursor = connection.cursor() # 生成cursor对象 cursor.execute() # 可以提交sql语句,也可以传参数 cursor.fetchall() # 拿取所有的结果 cursor.fetchone() # 拿取一条结果 models.Student.objects.raw() # 这个方法也可以提交sql语句,不建议使用
- in 通过
-
执行
python manage.py migrate
后数据库中默认生成的表的名称为 “应用名”+“_”+"模型类名"例如
booktest_bookinfo
、booktest_heroinfo
分别代表booktest
应用中的bookinfo
类和heroinfo
类,注意此时数据库表名中的所有字母为小写 -
修改数据:
更新:
c = Demo.objects.get(id=1) //单条更新 c.op = 123 //更新 c.save() //操纵完后,保存 等价于:Demo.object.filter(id=1).update(name=‘Guangzhou ’,address=‘guangdong’) //批量更新
创建:
# 一次增加一条数据 models.Student.objects.create(name="xxx",age=12) # 一次增加多条数据 obj = [ models.Student(name="qqq",age=12), models.Student(name="aaa",age=32), models.Student(name="www",age=21) ] models.Student.objects.bulk_create(obj)
删除:
c = Demo.objects.get(id=1) //单条删除
c.delete()
Demo.object.filter(id=1).delete() //批量删除
models.Student.objects.filter(name=“xxx”).delete()
# 如果是单表删除没有问题,但如果这张表的主键和别的表建立了外键关系,删除这条数据,另一张表对应的数据也会被删除掉
# 这也称为级联删除,如果我们不需要这样,可以在建外键是给on_delete赋值
# on_delete 几个参数的含义
# CASCADE 默认值 级联删除
# SET_NULL 取消级联删除,如果被关联外键的一条数据删除,关联的对应值用NULL代替,所以需要支持NULL
# SET_DEFAULT 取消级联删除,被删除就用默认值代替
class Student(models.Model):
name = models.CharField(max_length=32)
teac = models.ForeignKey('Teacher', null=True, on_delete=models.SET_NULL)
小技巧:
python的函数接收参数test(a=1,b=2)可以写成test(**{‘a’:1,’b’:2})以字典的形式接收
Demo.objects.create(**{‘id’:12,’op’:321})
Demo.objects.get(id=1).update(**{‘id’:12,’op’:321})
```
-
外键查询:一对多
from django.db import models class Owner(models.Model): name = models.CharField(u'名称',max_length=10) class Computer(models.Model): cname = models.CharField(u'电脑名',max_length=10) owner = models.ForeignKey(Owner) # 一找多 owner_obj = Owner.objects.get(id=1) owner_obj.computer_set.all() # 多找一 com_obj = Computer.objects.get(id=1) com_obj.owner.name # 复杂查询
Computer.objects.filter(owner__name=‘test’)
复杂查询
Owner.objects.filter(computer__cname='demo')
# 增
owner_obj = Owner.objects.get(id=1)
owner_obj.computer_set.create(name=‘test’)
Computer.objects.create(name=‘test’,owner_id=‘1’)
Computer.objects.create(name=‘test’,owner=owner_obj)
# 删除
owner_obj = Owner.objects.get(id=1)
owner_obj.computer_set.all().delete()
Computer.objects.filter(owner_id='1').delete()
Computer.objects.filter(owner=owner_obj).delete()
```
-
外键查询:多对多
# 创建两张表 from django.db import models class User(models.Model): username = models.CharField(u'名称',max_length=20) age = models.IntegeraField() sex = models.BooleanField() phone = models.CharField(u'电话号码',max_length=30) class Book(models.Model): name = models.CharField(u'电脑名',max_length=40) price = models.IntegeraField() owner = models.ManyToMany(User) # 定义了多对多的关系
生成三张表:
home_application_book home_application_book_user home_application_user
其中User表如下:(数据手动填)
其中Book表:(数据手动填)
其中Book_User表:(手动填)
多对多查询:
# 查询 owner_obj = Owner.objects.get(id=1) print owner_obj.computer_set.all().values() com_obj = Computer.objects.get(id=1) print com_obj.owner.all().values() # 添加关系 owner_obj.computer_set.add(com_obj) com_obj.owner.add(owner_obj) # 删除关系 owner_obj.computer_set.remove(com_obj) owner_obj.computer_set.clear() com_obj.owner.remove(owner_obj) com_obj.owner.clear()
多对多查询实例: