Mamy aplikację z bardzo wzajemnie powiązanymi danymi, tj. Istnieje wiele przypadków, w których dwa obiekty mogą odwoływać się do tego samego obiektu poprzez relację. O ile mogę powiedzieć, Django nie podejmuje żadnej próby zwrócenia odwołania do już pobranego obiektu, jeśli spróbujesz go pobrać przez inną, uprzednio nieocenioną relację.Unikanie wielu odniesień do tego samego obiektu w Django ORM
Na przykład:
class Customer(Model):
firstName = CharField(max_length = 64)
lastName = CharField(max_length = 64)
class Order(Model):
customer = ForeignKey(Customer, related_name = "orders")
Następnie zakładamy mają mamy jednego klienta, który ma dwa zlecenia w 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
Gdy masz dane silnie powiązane ze sobą, stopień, do którego dostęp relacje Twoje obiekty powodują powtarzające się zapytania do DB, ponieważ te same dane zwiększają się i stają się problemem.
Programujemy również dla iOS i jedną z miłych rzeczy na temat CoreData jest to, że zachowuje on kontekst, dzięki czemu w danym kontekście istnieje tylko jedna instancja danego modelu. W powyższym przykładzie CoreData nie wykonałby drugiego pobrania w (2), ponieważ rozwiązałoby to relację z wykorzystaniem klienta już w pamięci.
Nawet jeśli linia (2) została zastąpiona niepoprawnym przykładem zaprojektowanym do wymuszania kolejnego pobierania DB (jak print Order.objects.exclude(pk = order1.pk).get(customer = order1.customer)
), CoreData uświadomiłaby sobie, że wynik tego drugiego pobrania został rozwiązany na modelu w pamięci i zwrócił istniejący model zamiast nowy (tj. (4) wypisze True w CoreData, ponieważ będą one faktycznie tym samym obiektem).
Aby zabezpieczyć się przed tym zachowanie Django, jesteśmy trochę pisanie wszystkie straszne rzeczy, aby spróbować modeli pamięci podręcznej w pamięci ich (type, pk)
a następnie sprawdzić relacje z _id
przyrostkiem, aby spróbować wyciągnąć je z pamięci podręcznej przed ślepo uderzanie DB z innym pobieraniem. Ogranicza to przepustowość bazy danych, ale wydaje się bardzo krucha i może powodować problemy, jeśli normalne poszukiwanie relacji poprzez właściwości przypadkowo stanie się w jakimś contrib framework lub middleware, którego nie kontrolujemy.
Czy istnieją jakieś dobre praktyki lub struktury dla Django, aby uniknąć tego problemu? Czy ktoś próbował zainstalować jakiś rodzaj lokalnego kontekstu wątku w ORM Django, aby uniknąć powtarzania wyszukiwań i posiadania wielu odwzorowań instancji w pamięci do tego samego modelu bazy danych?
Wiem, że pamięć podręczna, taka jak JohnnyCache, jest tam (i pomaga zmniejszyć przepustowość bazy danych), jednak nadal istnieje problem związany z mapowaniem wielu instancji do tego samego podstawowego modelu, nawet przy zastosowaniu tych środków.
Dziękuję Danielowi - nie zaktualizowano go zbyt długo, ale sprawdzę, czy nadal działa. – glenc
Po drobnych badaniach wygląda na to, że to coś może działać, ponieważ w zasadzie ponownie przywiązuje metaclass '__call__' do zwracania buforowanych instancji zamiast nowych, gdy otrzymujesz (cache, pk) trafienia w pamięci podręcznej. To nadal całkowicie opiera się na buforowaniu zapytań, ponieważ nie zapewnia podklasów obiektu ForeignKey, które wiedzą, jak zwracać buforowane wystąpienia przed uruchomieniem prawdziwego zapytania - więc nie jest to w 100% idealne. Prawdopodobnie zaimplementuje ten klucz obcy w widelcu github i opublikuje go tutaj z wynikami. – glenc