Abbiamo un'app con dati altamente correlati, vale a dire che ci sono molti casi in cui due oggetti possono riferirsi allo stesso oggetto tramite una relazione. Per quanto posso dire, Django non fa alcun tentativo di restituire un riferimento a un oggetto già recuperato se si tenta di recuperarlo tramite una relazione diversa, precedentemente non valutata.Evitare più riferimenti allo stesso oggetto in Django ORM
Ad esempio:
class Customer(Model):
firstName = CharField(max_length = 64)
lastName = CharField(max_length = 64)
class Order(Model):
customer = ForeignKey(Customer, related_name = "orders")
Allora supponiamo di avere un singolo cliente che ha due ordini nel DB:
order1, order2 = Order.objects.all()
print order1.customer # (1) One DB fetch here
print order2.customer # (2) Another DB fetch here
print order1.customer == order2.customer # (3) True, because PKs match
print id(order1.customer) == id(order2.customer) # (4) False, not the same object
Quando si dispone di dati altamente interconnessi, il grado in cui l'accesso ai rapporti di i tuoi oggetti risultano in interrogazioni ripetute del DB per gli stessi aumenti di dati e diventa un problema.
Programmiamo anche per iOS e una delle cose belle di CoreData è che mantiene contesto, in modo che in un dato contesto ci sia sempre una sola istanza di un dato modello. Nell'esempio precedente, CoreData non avrebbe eseguito il secondo recupero in (2), poiché avrebbe risolto la relazione utilizzando il cliente già in memoria.
Anche se la riga (2) è stata sostituita con un esempio spuria progettato per forzare un altro recupero di database (come print Order.objects.exclude(pk = order1.pk).get(customer = order1.customer)
), CoreData si renderebbe conto che il risultato di tale secondo recupero è stato risolto in un modello in memoria e restituisce il modello esistente anziché uno nuovo (es. (4) stamperebbe True in CoreData perché sarebbe effettivamente lo stesso oggetto).
di copertura contro questo comportamento di Django, stiamo un po scrivendo tutta questa roba orribile per cercare di modelli di cache in memoria per la loro (type, pk)
e quindi controllare le relazioni con il suffisso _id
per cercare di tirarli dalla cache prima di colpire ciecamente la DB con un altro recupero. Questo sta riducendo il throughput di DB, ma si sente davvero fragile e probabilmente causa problemi se le normali ricerche di relazioni tramite proprietà avvengono accidentalmente in alcuni framework di contrib o middleware che non controlliamo.
Esistono best practice o framework per Django per evitare questo problema? Qualcuno ha tentato di installare una sorta di contesto thread-locale nell'ORM di Django per evitare ripetute ricerche e avere più istanze in memoria che si collegano allo stesso modello DB?
So che roba da cache di query come JohnnyCache è disponibile (e aiuta a ridurre il throughput del DB), tuttavia esiste ancora il problema di istanze multiple che mappano allo stesso modello sottostante anche con quelle misure in atto.
Grazie Daniel - non è stato aggiornato in un tempo spaventosamente lungo, ma ci provo e vediamo se funziona ancora. – glenc
Quindi, dopo una piccola ricerca, sembra che questa cosa potrebbe funzionare, perché fondamentalmente riaggancia il metaclass '__call__' per restituire le istanze memorizzate nella cache invece di nuove quando si ottengono hit della cache (type, pk). Questo comunque si basa totalmente sul caching delle query, perché non fornisce sottoclassi di ForeignKey che sanno come restituire le istanze memorizzate nella cache prima di lanciare una query reale, quindi non ideale al 100%. Probabilmente implementerà questo ForeignKey in un fork di github e pubblicherà qui i risultati. – glenc