Obtenga un conjunto de consultas de objetos a través de un modelo intermedio
-
07-07-2019 - |
Pregunta
Quiero obtener todos los objetos Geom que están relacionados con un determinado content_object (vea la función que estoy tratando de construir en la parte inferior, get_geoms_for_obj ()
class Geom(models.Model):
...
class GeomRelation(models.Model):
''' For tagging many objects to a Geom object and vice-versa'''
geom = models.ForeignKey(Geom)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
def get_geoms_for_object(obj):
''' takes an object and gets the geoms that are related
'''
ct = ContentType.objects.get_for_model(obj)
id = obj.id
grs = GeomRelation.objects.filter( content_type=ct, object_id=id )
# how with django orm magic can I build the queryset instead of list
# like below to get all of the Geom objects for a given content_object
geoms = []
for gr in grs:
geoms.append(gr.geom)
return set(geoms)
# A set makes it so that I have no redundant entries but I want the
# queryset ordering too .. need to make it a queryset for so many reasons...
Solución
Duh,
return Geom.objects.filter(geomrelation__in=grs)
Otros consejos
Si su clase Geom tiene una propiedad genérica.GenericRelation (), puede obtener los objetos mediante una relación estándar hacia atrás.
class Geom(models.Model):
...
geom_relations = generic.GenericRelation(GeomRelation)
Esta es una propiedad exclusiva de Python, que no necesita un cambio en la base de datos. Ahora para obtener las geom_relations, solo debes hacer:
geom.geom_relations.all()
Si pasa un objeto QuerySet como argumento de filtro para otro, el ORM usará instrucciones de selección anidadas. Es mejor unir las relaciones de la tabla en las búsquedas de filtro; entonces el ORM usará una simple cláusula WHERE en JOIN que debe hacer de todos modos. La diferencia en el rendimiento es significativa:
In [2]: from django.db import connection
In [3]: from app.models import *
In [4]: ct = ContentType.objects.get_for_model(Thing)
In [5]: grs = GeomRelation.objects.filter( content_type=ct, object_id=2 )
In [6]:
In [7]: # slow method
In [8]: list(Geom.objects.filter(geomrelation__in=grs));
In [9]: connection.queries[-1]
Out[9]:
{'sql': u'SELECT "app_geom"."id" FROM "app_geom" INNER JOIN "app_geomrelation" ON ("app_geom"."id" = "app_geomrelation"."geom_id") WHERE "app_geomrelation"."id" IN (SELECT U0."id" FROM "app_geomrelation" U0 WHERE (U0."content_type_id" = 10 AND U0."object_id" = 2 ))',
'time': '0.140'}
In [10]:
In [11]: # fast method
In [12]: list(Geom.objects.filter(geomrelation__content_type=ct,
....: geomrelation__object_id=2));
In [13]: connection.queries[-1]
Out[13]:
{'sql': u'SELECT "app_geom"."id" FROM "app_geom" INNER JOIN "app_geomrelation" ON ("app_geom"."id" = "app_geomrelation"."geom_id") WHERE ("app_geomrelation"."object_id" = 2 AND "app_geomrelation"."content_type_id" = 10 )',
'time': '0.001'}