2017-01-11 49 views
12

Sytuacja

Django modelu proxy do innej bazy danych


Mamy kilka różnych aplikacji, które wykorzystują bilety od systemu wsparcia bilet na różnego rodzaju funkcjonalności.

Przede wszystkim mamy aplikację, która ma kilka modeli reprezentujących modele naszego systemu obsługi biletów Kayako. Ta aplikacja nie powinna wiedzieć nic o innych aplikacjach, które jej używają i powinna pozostać jak najbardziej ogólna. Ponieważ ta aplikacja korzysta z istniejących tabel Kayako, uruchamiamy ją w tej samej bazie danych. Nazwijmy tę aplikację kayakodb.

Jedna aplikacja łączy klientów z naszej bazy danych klientów z biletami w systemie obsługi biletów. Poprzednio ten system posiadał własną reprezentację biletów w naszym systemie obsługi zgłoszeń, wyszukując bilety za pomocą interfejsu API dostarczonego przez kayakodb. Następnie wykorzystała tę reprezentację biletów do połączenia klientów i domen. Było to jednak zbyt złożone i niezbyt logiczne. Dlatego zdecydowaliśmy się przełączyć na model proxy i przenieść modele reprezentujące łącza do klientów i domen na kayakodb. Nazwijmy tę aplikację sidebar.

Kolejna, nowa aplikacja pokazuje bilety z systemu wsparcia biletów w przejrzysty sposób wraz z połączeniami telefonicznymi, dzięki czemu nasz dział wsparcia może łatwo sprawdzić, które połączenia i bilety są powiązane z wybranymi klientami. Ten system ma model proxy dla modelu proxy z sidebar, ponieważ niektóre funkcje zapewnione przez model sidebar są również wymagane dla tej aplikacji wraz z innymi, które deklaruje nowy model proxy. Nazwijmy ten projekt WOW.

Aplikacje sidebar i WOW są częścią tego samego projektu/repozytorium. Nazwiemy to repozytorium Coneybeach, które ma własną bazę danych. Jednak kayakodb jest całkowicie niezależnym projektem. Jest on zawarty w Coneybeach poprzez plik wymagań, który instalujemy poprzez pip.

Problem


Przy tworzeniu migracji do nowej konfiguracji Django tworzy model proxy migracji dla zainstalowanego kayakodb co jest, oczywiście, nie iść. Za każdym razem, gdy instalujemy nową wersję kayakodb, zostanie ona nadpisana. Nie mówiąc już o tym, że kayakodb nie powinien wiedzieć nic o tym, które modele z niego korzystają.

kod


Ticket modelu wewnątrz kayakodb:

class Ticket(models.Model): 
    """ 
    This model is a representation of the data stored in the "kayako" database table "swtickets". Minus a lot of stuff 
    we don't use. If you add a field make sure it has the same name as the field in kayako.swtickets. 
    """ 
    # Fields, functions and manager etc. 

    class Meta: 
     db_table = 'swtickets' 
     managed = False 

Model SidebarTicket Pełnomocnik wewnątrz sidebar:

from kayakodb.models import Ticket  

class SidebarTicket(Ticket): 
    class Meta: 
     # Since this class is a wrapper we don't want to create a table for it. We only want to access the original 
     # model as we always do, but provide a different interface (when it comes to functions). Proxy models allow us 
     # to do this: https://docs.djangoproject.com/en/1.10/topics/db/models/#proxy-models 
     proxy = True 

     # Don't look for this model in the sidebar tables, but in the kayakodb tables. 
     app_label = 'kayakodb' 

    # Some extra functions 

potrzeb klasy ContactTicketWrapper dziedziczy od (a żądanie Hynekc er).Model ten jest używany jako model bazowy TicketWrapper i innego modelu reprezentującego połączeń (choć nie ma problemów z tym modelem ile jestem świadomy):

class Contact(models.Model): 
    type = None 

    class Meta: 
     abstract = True 

    def __getattr__(self, attr): 
     if attr in ['customers', 'add_customer_id', 'remove_all_customers', 'byters', 'domainnames', 'add_domain_name', 
        'remove_domain_name', 'add_text', 'remove_text', 'texts', 'creation_date', 'add_tag', 'get_tags', 
        'remove_tag', 'identifier']: 
      raise NotImplementedError('You should implement {}'.format(attr)) 
     raise AttributeError(attr) 

Model TicketWrapper proxy wewnątrz WOW:

from sidebar.models import SidebarTicket 

class TicketWrapper(Contact, SidebarTicket): 
    class Meta: 
     # Since this class is a wrapper we don't want to create a table for it. We only want to access the original 
     # model as we always do, but provide a different interface (when it comes to functions). Proxy models allow us 
     # to do this: https://docs.djangoproject.com/en/1.10/topics/db/models/#proxy-models 
     proxy = True 

     # Don't look for this model in the WOW database, but in the kayakodb database. 
     app_label = 'kayakodb' 

    # Some extra functions 

