Django - prefetch_related 对于特定情况不起作用
Django - prefetch_related for specific situation doesn't work
我有三个模型:Trip
、City
和 GoogleApiCityCache
。
如果我想知道城市的名称,我在 City
对象上使用 @property
,该对象试图获取当前语言的 GoogleApiCityCache
。
我想减少模板中的查询数量:
{% for trip in trips %}
{{ trip.city.formatted_address }}
{% endfor %}
现在,它执行新的数据库查询以在每次迭代中查找 GoogleApiCityCache
。我想预取这些缓存。
所以属性formatted_address
必须从这个对象中找到一个GoogleApiCityCache
并得到formatted_address
。
尝试使用 prefetch_related
但没有用。
def profile(request, slug):
owner = User.objects.filter(userprofile__slug=slug).prefetch_related('trips__city__city_caches').first()
trips = Trip.objects.filter(user=owner).select_related('city').prefetch_related('city__city_caches')
return render(request, 'profiles/profile/profile.html', {'owner': owner,'trips':trips})
我的模特:
class Trip(models.Model):
user = models.ForeignKey('auth.User', related_name='trips')
city = models.ForeignKey('locations.City', related_name='trips')
date_from = models.DateField()
date_to = models.DateField()
detail = models.TextField(null=True, blank=True)
# participants = models.ManyToManyField('auth.User', related_name='participating_on_trips', blank=True)
objects = TripManager()
class City(models.Model):
...
def get_cache_by_lang(self, lang=None, force_refresh=False):
if not lang:
lang = get_language() or 'en'
cache, requested = GoogleApiCityCache.objects.get_or_request(city=self, lang=lang)
return cache
@property
def formatted_address(self):
return self.get_cache_by_lang().formatted_address
class GoogleApiCityCacheManager(models.Manager):
def _request_create(self, city, lang):
""" Create cache (wasn't found) """
....
return cache
def get_or_request(self, city, lang):
created = False
try:
cache = GoogleApiCityCache.objects.get(city=city, language_code=lang)
except GoogleApiCityCache.DoesNotExist:
cache = self._request_create(city, lang)
created = True
return cache,created
class GoogleApiCityCache(models.Model):
language_code = models.CharField(max_length=5)
api_json_response = JSONField(null=True, blank=True)
city = models.ForeignKey('locations.City', on_delete=models.CASCADE, related_name='city_caches')
objects = GoogleApiCityCacheManager()
class Meta:
unique_together = ('city', 'language_code')
@property
def formatted_address(self):
try:
return self.api_json_response['result']['formatted_address']
except:
return None
如您所见,city
和 language_code
的 GoogleApiCityCache
可以是 none。在这种情况下,请求 Google Api,创建一个新的 GoogleApiCityCache
对象并返回。
当您 prefetch_related('trips__city__city_caches')
时,您正在预取 city_caches
。这意味着下面的循环不会引起任何额外的查询。
for cache in city.city_caches.all()
print(cache)
但是,在您的代码中,formatted_address
调用 get_cache_by_lang
,后者调用 get_or_request
,后者调用 GoogleApiCityCache.objects.get()
,这将导致额外的查询。
为了利用 prefetch_related
,您需要重写 get_cache_by_lang
,使其循环遍历 self.city_caches.all()
以找到正确的对象。
我有三个模型:Trip
、City
和 GoogleApiCityCache
。
如果我想知道城市的名称,我在 City
对象上使用 @property
,该对象试图获取当前语言的 GoogleApiCityCache
。
我想减少模板中的查询数量:
{% for trip in trips %}
{{ trip.city.formatted_address }}
{% endfor %}
现在,它执行新的数据库查询以在每次迭代中查找 GoogleApiCityCache
。我想预取这些缓存。
所以属性formatted_address
必须从这个对象中找到一个GoogleApiCityCache
并得到formatted_address
。
尝试使用 prefetch_related
但没有用。
def profile(request, slug):
owner = User.objects.filter(userprofile__slug=slug).prefetch_related('trips__city__city_caches').first()
trips = Trip.objects.filter(user=owner).select_related('city').prefetch_related('city__city_caches')
return render(request, 'profiles/profile/profile.html', {'owner': owner,'trips':trips})
我的模特:
class Trip(models.Model):
user = models.ForeignKey('auth.User', related_name='trips')
city = models.ForeignKey('locations.City', related_name='trips')
date_from = models.DateField()
date_to = models.DateField()
detail = models.TextField(null=True, blank=True)
# participants = models.ManyToManyField('auth.User', related_name='participating_on_trips', blank=True)
objects = TripManager()
class City(models.Model):
...
def get_cache_by_lang(self, lang=None, force_refresh=False):
if not lang:
lang = get_language() or 'en'
cache, requested = GoogleApiCityCache.objects.get_or_request(city=self, lang=lang)
return cache
@property
def formatted_address(self):
return self.get_cache_by_lang().formatted_address
class GoogleApiCityCacheManager(models.Manager):
def _request_create(self, city, lang):
""" Create cache (wasn't found) """
....
return cache
def get_or_request(self, city, lang):
created = False
try:
cache = GoogleApiCityCache.objects.get(city=city, language_code=lang)
except GoogleApiCityCache.DoesNotExist:
cache = self._request_create(city, lang)
created = True
return cache,created
class GoogleApiCityCache(models.Model):
language_code = models.CharField(max_length=5)
api_json_response = JSONField(null=True, blank=True)
city = models.ForeignKey('locations.City', on_delete=models.CASCADE, related_name='city_caches')
objects = GoogleApiCityCacheManager()
class Meta:
unique_together = ('city', 'language_code')
@property
def formatted_address(self):
try:
return self.api_json_response['result']['formatted_address']
except:
return None
如您所见,city
和 language_code
的 GoogleApiCityCache
可以是 none。在这种情况下,请求 Google Api,创建一个新的 GoogleApiCityCache
对象并返回。
当您 prefetch_related('trips__city__city_caches')
时,您正在预取 city_caches
。这意味着下面的循环不会引起任何额外的查询。
for cache in city.city_caches.all()
print(cache)
但是,在您的代码中,formatted_address
调用 get_cache_by_lang
,后者调用 get_or_request
,后者调用 GoogleApiCityCache.objects.get()
,这将导致额外的查询。
为了利用 prefetch_related
,您需要重写 get_cache_by_lang
,使其循环遍历 self.city_caches.all()
以找到正确的对象。