2017-01-11 4 views
12

Situation

Django Proxy-Modell auf andere Datenbank


Wir haben ein paar verschiedene Anwendungen, die Tickets von einem Ticket-Support-System für verschiedene Arten von Funktionalität verwenden.

Zunächst haben wir eine Anwendung, die ein paar Modelle, die die Modelle unseres Ticket-Support-System Kayako darstellen. Diese Anwendung sollte nichts über andere Anwendungen wissen, die sie verwenden, und sollte so allgemein wie möglich bleiben. Da diese Anwendung vorhandene Tabellen von Kayako verwendet, läuft sie auf derselben Datenbank. Nennen wir diese Anwendung kayakodb.

Eine Anwendung verbindet Kunden aus unserer Kundendatenbank mit Tickets im Ticket-Support-System. Zuvor verfügte dieses System über eine eigene Darstellung der Tickets in unserem Ticket-Support-System, indem es mithilfe einer von kayakodb bereitgestellten API nach Tickets suchte. Es verwendete dann diese Darstellung von Tickets, um Kunden und Domains zu verknüpfen. Dies war jedoch zu komplex und nicht sehr logisch. Daher haben wir uns dafür entschieden, das Modell auf ein Proxy-Modell umzustellen und die Modelle, die die Links zu Kunden und Domains darstellen, auf kayakodb zu verschieben. Nennen wir diese Anwendung sidebar.

Eine weitere, neue Anwendung, zeigt die Tickets aus dem Ticket-Support-System in einer klaren Übersicht neben Anrufen, so dass unsere Support-Abteilung leicht sehen kann, welche Anrufe und Tickets zu welchen Kunden gehören. Dieses System verfügt über ein Proxy-Modell für das Proxy-Modell sidebar, da einige der Funktionen, die vom sidebar-Modell bereitgestellt werden, auch für diese Anwendung neben einigen anderen, die das neue Proxy-Modell deklariert, erforderlich sind. Nennen wir dieses Projekt WOW.

Die Anwendungen sidebar und WOW sind beide Teil desselben Projekts/Repositorys. Wir nennen dieses Repository Coneybeach, das eine eigene Datenbank hat. kayakodb ist jedoch ein völlig unabhängiges Projekt. Es ist in Coneybeach über eine Anforderungsdatei enthalten, die wir über pip installieren.

Problem


Wenn Migrationen für die neue Einrichtung zu schaffen Django einen Proxy-Modell Migration für die installierte kayakodb was natürlich ist erstellt, ein no go. Jedes Mal, wenn wir eine neue Version von kayakodb installieren würden, würde diese Migration überschrieben. Ganz zu schweigen von der Tatsache, dass kayakodb nichts darüber wissen sollte, welche Modelle davon Gebrauch machen.

-Code


Das Ticket Modell innerhalb 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 

Der SidebarTicket Proxy-Modell innerhalb 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 

The Contact Klasse TicketWrapper erbt von (wie von Hynekc angefordert er).Dieses Modell wird als Basismodell verwendet für TicketWrapper und ein anderes Modell Anrufe darstellt (obwohl gibt es keine Probleme mit diesem Modell soweit mir bekannt):

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) 

Der TicketWrapper Proxy-Modell innerhalb 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 

Was haben versucht, ich


  • ich habe versucht, nicht th Angabe e app_label für beide Proxy-Modelle. Dadurch werden zwar korrekte Migrationen erstellt, die Proxymodelle werden jedoch nach dem Modell kayakodb.Ticket in der Coneybeach-Datenbank gesucht.
  • Ich habe versucht, abstract = True für die Unterklassen zu spezifizieren, war aber nicht sicher, dass dies der war, weil ich noch in der Lage sein möchte, den Manager für die Modelle zu verwenden.
  • Ich überlegte, die Migration, die derzeit erstellt wird, auf das eigentliche kayakodb Projekt zu verschieben, aber ich denke nicht, dass dies eine gute Lösung ist. kayakodb sollte nichts über die Implementierungen seiner Modelle wissen oder wo sie verwendet werden.
  • ./manage.py check gibt 0 Probleme zurück.

Frage


Wie kann ich einen Proxy-Modell für ein Modell erstellen, die in einer anderen Datenbank oder das Projekt sich befindet?

bearbeiten


Nach dem kayakodb.Ticket Modell versucht das WOW Projekt nicht verwaltet werden Einstellung in kayakodb eine Migration für alle Modelle zu erstellen. Ergebnis:

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 Ja, das weiß ich. Das ist nett und alles, aber wenn es von Bedeutung ist, wenn es überschrieben wird. – Bono

+0

Sie sagen es selbst "Das liegt daran, dass das Proxy-Modell möglicherweise von einem anderen Modell in einer zukünftigen Migration verwendet wird." Warum würde es sonst stören, eine Migration zu erstellen, wenn sie danach einfach weggeworfen werden kann? – Bono

+0

Du hast mich dort !! Zurückziehen enger Abstimmung. – e4c5

Antwort

4

Wie @hynekcer sagte, wenn kayakodb ist eine vorhandene Datenbank, müssen Sie managed = False für alle seine Modelle festlegen. Allerdings bleibt das Problem der Migration für das Proxy-Modell, das in der falschen App erstellt wurde (kayakodb).

Das Hacky Update, das die app_label des Proxy-Modells änderte funktionieren kann, was auch immer app ist in Ordnung, um die Migration (sidebar in diesem Fall) zu setzen, und einen Router zu machen, das dieses Proxy-Modell wird darauf zum Lesen und Schreiben von kayakodb.

z.das Proxy-Modell:

# in sidebar/models.py 

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

und der Router innerhalb des Projektes, die es verwendet:

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

