2010-12-19 10 views
4

Ich habe erfolgreich meine Anwendung läuft über mehrere Datenbanken mit dem Routing-Schema basierend auf Modellen. I.e. Modell A lebt von DB A und Modell B lebt von DB B. Ich muss jetzt meine Daten zerlegen. Ich schaue mir die Dokumente an und habe Schwierigkeiten, herauszufinden, wie es funktioniert, da das gleiche Modell auf mehreren Datenbankservern vorhanden sein muss. Ich will eine Flagge haben, um zu sagen, DB für NEUE Mitglieder ist jetzt Datenbank X und die Mitglieder X-Y leben auf Datenbank N etc.Django Daten sharding

Wie mache ich das? Verwendet es ** Hinweise, das scheint mir unzureichend dokumentiert zu sein.

+0

interessanter Link: http://groups.google.com/group/django-developers/msg/078099f199bdfb79?pli=1 – SingleNegationElimination

+0

Hallo Ich bin mir nicht ganz sicher, was Sie vorhaben. Könnten Sie bitte bestimmte Codebeispiele geben? – MiniQuark

Antwort

2

using sollten Sie angeben können, welche Datenbank Sie verwenden möchten.

Unterklasse der Create-Methode könnte erreichen, was Sie tun möchten.

class CustomManager(models.Manager) 
    def get_query_set(self): 
     return super(CustomManager, self).get_query_set() 

    def create(self, *args, **kwargs): 
     return super(CustomManager, self).using('OTHER_DB').create(*args, **kwargs) 

class ModelA(models.Model): 
    objects = CustomManager() 

Ich habe nicht getestet, damit ich weiß nicht, ob Sie einen tack können ‚create‘ auf das Ende eines ‚mit‘

6

Die hints Parameter entworfen, um Ihre Datenbank-Router, um zu entscheiden, wo es sollte seine Daten lesen oder schreiben. Es kann sich mit zukünftigen Versionen von Python entwickeln, aber für den Moment gibt es nur eine Art von Hinweis, die vom Django-Framework gegeben werden kann, und das ist die instance, an der es arbeitet.

schrieb ich diese sehr einfache Datenbank-Router zu sehen, was Django tut:

# routers.py 
import logging 
logger = logging.getLogger("my_project") 

class DebugRouter(object): 
    """A debugging router""" 

    def db_for_read(self, model, **hints): 
     logger.debug("db_for_read %s" % repr((model, hints))) 
     return None 

    def db_for_write(self, model, **hints): 
     logger.debug("db_for_write %s" % repr((model, hints))) 
     return None 

    def allow_relation(self, obj1, obj2, **hints): 
     logger.debug("allow_relation %s" % repr((obj1, obj2, hints))) 
     return None 

    def allow_syncdb(self, db, model): 
     logger.debug("allow_syncdb %s" % repr((db, model))) 
     return None 

du erklären in settings.py:

DATABASE_ROUTERS = ["my_project.routers.DebugRouter"] 

sicher Ausgang Debug-Ausgabe richtig konfiguriert ist (zum Beispiel Make-Protokollierung zu stderr):

LOGGING = { 
    'version': 1, 
    'disable_existing_loggers': False, 
    'handlers': { 
     [...some other handlers...] 
     'stderr': { 
      'level': 'DEBUG', 
      'class': 'logging.StreamHandler' 
     } 
    }, 
    'loggers': { 
     [...some other loggers...] 
     'my_project': { 
      'handlers': ['stderr'], 
      'level': 'DEBUG', 
      'propagate': True, 
     }, 
    } 
} 

Dann können Sie ein Django s öffnen Hölle und testen ein paar Anfragen, um zu sehen, welche Daten Ihr Router gegeben wird:

$ ./manage.py shell 
[...] 
>>> from my_project.my_app.models import User 
>>> User.objects.get(pk = 1234) 
db_for_read (<class 'my_project.my_app.models.User'>, {}) 
<User: User object> 
>>> user = User.objects.create(name = "Arthur", title = "King") 
db_for_write (<class 'my_project.my_app.models.User'>, {}) 
>>> user.name = "Kong" 
>>> user.save() 
db_for_write (<class 'my_project.my_app.models.User'>, {'instance': 
       <User: User object>}) 
>>> 

Wie Sie sehen können, die hints immer leer ist, wenn keine Instanz verfügbar ist (im Speicher) noch. Daher können Sie Router nicht verwenden, wenn Sie Abfrageparameter (z. B. die ID des Objekts) benötigen, um zu ermitteln, welche Datenbank abgefragt werden soll. Es könnte in Zukunft möglich sein, wenn Django die Abfrage- oder Queryset-Objekte in der dict bereitstellt.

Um Ihre Frage zu beantworten, würde ich sagen, dass Sie jetzt einen benutzerdefinierten Manager erstellen müssen, wie von Aaron Merriam vorgeschlagen. Das Überschreiben der create-Methode reicht jedoch nicht aus, da Sie auch ein Objekt in der entsprechenden Datenbank abrufen müssen. So etwas wie dies funktionieren könnte (noch nicht geprüft):

class CustomManager(models.Manager) 
    def self.find_database_alias(self, pk): 
     return #... implement the logic to determine the shard from the pk 

    def self.new_object_database_alias(self): 
     return #... database alias for a new object 

    def get(self, *args, **kargs): 
     pk = kargs.get("pk") 
     if pk is None: 
      raise Exception("Sharded table: you must provide the primary key") 
     db_alias = self.find_database_alias(pk) 
     qs = self.get_query_set().using(db_alias) 
     return qs.get(*args, **kargs) 

    def create(self, *args, **kwargs): 
     db_alias = self.new_object_database_alias() 
     qs = super(CustomManager, self).using(db_alias) 
     return qs.create(*args, **kwargs) 

class ModelA(models.Model): 
    objects = CustomManager() 

Prost