2010-05-30 15 views
7

Il lavoro select_related per le relazioni GenericRelation o esiste un'alternativa ragionevole? Al momento Django sta facendo call individuali per ogni oggetto nel mio queryset, e vorrei evitare di usare qualcosa come select_related.Django: select_related e 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') 

che sto selezionando un mucchio delle rivendicazioni, e mi piacerebbe delle relative prove per essere tirato dentro anziché interrogati individualmente.

risposta

16

Non c'è un modo integrato per farlo. Ma ho pubblicato una tecnica per simulare lo select_related sulle relazioni generiche on my blog. contenuti


Blog riassunta:

possiamo utilizzare il campo di Django _content_object_cache di creare in sostanza il nostro select_related per le relazioni generiche.

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]) 

Qui abbiamo tutti i diversi tipi di contenuto utilizzati dai rapporti nella queryset, e l'insieme di ID oggetto distinte per ciascuno, quindi utilizzare il built-in in_bulk metodo gestore per ottenere tutti i tipi di contenuto contemporaneamente in un bel dizionario pronto per l'uso digitato da ID. Quindi, eseguiamo una query per tipo di contenuto, utilizzando nuovamente in_bulk, per ottenere tutto l'effettivo oggetto .

Infine, abbiamo semplicemente impostato l'oggetto relativo sul campo _content_object_cache dell'elemento di origine. Il motivo per cui lo facciamo è che questo è l'attributo che Django controllerebbe, e popola se è necessario , se si chiama direttamente x.content_object. Pre-popolando , ci assicuriamo che Django non debba mai chiamare la singola ricerca - in effetti quello che stiamo facendo è implementare una specie di select_related() per le relazioni generiche.

3

è possibile utilizzare .extra() per estrarre manualmente i campi:

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

Il .filter() farà il join, il .extra() tirerà un campo. proof_proof è il nome della tabella SQL per il modello Proof. Se hai bisogno di più di un campo, specifica ciascuno di essi nel dizionario.