2009-03-25 28 views
7

Rozważmy następujący szkielet models.py dla gry podboju kosmosu:Django ORM: buforowanie i manipulowanie ForeignKey obiektów

class Fleet(models.Model): 
    game = models.ForeignKey(Game, related_name='planet_set') 
    owner = models.ForeignKey(User, related_name='planet_set', null=True, blank=True) 
    home = models.ForeignKey(Planet, related_name='departing_fleet_set') 
    dest = models.ForeignKey(Planet, related_name='arriving_fleet_set') 
    ships = models.IntegerField() 

class Planet(models.Model): 
    game = models.ForeignKey(Game, related_name='planet_set') 
    owner = models.ForeignKey(User, related_name='planet_set', null=True, blank=True) 
    name = models.CharField(max_length=250) 
    ships = models.IntegerField() 

mam wiele modeli takich danych do projektu pracuję nad, i ja zmienić stan gry na podstawie nieco skomplikowanych interakcji między różnymi obiektami danych. Chcę uniknąć wiele niepotrzebnych połączeń do bazy danych, więc raz na turę, robię coś

  1. Query wszystkich flot, planet i innych obiektów z bazy danych i buforować je jako obiekty Pythona
  2. przetworzyć obiekty gry, rozwiązywanie stan gry
  3. zapisać je z powrotem w bazie

model ten wydaje się całkowicie rozbić przy użyciu ForeignKey obiektów. Na przykład, kiedy nowa flota odchodzi planetę, mam wiersza, który wygląda mniej więcej tak:

fleet.home.ships -= fleet.ships 

Po linia ta biegnie, mam inny kod, który zmienia liczbę statków w każdej z planet, w tym planeta flota.home. Niestety, zmiany dokonane w powyższym wierszu nie znajdują odzwierciedlenia w QuerySet planet, które otrzymałem wcześniej, więc kiedy zapisuję wszystkie planety na końcu tury, zmiany w statkach floty.home zostają nadpisane.

Czy jest jakiś lepszy sposób radzenia sobie z tą sytuacją? A może tak wygląda ORM?

Odpowiedz

20

Django ORM nie wdrożyć identity map (to w ticket tracker, ale nie jest jasne, czy i kiedy zostanie on wdrożony, co najmniej jeden rdzeń Django committer ma expressed opposition to it). Oznacza to, że jeśli dojdziesz do tego samego obiektu bazy danych za pośrednictwem dwóch różnych ścieżek zapytań, pracujesz z różnymi obiektami Pythona w pamięci.

Oznacza to, że Twój projekt (załaduj wszystko do pamięci naraz, zmodyfikuj wiele rzeczy, a następnie zapisz wszystko z powrotem na końcu) jest niemożliwy do wykonania przy użyciu Django ORM. Po pierwsze, ponieważ często marnuje mnóstwo pamięci w duplikatach tego samego obiektu, a po drugie z powodu problemów z "nadpisywaniem", takich jak ten, na który się natknąłeś.

Musisz przerobić swój projekt, aby uniknąć tych problemów (bądź ostrożny, aby pracować tylko z jednym zestawem QuerySet naraz, zapisując wszystko, co zostało zmodyfikowane, zanim zrobisz inne zapytanie, lub jeśli załadujesz kilka zapytań, wyszukaj wszystkie relacje ręcznie , nigdy nie przechodź przez ForeignKeys za pomocą wygodnych dla nich atrybutów) lub użyj alternatywnej ORM Pythona, która implementuje mapę tożsamości. SQLAlchemy jest jedną z opcji.

Należy zauważyć, że nie oznacza to, że ORM Django jest "zły". Jest zoptymalizowany pod kątem aplikacji internetowych, gdzie tego typu problemy są rzadkie (robiłem web development z Django od lat i nigdy nie miałem tego problemu na prawdziwym projekcie). Jeśli Twój przypadek użycia jest inny, możesz wybrać inną ORM.

+1

Ładnie zestawione –

+0

Tak, dziękuję bardzo za odpowiedzi informacyjny.Wiem, że to nie oznacza, że ​​ORM Django jest zły; w rzeczywistości opracowałem cały projekt przy użyciu Django, który nie wymaga tego skomplikowanego przetwarzania danych, a ten problem nigdy nie pojawił się, dlatego też miałem taką stratę. –

Powiązane problemy