Co mają próbowałem


  • próbowałem nie określając th e app_label dla obu modeli proxy. Tworzy to poprawne migracje, ale powoduje, że modele proxy szukają modelu kayakodb.Ticket w bazie danych Coneybeach.
  • Podjęto próbę określenia abstract = True dla podklas, ale nie byłem pewien, czy to dlatego, że nadal chcę móc korzystać z menedżera dla modeli.
  • Zastanawiam się nad przeniesieniem migracji, która jest obecnie tworzona, do rzeczywistego projektu kayakodb, ale nie sądzę, że jest to dobre rozwiązanie. kayakodb nie powinien wiedzieć nic o implementacjach swoich modeli lub miejscach, w których są używane.
  • ./manage.py check zwraca 0 wydań.

Pytanie


Jak mogę utworzyć model proxy dla modelu, który znajduje się w innej bazy danych lub projektu?

Edit


Po ustawieniu modelu kayakodb.Ticket być niekontrolowana projekt WOW próbuje utworzyć migracji dla wszystkich modeli kayakodb. Rezultat:

Migrations for 'sidebar': 
    0004_auto_20170116_1210.py: 
    - Delete model Ticket 

Migrations for 'kayakodb': 
    0001_initial.py: 
    - Create model Staff 
    - Create model Tag 
    - Create model Ticket 
    - Create model TicketPost 
    - Create model TicketTag 
    - Create model TicketCustomer 
    - Create model TicketDomain 
    - Create proxy model SidebarTicket 
    - Alter unique_together for ticketdomain (1 constraint(s)) 
    - Alter unique_together for ticketcustomer (1 constraint(s)) 
    - Create proxy model TicketWrapper 
+0

@ e4c5 Tak, wiem o tym. To miło i wszystko, ale jeśli to jest konsekwencja, gdy zostanie nadpisane. – Bono

+0

Sam tak mówisz "To dlatego, że model proxy może zostać skierowany przez inny model w przyszłej migracji." Dlaczego w przeciwnym razie miałby przeszkadzać w stworzeniu migracji, jeśli można ją później wyrzucić? – Bono

+0

Masz mnie tam !! Wycofujemy głosowanie bliskie. – e4c5

Odpowiedz

4

Jak @hynekcer powiedział, że jeśli jest kayakodb istniejącą bazę danych, musisz ustawić managed = False dla wszystkich jej modeli. Jednak nadal pozostaje problem migracji do modelu proxy utworzonego w niewłaściwej aplikacji (kayakodb).

Rozwiązaniem, które może się przydać, jest zmiana app_label modelu proxy na dowolną aplikację przeznaczoną do migracji (sidebar w tym przypadku) i utworzenie routera, który wskaże ten model proxy, aby odczytać i zapisać z kayakodb.

E.g.model proxy:

# in sidebar/models.py 

class SidebarTicket(KayakoTicket): 
    class Meta: 
     proxy = True 
     app_label = 'sidebar' 

i router wewnątrz projektu, który używa go:

from django.conf import settings 
from kayakodb.models import Ticket 

class ProxyDatabaseRouter(object): 
    def allow_proxy_to_different_db(self, obj_): 
     # check if this is a sidebar proxy to the Ticket model in kayakodb 
     return isinstance(obj_, Ticket) and obj_._meta.proxy and obj_._meta.app_label == 'sidebar' 

    def db_for_read(self, model, **hints): 
     if issubclass(model, Ticket) and model._meta.proxy and model._meta.app_label == 'sidebar': 
      return 'kayakodb' 
     # the rest of the method goes here 

    def db_for_write(self, model, **hints): 
     if issubclass(model, Ticket) and model._meta.proxy and model._meta.app_label == 'sidebar': 
      return 'kayakodb' 
     return None 
     # the rest of the method goes here 

    def allow_relation(self, obj1, obj2, **hints): 
     if self.allow_proxy_to_different_db(obj1) or self.allow_proxy_to_different_db(obj2): 
      return True 
     # the rest of the method goes here 
+0

Jest to użyteczne, ale warunek 'if model == SidebarTicket:' jest nadal nieprawidłowy, ponieważ SidebarTicket jest tylko proxy, ale model bazy danych nie jest jeszcze wystarczająco opisany i dlatego nie jest znany prawidłowy warunek. – hynekcer

+1

@hynekcer jak już powiedziałem, jak dokładnie porównanie jest wykonywane nie jest ważne, rozważ powyższy pół pseudokod. DB do odczytu i zapisu można nawet zapisać w samym modelu, dzięki czemu metody routerów zwracają 'getattr (model," _CUSTOM_DATABASE ", Brak)' w razie potrzeby, co nie stanowi problemu, ponieważ model jest przekazywany do routera metody. –

