文章目录
|- apps
|-----Blog
|---------models.py: author, blog, category
filter/get
get()
不到会抛异常,但get到多条也会报错!!!
author = Author.objects.filter(name="GG", status=0).first()
if not author:
continue
# get()不到会抛异常,但get到多条也会报错;可用filter().first()代替
try:
author = Author.objects.get(name="GG", status=0)
except Exception:
return
get/update_or_create
get_or_create()
在查询时使用defaults以外的参数进行查询;当实例不存在时,将包含default参数一起创建一个新实例。update_or_create()
数据存在则更新,当实例不存在时,将包含default参数一起创建一个新实例。
au, created = Author.objects.get_or_create(id=6, defaults={'name': "GG"}) # 返回(obj, created),obj是查询或创建的对象实例,created是个布尔值,表示是否是新创建的实例。
BlogAuthoItem.objects.get_or_create(author=au, blog=blog, defaults=info) # django中的字段名
au, created = Author.objects.update_or_create(id=6, defaults=defaults) # 数据存在则更新,不存在则保存
get_or_create() 如果查找到的对象超过一个以上,将引发MultipleObjectsReturned。
update
- update()会立即执行,不用再执行save()。但用=赋值操作要.save()。
- 不会产生
pre_save或post_save信号
(调用save()方法产生)或服从auto_now字段选项, 因此update_time要手动更新!!!
- 不会产生
- update更加适用于批量数据更新,而save则只适合做单条数据更新操作,效率比update低。
Blog.objects.all()[:10] # 切片操作,获取10个人,不支持负索引,切片可节约内存
v1 = Business.objects.all() # object <QuerySet [<Business: Business object>,...]
v2 = Business.objects.all().values('id','caption') # list <QuerySet [{'caption': '运维部', 'id': 1}, {'caption': '开发', 'id': 2}, {'caption': '市场', 'id': 3}, {'caption': '测试', 'id': 4}]>
v3 = Business.objects.all().values_list('id','caption') # tuple <QuerySet [(1, '运维部'), (2, '开发'), (3, '市场'), (4, '测试')]>
# 批量更新update:适用于 .all() .filter() .exclude()等后面. 危险??
Blog.objects.filter(name__contains="abc").update(name='xxx', publish_time=datetime.now())
Author.objects.filter(id=1).update(**defaults) # defaults是个dict
Blog.objects.all().delete()
# 单条更新save():适合于.get() .get_or_create() .update_or_create() 等得到的obj. 修改字段后不要忘记 .save()!!!!!!
au.save()
django查询之Q对象、F对象、聚合查询、分组查询
from django.db.models import Q, F, Avg
from datetime import timedelta, datetime
from django.db.models import Avg, Max, Min, Count
from django.db.models.functions import TruncDate, TruncDay, TruncHour, TruncMinute, TruncSecond
# F() 比较同一个model实例中两个不同字段的值。
Blog.objects.filter(like_num__lt=F('hate_num')
q = Q(update_time__gt=F("publish_time") - timedelta(days=1)) # 时间增量
q &= Q(create_time__gt=datetime.now() - timedelta(days=3))
# Q对象:支持 & | ~ 组合。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。
blogs = Blog.objects.filter(Q(authors__name="yuan")| Q(authors__name="egon"))
# 聚合查询: aggregate()是QuerySet的一个终止子句,它返回一个包含一些键值对的字典
Blog.objects.all().aggregate(Avg("price"),Max("price")) # 计算价格的均值、最大值,返回 {'price__avg': 192.593333, 'price__max': Decimal('233.33')}
# 分组查询:annotate为调用的QuerySet中每一个对象都生成一个独立的统计值authorNum,适合多对多关系查询
blogs = Blog.objects.all().annotate(authorNum=Count("authors")).order_by('authorNum')
Blog.objects.annotate( hour=TruncHour('start_datetime', output_field=TimeField())).values('hour').annotate(experiments=Count('id'))
# Q对象 和 filter比较:
Blog.objects.filter(Q(category__id=category.id))
Blog.objects.filter(category=category)
Blog.objects.filter(lcategory__id=category.id)
QuerySet
- 支持链式查询:
Blog.objects.filter(category=2).exclude(private=True)
- 查询结果排序:
Author.objects.all().order_by('name')
- 不支持负索引:
Blog.objects.all().reverse()[:2] # 最后两条
Blog.objects.order_by('-id')[:20] # id最大的20条
- 去重:
qs = qs.distinct() # distinct('author')
blogs = Blog.objects.values('number') # [{'number': u'1'}, {'number': u'2'}, {'number': u'3'}]
blogs = Blog.objects.values_list('number') # [(u'1',), (u'2',), (u'3',)]
blogs = Blog.objects.values_list('number', flat=True) .distinct() # [u'1', u'2', u'3']
queryset.extra(select={'length': 'Length(title)'}).order_by('length')
并不是执行Objects.all(),或者filter之后就会与数据库交互
- queryset总是惰性的
一个创建QuerySets的动作不会立刻导致任何的数据库行为。你可以不断进行filter动作一整天,Django不会运行任何实际的数据库查询动作,直到QuerySets被提交(evaluated)。# 返回QuerySet对象,程序并没有真的在数据库中执行SQL语句查询数据,但支持迭代,可用for循环可获取数据 blogs = Blog.objects.all() blogs = Blog.objects.filter(name__icontains="Django") # 返回Model对象,说明用get方法会直接执行sql语句获取数据 blog = Blogs.objects.get(id='1')
- queryset具有cache的,可减少数据库查询
- for语句遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model,这被称为执行(evaluation)。
- 这些model会保存在queryset内置的
cache
中,若再次遍历这个queryset,不需要重复运行通用的查询。即代码只会执行一次数据库查询。 if queryset
触发执行: 用if语句判断会完全执行整个queryset并把数据放入cache,虽然你并不需要这些数据!!
- 避免cache问题
- 为避免if,可用
queryset.exists()
来检查是否有数据,从而节省带宽和内存. - 避免遍历数据时产生cache,可用
.iterator()
来获取数据防止生成cache,处理完数据就将其丢弃。 但多次遍历时会重复执行查询!!! exists() + iterator()
: 用两次数据库查询来避免使用查询集缓存。
- 为避免if,可用
len()方法相当于会把整个queryset遍历一次,把所有的数据都取出来对象化,消耗大量资源
- 数据是否存在:.exists() > count() > len()
- 数据数量:count() > len()
- 批量操作QuerySet.update()/delete()`: 不会执行自定义行为。
- 无需获取不使用字段
- 通过 values() 和values_list()
可以提高查询速度
-defer()
排除字段和only()
指定字段
- 请注意,如果确实使用它们,ORM将不得不在一个单独的查询中获取它们,如果您不恰当地使用它们,那么这将是一个悲观化。
优化: 减少查询次数:
Book.objects.raw('select * from Book') # 用RawSQL
book = Book.objects.get(id=27) # 主键索引更快
book = Book.objects.get(name__startswith='将') # 字段模糊查询会更慢
select_related
用inner join
、主要针查询外键(ForeignKey)或一对一(OneToOneField)关系进行优化。通过减少SQL查询的次数来进行优化、提高性能。prefetch_related
执行一个单独的查找,它允许预先读取多对多和多对一的对象数据,这是 select_related 做不到的。另外 perfetch_related 也可以与通用外键和关系一起使用。
blog = Blog.objects.get(id=124) # 访问一次数据库
author = blog.author # 再次访问数据库
blog = Blog.objects.select_related('author').get(id=124) # 访问数据库
author = blog.author # 不会再次访问数据库,在之前的查询中已经取到了对应的数据
单纯只要外键值:
blog = Blog.objects.get(id=2)
author_id = blog.author.id # Bad query, additional db lookup in author table
blog = Blog.objects.select_related('author').get(id=2)
author_id = blog.author.id # A better version of above query but still a bad query
blog = Blog.objects.get(id=2)
author_id = blog.author_id # Good Query 最佳实践
MTM: add() / remove() / update() / delete() / clear()
方法
1、为对象添加多对多关系前,需要保持对象,即写入数据库产生id列,然后才能利用id列建立多对多关系表。
2、可以用create()函数,连建立to_*_id代表的目标对象,带add()新建对象到多对多关系表一起,一步完成两个操作。
3、.distinct().count()
组合使用
au.blogs.all().delete() # 需指出类似all()的查询集。
au.blogs.clear()
.add(obj1[, obj2, ...]) # 不需要再调用save(), 多次添加同一个关系不会重复。但参数类型不能错,否则引发TypeError
.remove(obj1[, obj2, ...]) # 解绑指定关系。 只删除关系,保留源侧和目标侧对象。
.set(queryset) # 批量设多个关系
.clear() # 不会删除对象,只会清除他们之间的关系
.delete() # 删除单个或批量删除对象。 级联删除,相关联的一对多,多对多关系都会删除。 【任何有外键指向要删除对象的对象将一起被删除】
# 该方法将返回被删除对象的总数量和一个字典,字典包含每种被删除对象的类型和该类型的数量。
Article.objects.filter(publications__title__startswith="Science")
Article.objects.filter(publications__id=1)
Article.objects.filter(publications__pk=1)
Article.objects.filter(publications=1) # (publications__in=[1,2])
Article.objects.filter(publications=p1) # (publications__in=[p1,p2])
性能分析
方法一: code
from django.db import connection
dbsql = connection.queries # 具体sql执行情况和耗时情况
方法二: pip install django-extensions
- INSTALLED_APPS = (
...
'django_extensions',
...
)
- python manage.py shell_plus --print-sql # 查看执行sql和结果