要注意的是,在使用QuerySet的时候,一旦在链式操作中改变了数据库请求,之前用prefetch_related缓存的数据将会被忽略掉。这会导致Django重新请求数据库来获得相应的数据,从而造成性能问题。这里提到的改变数据库请求指各种filter()、exclude()等等最终会改变SQL代码的操作。
prefetch_related('comments')隐含表示blog.comments.all(),因此all()并不会改变最终的数据库请求,因此是不会导致重新请求数据库的。
然而
for comment in blog.comments.filter(author="jack"):就会导致Django重新请求数据库
只需要取出部分字段博客文章的content字段数据量可能非常大,取出而不用可能会影响性能。之前的需求中可以进一步优化只取出博客和评论中的部分字段
blogs = Blog.objects.filter(name="Django教程").only('id').\ prefetch_related( Prefetch('comments', queryset=Comment.objects.only('id', 'content', 'blog_id')) )使用only指定查询的字段,使用Prefetch对象自定义prefetch_related查询的内容(默认queryset=Comment.objects.all())
注意comment.blog_id字段是必须要取出的,因为在python中将comments拼到对应的blog时需要comment.blog_id字段与blog.id字段匹配,如果在Prefetch对象中不取出comment.blog_id,拼接时会浪费很多数据库查询去找comment.blog_id字段
多数据库的情况在多数据库的情况下,prefetch_related使用的数据源与主查询指定的数据源一致。
比如:
blogs = Blog.objects.using('my_db_alias').filter(name="Django教程").only('id').\ prefetch_related( Prefetch('comments', queryset=Comment.objects.only('id', 'content', 'blog_id')) )查询Comment表时会使用与Blog一样的数据源
五、向数据库插入数据的时候尽量使用bulk_create # 以下代码会发起10次数据库插入: for i in range(10): Comment.objects.create(content=str(i), author="kim", blog_id=1) # 以下代码只会发起一次数据库插入: comments = [] for i in range(10): comments.append(Comment(content=str(i), author="kim", blog_id=1)) Comment.objects.bulk_create(comments, batch_size=5000)注意:
bulk_create不会返回id:When you bulk insert you don't get the primary keys back
小心数据库连接超时:如果一次性插入过多的数据会导致Mysql has gone away的报错。指定batch_size=5000可以避免这个问题,当插入数据>5000时,会分成多个sql执行数据批量插入
六、尽量不要重复取数据可以将数据库的数据以id为key存到内存的字典中,这样下次用到的时候就无需再次访问数据库,可提高效率