+0

zaktualizował fragment kodu routera lepiej, jeśli brakowało klauzul i metody "allow_relation" –

4

tl; dr ale grepped swoje pytanie słowem router które nie zostało wymienione, więc myślałem rzeczą szukasz są Database Routers

+0

Jeśli pytanie brzmi "Jak utworzyć model proxy dla modelu znajdującego się w innej bazie danych lub projekcie", odpowiedź może brzmieć "użyj routera bazy danych". Przepraszamy za pomoc. – yedpodtrzitko

+0

Przepraszam, nie chciałem być niegrzeczny. Naprawdę przepraszam. Pisanie do modelu w innej bazie danych nie jest problemem (w każdym przypadku dla mnie). Problem polega na tym, że migracja jest tworzona w niewłaściwym miejscu (a jeśli przeczytałeś pytanie, zobaczyłbyś to :)). Powiedz, co, jeśli edytujesz swoje pytanie, aby dołączyć więcej informacji o routerach DB (podaj przykład). Będę mógł usunąć mój głos w dół. Patrząc wyłącznie na to pytanie, zgadzam się, że to może być odpowiedź. – Bono

4

tl; dr Zastosowanie

class Meta: 
    managed = False 

dla wszystkich modeli w db, które nie powinny być kontrolowane autor: Django. (Tj kayakodb)


Należy zauważyć, że PyPI Kayako jest API Python (bez Djago) do jakiejś aplikacji Kayako napisane w innym języku.

Przydaje się informacja, że ​​Kayako i WOW znajdują się w różnych bazach danych, ale nie są to informacje podstawowe. Django zezwala na np. ten jeden model występuje w dwóch bazach danych: podstawowej i dodatkowej db. Najważniejsze są opcje meta, w tym przypadku opcja managed = False. Dotyczy to przypadku Integrating Django with a legacy database. To nie oznacza tylko dokładnie bardzo starego projektu, ale projekt, który nie jest napisany w Pythonie + Django lub nie obsługuje migracji i nie udostępnia informacji, które migracje nie są jeszcze stosowane. Może jeśli nowa wersja Kayako doda nowe pola do bazy danych, nie musisz czytać tego pola lub jeśli dodasz je do modelu, Django nie jest odpowiedzialny za dodanie pola do bazy danych, ponieważ jest kontrolowane przez uaktualnienie Kayako.

Alternatywnie można użyć routerów bazy danych, aby utworzyć tabele dla aplikacji kayakodb (nigdzie), a także brak tabel w bazie danych kayakodb, np. Django dla użytkowników i grup:

plików myrouter.py lub podobnych

class MyRouter(object): 
    allow_migrate(db, app_label, model_name=None, **hints): 
     if app_label == 'kayakodb' or db == 'kayakodb': 
      return False 

plik settings.py

DATABASE_ROUTERS = ['path.to.myrouter.MyRouter',...] 
# ... if another router has been defined previously 

Zaletą jest to, że blokuje migrację na wszelkie bieżące lub przyszłe model, ale polecam dodać tekst class Meta: managed = False również gdzieś do komentarzy w kayakodb/models.py, aby uczynić go jasnym dla każdego późniejszego programisty, ponieważ może on łatwo zapomnieć najpierw przeczytać routery.

Możesz napisać zależność wersji projektu od minimalnej i maksymalnej wersji API Kayako, ale nie może to być migracja.


Twój Innym problemem jest to, że "A proxy model must inherit from exactly one non-abstract model class....", Ale model proxy TicketWrapper dziedziczy z Contact i SidebarTicket. Wygląda to jak bzdura i zastanawiam się, że nie widzisz błędu. TypeError: Proxy model 'TicketWrapper' has more than one non-abstract model base class. Ten sam kontakt może być udostępniany przez więcej biletów. (Czy nie jest to zarejestrowany użytkownik? Czy nie może zmienić niczego w swoim profilu użytkownika w historii problemu?) Powinien być prawdopodobnie obcym kluczem do Kontaktu, a nie wielokrotnym dziedziczeniem.

+0

"Kontakt" jest abstrakcyjnym modelem Django w tym przypadku, więc wszystko w porządku. Ustawiłem 'managed = False' dla modeli' kayakodb'. Ale kiedy próbuję utworzyć migrację w projekcie "WOW", teraz próbuje utworzyć migrację dla wszystkich modeli 'kayakodb' (nawet jeśli są one niezarządzane). Podczas konfigurowania routera tak, aby nie zezwalał na migracje działającego modelu 'kayakodb', ale oznacza to również, że migracja modelu proxy nie jest tworzona, co jest wymagane (AFAIA). Wciąż mam problem, że nie mogę utworzyć modelu proxy (lub migracji dla jednego) dla modeli określonych w kayakodb. – Bono