2012-02-17 17 views
12

¿Cómo hacer imitación select_related "manual" para evitar hits DB indeseables?Selección de Django relacionada con la solicitud bruta

tenemos:

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 

Cómo decirle a Django que los modelos se refieran han sido exagerado.

+0

Puede intentar utilizar .values ​​() para recuperar ciudades como diccionario. Ver: https: //docs.djangoproject.com/en/dev/ref/models/querysets/#values ​​ – Jingo

Respuesta

1

No estoy seguro si puede hacer esto. Como alternativa, puede seleccionar campos individuales de la tabla de países y acceder a ellos en cada instancia.

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 
+2

Sí, este enfoque funcionará, pero requiere un nuevo rediseño del código. – Andrew

7

una solución con prefetch_related(esto significa que se harán dos consultas, 1 para la cities y 1 para el countries) tomado de django-users que no es parte de la API pública pero está trabajando en 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']) 

ACTUALIZACIÓN

Ahora en Django 1.10 prefetch_related_objects es parte de la API pública.

6

No estoy seguro de si todavía necesita esto, pero lo resolví comenzando con la respuesta de Alasdair. Desea utilizar la información de la consulta para crear el modelo o de todos modos generará consultas adicionales cuando intente acceder al campo de la clave externa. Así, en su caso, que querría:

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) 

La línea que asigna el país no golpea la base de datos, es sólo la creación de un modelo. Luego, cuando acceda al city.country, no se activará otra consulta de la base de datos.

Cuestiones relacionadas