2010-05-14 14 views
12

tengo 2 modelos en mi código de Django:obtener objetos de clave externa en una sola consulta - Django

class ModelA(models.Model): 
    name = models.CharField(max_length=255) 
    description = models.CharField(max_length=255) 
    created_by = models.ForeignKey(User) 

class ModelB(models.Model): 
    category = models.CharField(max_length=255) 
    modela_link = models.ForeignKey(ModelA, 'modelb_link') 
    functions = models.CharField(max_length=255) 
    created_by = models.ForeignKey(User) 

Say ModelA tiene 100 registros, todos los cuales pueden o no tener enlaces a ModelB

ahora digo que quiero obtener una lista de todos los registros ModelA junto con los datos de ModelB

que haría:

list_a = ModelA.objects.all() 

Luego de obtener los datos para ModelB que tendría que hacer

for i in list_a: 
    i.additional_data = i.modelb_link.all() 

Sin embargo, este ejecuta una consulta en cada caso de i. Por lo tanto, hacer 101 consultas para ejecutar.

¿Hay alguna manera de ejecutar todo esto en solo 1 consulta. O al menos menos de las 101 consultas.

He intentado poner en ModelA.objects.select_related().all() pero esto no pareció tener ningún efecto.

Gracias

Respuesta

7

Como dice Ofri, select_related sólo funciona en las relaciones hacia delante, no revertir queridos.

No hay manera integrada para seguir automáticamente las relaciones en Django inversa, pero ver my blog post una técnica para hacerlo razonablemente eficiente. La idea básica es obtener todos los objetos relacionados para cada elemento a la vez, luego asociarlos manualmente con su elemento relacionado, para que pueda hacerlo en 2 consultas en lugar de n + 1.

2

ORM de Django es una buena cosa, pero algunas cosas es mejor que hacer manualmente. Puede importar el cursor de conexión y ejecutar sql sin formato en una sola consulta.

from django.db import connection 
cur=connection.cursor() 
cur.execute(query) 
rows = cur.fetchall() 

la consulta debe ser similar (para MySQL)

SELECT * FROM appname_modela INNER JOIN appname_modelb ON appname_modela.id=appname_modelb.modela_link_id 
+0

Sé que al usar sql sin procesar no se devuelve un objeto, así que conoces alguna forma de convertir los resultados en un objeto de Django – John

1

La razón .select_related() no funciona, es que .select_related() se utiliza para seguir las claves externas. Su ModelA no tiene una clave externa para ModelB. Su modelo B que tiene una clave externa para ModelA. (por lo que una instancia de ModelA puede tener varias instancias de ModelB relacionadas con ella).

Usted puede usar esto para hacerlo en 2 consultas, y un poco de código Python:

list_b = ModelB.objects.all() 
list_a = ModelA.objects.all() 
for a in list_a: 
    a.additional_data = [b for b in list_b if b.modela_link_id==a.id] 
+1

Gracias por esto. ¿Hay algún problema de rendimiento de este – John

Cuestiones relacionadas