22 Jun 18
一、F查询
0. 字段和常量间作比较;如果不使用F函数,想比较两个字段相对麻烦。
models.Product.objects.filter(maichu_gt=10)
1. 两个字段之间作比较(F查询应用一)
models.Product.objects.filter(maichu__gt=F("kucun"))
2. Django支持F()对象之间以及F()对象和常数之间的加减乘除和取模的操作。
a. 使用F函数进行查找操作
models.Product.objects.filter(maichu_lt=F('kucun')*2)
b. 使用F函数进行修改操作
models.Book.objects.all().update(price=F('price')+30)
3. 字符串拼接 (F查询应用二)
from django.db.models.functions import Concat
from django.db.models import Value
# 在Product表中name字段的每个值后拼接'新款'
models.Product.objects.update(name=Concat(F("name"), Values("("), Value("新款"), Value(")")))
# update:更新数据, 只能接在QuerySet对象后;F(要拼接的字段名);Value(要拼接的内容)
二、update修改字段和对象.属性修改字段的区别
1. 对象.属性方法会更新所有字段; update方法只会更新修改的那个字段
# 用update放大效率更高,推荐
2. 对象.属性方法需要后跟obj.save();update方法不需要save
3. 对象.属性方法只能应用于对象;update方法只能应用于QuerySet对象
# 使用对象.属性修改字段
publisher_obj = models.Publisher.objects.first() # 得到一个对象,为后续操作提供条件
publisher_obj.name = "沙河出版社"
publisher_obj.save() # 对象.属性修改字段后需要.save()保存修改
# 使用update方法修改字段 (应用于QuerySet对象)
models.Publisher.objects.filter(id=1).update(name="北京沙河出版社")
三、Q查询
filter() 等方法中的关键字参数查询都是取并集操作的。 如果需要执行更复杂的查询(例如OR语句),可以使用Q对象。
#1 多个查询条件做 交集 并集 取反 操作时
#2 如果Q查询和关键字查询同时存在时,Q查询要放在关键字查询的前面!
0. 查询卖出数大于100 并且 价格小于100块的:多个条件做交集可使用关键字参数查询
models.Product.objects.filter(maichu__gt=100, price__lt=100)
1. 查询 卖出数大于100 或者 价格小于100块的
from django.db.models import Q
print(models.Product.objects.filter(Q(maichu__gt=100)|Q(price__lt=100)))
# |: 或
2. 查询 库存数是100 并且 卖出数不是0 的产品
print (models.Product.objects.filter(Q(kucun=100) & ~Q(maichu=0)))
# &: 并且;~: 非
3. 查询产品名包含新款, 并且库存数大于60的 (查询函数可以混合使用Q 对象和关键字参数)
print (models.Product.objects.filter(Q(kucun__gt=60), name__contains="新款"))
print (models.Product.objects.filter(kucun__gt=60, name__contains="新款"))
print (models.Product.objects.filter(name__contains="新款",Q(kucun__gt=60)) # 报错positional argument follows keyword argument
四、事务
1. 数据库事务必须具备ACID特性,ACID是Atomic原子性,Consistency一致性,Isolation隔离性,Durability持久性。
2. Django ORM事务
from django.db.models import F
from django.db import transaction
# 开启事务处理
try:
with transaction.atomic():
# 创建一条订单数据
models.Order.objects.create(num="110110111", product_id=1, count=1)
# 报错,get()得到的是一个对象,无法使用update方法
# models.Product.objects.get(id=1).update(kucun=F("kucun")-1, maichu=F("maichu")+1)
# 去产品表 将卖出数+1, 库存数-1 (可正常执行语句)
models.Product.objects.filter(id=1).update(kucun=F("kucun")-1, maichu=F("maichu")+1)
except Exception as e:
print(e)
# 不开启事务 (第一条语句执行成功,数据库已完成修改,但第二条语句执行失败并报错)
try:
models.Order.objects.create(num="110110110", product_id=1, count=1)
models.Product.objects.get(id=1).update(kucun=F("kucun") - 1, maichu=F("maichu") + 1)
except Exception as e:
print(e)
五、其他:select_related和prefetch_related
1. select_related
性能相关:表之间进行join连表操作,一次性获取关联的数据,以减少查询数据库的次数; 适用于一对一和多对一的查询的情况
# 操作一个对象
print(models.B.objects.first().a.name)
# 操作多个对象,不使用select_related
b_list = models.B.objects.all()
for b in b_list:
print(b.a.name)
# 操作多个对象,使用select_related
b_list = models.B.objects.select_related("a").all()
for b in b_list:
print(b.a.name)
2. prefetch_related 利用Python 来做类似JOIN操作
性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
# 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
# prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。
六、bulk_create
import random
data = ["".join([str(random.randint(65, 99)) for i in range(4)]) for j in range(100)]
# 不使用批量操作
for i in data:
models.A.objects.create(name=i)
# 使用批量操作bulk_create
obj_list = [models.A(name=i) for i in data]
models.A.objects.bulk_create(obj_list)
random及列表推导式复习:
# 产生100个8位数字随机数
import random
# 不使用列表生成式
ret = []
for j in range(100):
list1 = []
for i in range(4):
list1.append(str(random.randint(65, 99))) # ['82', '74', '73', '93']
# 必须转换成字符串先,后续才能有,连接
ret.append("".join(list1))
print(ret) # ["66676869", "69656863", ...]
# 列表生成式
ret2 = [ "".join([str(random.randint(65, 99)) for i in range(4)]) for j in range(100)]
print(ret2) # ["66676869", "69656863", ...]
七、ORM执行原生SQL的方法
1. extra
# 查询书籍名称和出版时间(年月)
print(models.Book.objects.all().extra(select={"z": "DATE_FORMAT(publish_date, '%%Y-%%m')"}).values("title", "z"))
# 将 书籍 按 年月 归档(BBS项目后续会使用)
from django.db.models import Count
print(models.Book.objects.extra(select={"z": "DATE_FORMAT(publish_date, '%%Y-%%m')"}).values("z").annotate(num=Count("id")).values("z", "num"))
2. 类似pymysql方式(更灵活的执行原生SQL语句)
from django.db import connection
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from app01_book where id = %s""", [1])
row = cursor.fetchone()
print(row)