Django выберите связанный в необработанном запросе - PullRequest
13 голосов
/ 17 февраля 2012

Как сделать "ручную" имитацию 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, что связанные модели уже получены.

Ответы [ 3 ]

11 голосов
/ 26 марта 2015

Решение с 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.

8 голосов
/ 10 апреля 2015

Не уверен, если вам все еще нужно это, но я решил это, начиная с ответа Alasdair. Вы хотите использовать информацию из запроса для построения модели, или она все равно будет запускать дополнительные запросы при попытке доступа к полю внешнего ключа. Так что в вашем случае вы бы хотели:

    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 не будет запущен другой запрос к базе данных.

2 голосов
/ 17 февраля 2012

Я не уверен, что вы можете сделать это.В качестве альтернативы вы можете выбрать отдельные поля из таблицы стран и получить к ним доступ в каждом случае.

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
...