Выбор Django в необработанном запросе
Как сделать "ручную" select_related имитацию, чтобы избежать нежелательных ударов БД?
имеем:
class Country:
name = CharField()
class City:
country = models.ForeignKey(Country)
name = models.CharField()
cities = City.objects.raw("select * from city inner join country on city.country_id = country.id where name = 'london'")
#this will hill hit DB
print cities[0].country.name
Как сообщить django, что связанные модели уже получены.
Ответы
Ответ 1
Решение с prefetch_related
(это означает, что будут сделаны два запроса: 1 для cities
и 1 для countries
), взятых из django-users, который не является частью общедоступного API, но работает над Django 1.7
from django.db.models.query import prefetch_related_objects
#raw querysets do not have len()
#thats why we need to evaluate them to list
cities = list(City.objects.raw("select * from city inner join country on city.country_id = country.id where name = 'london'"))
prefetch_related_objects(cities, ['country'])
ОБНОВЛЕНИЕ
Теперь в Django 1.10 prefetch_related_objects
является частью общедоступного API.
Ответ 2
Не уверен, что вам все еще нужно это, но я решил это, начиная с ответа Аласдайра. Вы хотите использовать информацию из запроса для сборки модели, иначе она будет запускать дополнительные запросы при попытке получить доступ к полю внешнего ключа. Поэтому в вашем случае вам нужно:
cities = list(City.objects.raw("""
SELECT
city.*, country.name as countryName
FROM
cities INNER JOIN country ON city.country_id = country.id
WHERE
city.name = 'LONDON"""))
for city in cities:
city.country = Country(name=city.countryName)
Линия, которая назначает страну, не попадает в базу данных, она просто создает модель. Затем после этого при доступе к city.country
он не будет запускать другой запрос базы данных.
Ответ 3
Я не уверен, сможешь ли ты это сделать. В качестве альтернативы вы можете выбрать отдельные поля из таблицы страны и получить к ним доступ в каждом экземпляре.
cities = City.objects.raw("select city.*, name as country_name from city inner join country on city.country_id = country.id where name = 'london'")
city = cities[0]
# this will not hit the database again
city.country_name