python - create - orm django queries
Django ORM: Seleccionando conjunto relacionado (4)
Creo que lo que está diciendo es: "Quiero todas las opciones para un conjunto de encuestas". Si es así, intente esto:
polls = Poll.objects.filter(category=''foo'')
choices = Choice.objects.filter(poll__in=polls)
Digamos que tengo 2 modelos:
class Poll(models.Model):
category = models.CharField(u"Category", max_length = 64)
[...]
class Choice(models.Model):
poll = models.ForeignKey(Poll)
[...]
Dado un objeto de encuesta, puedo consultar sus opciones con:
poll.choice_set.all()
Pero, ¿existe una función de utilidad para consultar todas las opciones de un conjunto de Encuesta?
En realidad, estoy buscando algo como lo siguiente (que no es compatible y no busco cómo podría ser):
polls = Poll.objects.filter(category = ''foo'').select_related(''choice_set'')
for poll in polls:
print poll.choice_set.all() # this shouldn''t perform a SQL query at each iteration
Hice una función (fea) para ayudarme a lograr eso:
def qbind(objects, target_name, model, field_name):
objects = list(objects)
objects_dict = dict([(object.id, object) for object in objects])
for foreign in model.objects.filter(**{field_name + ''__in'': objects_dict.keys()}):
id = getattr(foreign, field_name + ''_id'')
if id in objects_dict:
object = objects_dict[id]
if hasattr(object, target_name):
getattr(object, target_name).append(foreign)
else:
setattr(object, target_name, [foreign])
return objects
que se utiliza de la siguiente manera:
polls = Poll.objects.filter(category = ''foo'')
polls = qbind(polls, ''choices'', Choice, ''poll'')
# Now, each object in polls have a ''choices'' member with the list of choices.
# This was achieved with 2 SQL queries only.
¿Hay algo más fácil ya proporcionado por Django? O al menos, un fragmento haciendo lo mismo de una manera mejor.
¿Cómo manejas este problema usualmente?
Creo que lo que está intentando hacer es el término "carga impaciente" de datos secundarios, lo que significa que está cargando la lista secundaria (choice_set) para cada Encuesta, pero todo en la primera consulta al DB, para que no tenga para hacer un montón de consultas más adelante.
Si esto es correcto, entonces lo que está buscando es ''select_related'' - vea https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related
Noté que intentaste ''relacionado con select_'' pero no funcionó. ¿Puedes intentar hacer el ''select_related'' y luego el filtro. Eso podría arreglarlo.
ACTUALIZACIÓN: Esto no funciona, ver comentarios a continuación.
El tiempo ha pasado y esta funcionalidad ahora está disponible en Django 1.4 con la introducción de la función QuerySet prefetch_related() . Esta función hace efectivamente lo que realiza la función qbind
sugerida. es decir. Se realizan dos consultas y la unión se produce en el terreno de Python, pero ahora esta se maneja mediante el ORM.
La solicitud de consulta original ahora se convertiría en:
polls = Poll.objects.filter(category = ''foo'').prefetch_related(''choice_set'')
Como se muestra en el siguiente ejemplo de código, los polls
QuerySet se pueden usar para obtener todos los objetos de Choice
por Poll
sin que sea necesario realizar ningún otro golpe de base de datos:
for poll in polls:
for choice in poll.choice_set:
print choice
Actualización : Desde Django 1.4, esta característica está integrada: ver prefetch_related .
Primera respuesta: no pierda tiempo escribiendo algo como qbind hasta que ya haya escrito una aplicación que funcione, la haya perfilado y demostrado que las consultas N son en realidad un problema de rendimiento para su base de datos y escenarios de carga.
Pero tal vez has hecho eso. Entonces, la segunda respuesta: qbind () hace lo que debe hacer, pero sería más idiomático si se empaquetara en una subclase QuerySet personalizada, con una subclase de Manager adjunta que devuelva instancias del QuerySet personalizado. Idealmente, podría incluso hacerlos genéricos y reutilizables para cualquier relación inversa. Entonces podrías hacer algo como:
Poll.objects.filter(category=''foo'').fetch_reverse_relations(''choices_set'')
Para ver un ejemplo de la técnica Manager / QuerySet, vea este fragmento de código, que resuelve un problema similar, pero para el caso de las claves externas genéricas, no las relaciones inversas. No sería demasiado difícil combinar las entrañas de su función qbind () con la estructura que se muestra allí para hacer una buena solución a su problema.