prefetch()类方法:
prefetch_related
, on the other hand, does a separate lookup for each relationship, and does the 'joining' in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using
For example, suppose you have these models
from django.db import models class Topping(models.Model): name = models.CharField(max_length=30) class Pizza(models.Model): name = models.CharField(max_length=50) toppings = models.ManyToManyField(Topping) def __str__(self): return "%s (%s)" % ( self.name, ", ".join(topping.name for topping in self.toppings.all()), ) and run:
class Restaurant(models.Model):
pizzas = models.ManyToManyField(Pizza, related_name='restaurants')
best_pizza = models.ForeignKey(Pizza, related_name='championed_by', on_delete=models.CASCADE)
>>> Pizza.objects.all() ["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...
The problem with this is that every time Pizza.__str__()
asks for self.toppings.all()
it has to query the database, so Pizza.objects.all()
will run a query on the Toppings table for every item in the Pizza QuerySet
.
也就是说,上面的代码中每一次调用__str__(),又要多一次查询,而不是一次all()查询。
prefetch()可以只用两次查询,不是通过外键关联一次查询的方式。
>>> Pizza.objects.all().prefetch_related('toppings')
原理是,prefetch()将其查到的结果存到了缓存区,并做好了关联。
也可以多次连表
Restaurant.objects.prefetch_related('pizzas__toppings')
另外,prefetch可以通过Prefetch对象,实现自定义的查询。
>>> vegetarian_pizzas = Pizza.objects.filter(vegetarian=True) >>> Restaurant.objects.prefetch_related( ... Prefetch('pizzas', to_attr='menu'), ... Prefetch('pizzas', queryset=vegetarian_pizzas, to_attr='vegetarian_menu'))
需要注意的是。有一些函数会清空prefetch()存下的数据
Remember that, as always with QuerySets
, any subsequent chained methods which imply a different database query will ignore previously cached results, and retrieve data using a fresh database query. So, if you write the following:
>>> pizzas = Pizza.objects.prefetch_related('toppings')
>>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
...then the fact that pizza.toppings.all()
has been prefetched will not help you. The prefetch_related('toppings')
implied pizza.toppings.all()
, but pizza.toppings.filter()
is a new and different query. The prefetched cache can't help here; in fact it hurts performance, since you have done a database query that you haven't used. So use this feature with caution!
Also, if you call the database-altering methods add()
, remove()
, clear()
or set()
, onrelated managers
, any prefetched cache for the relation will be cleared.