Es ist nützlich, aber die Bedingung 'wenn Model == SidebarTicket:' ist immer noch ungültig, weil SidebarTicket nur ein Proxy ist, aber das Datenbankmodell ist noch nicht genug beschrieben und die korrekte Bedingung ist daher nicht bekannt. – hynekcer

+1

@hynekcer wie gesagt, wie genau der Vergleich ist nicht wichtig, betrachten Sie die oben genannten halben Pseudocode. Der DB zum Lesen und Schreiben kann sogar im Modell selbst gespeichert werden, so dass die Router-Methoden 'getattr (Modell, _CUSTOM_DATABASE ', None)' zurückgeben können, was kein Problem ist, da das Modell in den Router weitergeleitet wird Methoden. –

+0

aktualisiert das Router-Code-Snippet mit besseren if-Klauseln und die 'allow_relation'-Methode, die fehlte –

4

tl; dr aber ich grep Ihre Frage nach einem Wort router, die nicht erwähnt wird, so würde ich denke, die Sache, die Sie suchen, sind Database Routers

+0

Wenn die Frage lautet "Wie kann ich ein Proxy-Modell für ein Modell erstellen, das sich in einer anderen Datenbank oder in einem anderen Projekt befindet", lautet die Antwort möglicherweise "Datenbankrouter verwenden". Tut mir leid, dass ich versucht habe zu helfen. – yedpodtrzitko

+0

Sorry wollte nicht unhöflich sein. Ich entschuldige mich. Das Schreiben in das Modell in der anderen Datenbank ist nicht das Problem (für mich auf jeden Fall). Das Problem ist, dass eine Migration am falschen Ort erstellt wird (und wenn Sie die Frage gelesen haben, hätten Sie das gesehen :)). Sagen Sie was, wenn Sie Ihre Frage bearbeiten, um weitere Informationen zu den DB-Routern hinzuzufügen (ein Beispiel). Ich werde meine Down-Abstimmung entfernen können. Betrachtet man nur die Frage selbst, stimme ich zu, dass dies die Antwort gewesen sein könnte. – Bono

4

tl; dr Verwenden

class Meta: 
    managed = False 

für alle Modelle in der db, die nicht kontrolliert werden sollen, von Django. (D kayakodb)


Es sollte beachtet werden, dass ein PyPI Kayako Python API ist (ohne Djago) bis zu einem gewissen Kayako app in einer anderen Sprache geschrieben.

Es ist nützlich, von Ihnen zu wissen, dass Kayako und WOW in verschiedenen Datenbanken sind, aber es ist keine fundamantale Information. Django ermöglicht z.B. Dieses eine Modell ist in zwei Datenbanken vorhanden: eine primäre und eine sekundäre db. Die wichtigsten sind Meta-Optionen, in diesem Fall die Option managed = False. Es ist für den Fall Integrating Django with a legacy database. Das bedeutet nicht nur ein sehr altes Projekt, sondern ein Projekt, das nicht in Python + Django geschrieben ist oder keine Migrationen unterstützt und keine Informationen darüber teilt, welche Migrationen noch nicht angewendet wurden. Vielleicht, wenn eine neue Version von Kayako der Datenbank neue Felder hinzufügen würde, brauchen Sie dieses Feld nicht zu lesen oder wenn Sie es zum Modell hinzufügen, ist Django nicht dafür verantwortlich, das Feld zur Datenbank hinzuzufügen, da es von Kayako aktualisiert wird.

Sie können alternativ Datenbankrouter verwenden, um keine Tabellen für die kayakodb-App (nirgendwo) zu erstellen, und auch keine Tabellen in der kajakodb-Datenbank, z. Django Benutzer und Gruppen:

Datei myrouter.py oder ähnliche

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

Datei settings.py

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

Der Vorteil ist, dass diese Migration für alle aktuellen oder zukünftigen deaktiviert Modell, aber ich empfehle, einen Text auch irgendwo zu den Kommentaren in kayakodb/models.py hinzuzufügen, um es jedem späteren Entwickler klar zu machen, weil er es kann leicht vergessen, zuerst die Router zu lesen.

Sie können eine Abhängigkeit Ihrer Projektversionen auf einer minimalen und maximalen Version der Kayako API schreiben, aber nicht in Form einer Migration.


Ihr weiteres Problem ist, dass "A proxy model must inherit from exactly one non-abstract model class....", Aber Ihr Proxy-Modell TicketWrapper erbt von Contact und SidebarTicket. Es sieht wie ein Unsinn aus und ich wundere mich, dass Sie keinen Fehler sehen TypeError: Proxy model 'TicketWrapper' has more than one non-abstract model base class. Derselbe Kontakt kann von mehr Tickets geteilt werden. (Ist das nicht ein registrierter Benutzer? Kann er während des Verlaufs der Ausgabe nichts in seinem Benutzerprofil ändern?) Es sollte wahrscheinlich ein Fremdschlüssel für Contact sein, keine Mehrfachvererbung.

+0

' Contact' ist ein abstraktes Django-Modell in diesem Fall, das ist also in Ordnung. Ich habe die 'gemanaged = False' für die 'Kayakodb'-Modelle eingestellt. Aber wenn ich versuche, eine Migration im 'WOW'-Projekt zu erstellen, versucht es jetzt, eine Migration für alle 'kajakodb'-Modelle zu erstellen (auch wenn sie nicht verwaltet werden). Wenn der Router keine Migrationen für 'kajakodb'-Modelle zulässt, funktioniert das, aber das bedeutet auch, dass die Proxy-Modell-Migration nicht erstellt wird, die ich benötige (AFAIA). Ich stoße also immer noch auf das Problem, dass ich kein Proxy-Modell (oder eine Migration für eines) für die in Kayakodb angegebenen Modelle erstellen kann. – Bono