SQL Debug日志开启

LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
select_related(*fields)
返回一个QuerySet,当执行它的查询时它沿着外键关系查询关联的对象的数据。它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询。
***对于一对一字段(OneToOneField)和外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化,在对QuerySet使用select_related()函数后,Django会获取相应外键对应的对象,从而在之后需要的时候不必再查询数据库了。
示例1:
标准的查询:
# Hits the database. e = Entry.objects.get(id=5) # Hits the database again to get the related Blog object. b = e.blog
select_related查询:
# Hits the database. e = Entry.objects.select_related('blog').get(id=5) # Doesn't hit the database, because e.blog has been prepopulated # in the previous query. b = e.blog
示例二:
Host.objects.select_related("business").filter(user__username=user_name).count()
小结
1.select_related主要针一对一和多对一关系进行优化。
2.select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。
3.可以通过可变长参数指定需要select_related的字段名。也可以通过使用双下划线“__”连接字段名来实现指定的递归查询。没有指定的字段不会缓存,没有指定的深度不会缓存,如果要访问的话Django会再次进行SQL查询。
4.也可以通过depth参数指定递归的深度,Django会自动缓存指定深度内所有的字段。如果要访问指定深度外的字段,Django会再次进行SQL查询。
5.也接受无参数的调用,Django会尽可能深的递归查询所有的字段。但注意有Django递归的限制和性能的浪费。
prefetch_related(*lookups)
这允许它预取多对多和多对一对象,因为它们不能使用select_related来完成。
*** select_related主要是执行跨表操作,而prefetch_related不是跨表操作,而是对每个表分别执行SQL查询(Django内部在您使用时自动会帮你像跨表操作一样连接起来),从而减少跨表查询,提升性能。
小结
- 因为select_related()总是在单次SQL查询中解决问题,而prefetch_related()会对每个相关表进行SQL查询,因此select_related()的效率通常比后者高。
- 鉴于第一条,尽可能的用select_related()解决问题。只有在select_related()不能解决问题的时候再去想prefetch_related()。
- 你可以在一个QuerySet中同时使用select_related()和prefetch_related(),从而减少SQL查询的次数。
- 只有prefetch_related()之前的select_related()是有效的,之后的将会被无视掉。
理解缓存属性
和整个QuerySet的缓存相同,ORM对象的属性的结果中也存在缓存。通常来说,不可调用的属性会被缓存。例如下面的博客模型示例:
>>> entry = Entry.objects.get(id=1) >>> entry.blog # Blog object is retrieved at this point
>>> entry.blog # cached version, no DB access
但是通常来讲,可调用的属性每一次都会访问数据库。
>>> entry = Entry.objects.get(id=1) >>> entry.authors.all() # query performed
>>> entry.authors.all() # query performed again
使用iterator()
当你有很多对象时,QuerySet的缓存行为会占用大量的内存。这种情况下,采用iterator()解决。
用唯一的被索引的列来检索独立对象
有两个原因在get()中,用带有unique或者db_index的列检索独立对象。首先,由于查询经过了数据库的索引,所以会更快。其次,如果很多对象匹配查询,查询会更慢一些;列上的唯一性约束确保这种情况永远不会发生。
使用QuerySet.values()和values_list()
当你仅仅想要一个带有值的字典或者列表,并不需要使用ORM模型对象时,可以适当使用values()。对于在模板代码中替换模型对象,这样会非常有用 —— 只要字典中带有的属性和模板中使用的一致,就没问题。
使用QuerySet.defer()和only()
如果一些数据库的列你并不需要(或者大多数情况下并不需要),使用defer()和only()来避免加载它们。注意如果你确实要用到它们,ORM会在另外的查询之中获取它们。如果你不能够合理地使用这些函数,不如不用。
使用QuerySet.count()
...如果你想要获取大小,不要使用 len(queryset)。
使用QuerySet.exists()
...如果你想要知道是否存在至少一个结果,不要使用if queryset。
user_obj = UserInfo.objects.filter(username=login_obj.cleaned_data["login_username"], password=login_obj.cleaned_data["login_password"]) if user_obj.exists(): return HttpResponse(json.dumps(response))
直接使用外键的值
如果你仅仅需要外键当中的一个值,要使用对象上你已经取得的外键的值,而不是获取整个关联对象再得到它的主键。例如,执行:
entry.blog_id
而不是:
entry.blog.id
整体插入
创建对象时,尽可能使用bulk_create()来减少SQL查询的数量。例如:
Entry.objects.bulk_create([ Entry(headline="Python 3.0 Released"), Entry(headline="Python 3.1 Planned") ])
...更优于:
Entry.objects.create(headline="Python 3.0 Released") Entry.objects.create(headline="Python 3.1 Planned")
这也可以用在ManyToManyFields中,所以:
my_band.members.add(me, my_friend)
...更优于:
my_band.members.add(me)
my_band.members.add(my_friend)
使用QuerySet.update()和delete()
通过QuerySet.update()使用批量的SQL UPDATE语句,而不是获取大量对象,设置一些值再单独保存。与此相似,在可能的地方使用批量deletes。
1111