2010-05-30 23 views
7

¿Tiene select_related trabajo para las relaciones GenericRelation, o hay una alternativa razonable? Por el momento, Django está haciendo llamadas sql individuales para cada elemento en mi queryset, y me gustaría evitar eso usando algo como select_related.Django: select_related y GenericRelation

class Claim(models.Model): 
    proof = generic.GenericRelation(Proof) 


class Proof(models.Model): 
    content_type = models.ForeignKey(ContentType) 
    object_id = models.PositiveIntegerField() 
    content_object = generic.GenericForeignKey('content_type', 'object_id') 

estoy seleccionando un montón de las reivindicaciones, y me gustaría que las pruebas relacionadas a ser tirado en lugar de consultarse individualmente.

Respuesta

16

necesita No hay un mecanismo incorporado para realizar esta acción. Pero publiqué una técnica para simular select_related en las relaciones genéricas on my blog. contenido


Blog resume:

podemos utilizar _content_object_cache campo de Django para crear nuestra propia esencia select_related para las relaciones genéricas.

generics = {} 
for item in queryset: 
    generics.setdefault(item.content_type_id, set()).add(item.object_id) 

content_types = ContentType.objects.in_bulk(generics.keys()) 

relations = {} 
for ct, fk_list in generics.items(): 
    ct_model = content_types[ct].model_class() 
    relations[ct] = ct_model.objects.in_bulk(list(fk_list)) 

for item in queryset: 
    setattr(item, '_content_object_cache', 
      relations[item.content_type_id][item.object_id]) 

Aquí tenemos todos los diferentes tipos de contenido utilizados por las relaciones en el conjunto de consultas, y el conjunto de ID de objetos diferentes para cada uno, y luego utiliza el incorporado en in_bulk método gerente para conseguir todo los tipos de contenido a la vez en un buen diccionario listo para usar con ID. A continuación, hacemos una consulta según el tipo de contenido, utilizando de nuevo in_bulk, para obtener toda la objeto real.

Finalmente, simplemente establecemos el objeto relevante en el campo _content_object_cache del elemento de origen. La razón por la que hacemos esto es que este es el atributo que Django comprobaría, y poblar si necesario, si se llama a x.content_object directamente. Al pre-poblar , nos estamos asegurando de que Django nunca necesitará llamar a la persona de búsqueda - en efecto, lo que estamos haciendo está llevando a cabo una especie de select_related() para las relaciones genéricas.

3

puede utilizar .extra() para extraer manualmente los campos:

Claims.filter(proof__filteryouwant=valueyouwant).extra(select={'field_to_pull':'proof_proof.field_to_pull'}) 

El .Filter() va a hacer la unión, la .extra() tirará de un campo. proof_proof es el nombre de la tabla SQL para el modelo de prueba. Si necesita más de un campo, especifique cada uno de ellos en el dictionario.