“Chiavi esterne” banche dati in tutto molto separati in Django
-
08-10-2019 - |
Domanda
Ho scrivere un sito Django che utilizza due database diversi. Uno è il locale, chiamiamolo, "Django", database che memorizza tutte le tabelle standard da uno standard abbastanza installazione - autenticazione, siti, commenti, ecc. - più alcuni tavoli extra
La maggior parte dei dati, compresi gli utenti, proviene da un database su un altro server, chiamiamolo il database "Legacy".
sto cercando suggerimenti su pulito, modi divinatorio per collegare le due basi di dati, in particolare per quanto riguarda gli utenti.
Io sto usando un modello di delega, che funziona alla grande quando posso esplicitamente usarlo, ma io incorrere in problemi quando sto accedere all'oggetto utente come un oggetto correlato (ad esempio, quando si utilizza il built-in Django commenti sistema).
Ecco cosa gli sguardi codice come:
models.py : (punta al database di Django)
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User as AuthUser, UserManager as AuthUserManager, AnonymousUser as AuthAnonymousUser
class UserPerson(models.Model):
user = models.OneToOneField(AuthUser, related_name="person")
person_id = models.PositiveIntegerField(verbose_name='Legacy ID')
def __unicode__(self):
return "%s" % self.get_person()
def get_person(self):
if not hasattr(self, '_person'):
from legacy_models import Person
from utils import get_person_model
Person = get_person_model() or Person
self._person = Person.objects.get(pk=self.person_id)
return self._person
person=property(get_person)
class UserManager(AuthUserManager):
def get_for_id(self, id):
return self.get(person__person_id=id)
def get_for_email(self, email):
try:
person = Person.objects.get(email=email)
return self.get_for_id(person.pk)
except Person.DoesNotExist:
return User.DoesNotExist
def create_user(self, username, email, password=None, *args, **kwargs):
user = super(UserManager,self).create_user(username, email, password, *args, **kwargs)
try:
person_id = Person.objects.get(email=email).pk
userperson, created = UserPerson.objects.get_or_create(user=user, person_id=person_id)
except Person.DoesNotExist:
pass
return user
class AnonymousUser(AuthAnonymousUser):
class Meta:
proxy = True
class User(AuthUser):
class Meta:
proxy=True
def get_profile(self):
"""
Returns the Person record from the legacy database
"""
if not hasattr(self, '_profile_cache'):
self._profile_cache = UserPerson.objects.get(user=self).person
return self._profile_cache
objects = UserManager()
legacy_models.py : (punta al database di "Legacy")
class Person(models.Model):
id = models.AutoField(primary_key=True, db_column='PeopleID') # Field name made lowercase.
code = models.CharField(max_length=40, blank=True, db_column="person_code", unique=True)
first_name = models.CharField(max_length=50, db_column='firstName', blank=True) # Field name made lowercase.
last_name = models.CharField(max_length=50, db_column='lastName', blank=True) # Field name made lowercase.
email = models.CharField(max_length=255, blank=True)
def __unicode__(self):
return "%s %s" % (self.first_name, self.last_name)
def get_user(self):
from models import User
if not hasattr(self,'_user'):
self._user = User.objects.get_for_id(self.pk)
return self._user
user = property(get_user)
class Meta:
db_table = u'People'
Ho anche montata una mia middleware, in modo request.user
è l'oggetto User
delega anche.
Il vero problema è quando sto usando qualcosa che ha utente come un oggetto correlato, in particolare in un modello in cui ho ancora meno controllo.
Nel modello:
{{ request.user.get_profile }}
{# this works and returns the related Person object for the user #}
{% for comment in comments %} {# retrieved using the built-in comments app %}
{{ comment.user.get_profile }}
{# this throws an error because AUTH_PROFILE_MODULE is not defined by design #}
{% endfor %}
A corto di creare una versione avvolto del sistema di commenti, che usa il mio delega modello User, invece, c'è qualcos'altro che posso fare?
Soluzione
Ecco come ho risolto esso. Ho smesso di usare il proxy per l'utente del tutto.
models.py :
from django.db import models
from legacy_models import Person
from django.contrib.auth.models import User
class UserPerson(models.Model):
user = models.OneToOneField(User, related_name="person")
person_id = models.PositiveIntegerField(verbose_name='PeopleID', help_text='ID in the Legacy Login system.')
def __unicode__(self):
return "%s" % self.get_person()
def get_person(self):
if not hasattr(self, '_person'):
self._person = Person.objects.get(pk=self.person_id)
return self._person
person=property(get_person)
class LegacyPersonQuerySet(models.query.QuerySet):
def get(self, *args, **kwargs):
person_id = UserPerson.objects.get(*args, **kwargs).person_id
return LegacyPerson.objects.get(pk=person_id)
class LegacyPersonManager(models.Manager):
def get_query_set(self, *args, **kwargs):
return LegacyPersonQuerySet(*args, **kwargs)
class LegacyPerson(Person):
objects = LegacyPersonManager()
class Meta:
proxy=True
e legacy_models.py :
class Person(models.Model):
id = models.AutoField(primary_key=True, db_column='PeopleID') # Field name made lowercase.
code = models.CharField(max_length=40, blank=True, db_column="person_code", unique=True)
first_name = models.CharField(max_length=50, db_column='firstName', blank=True) # Field name made lowercase.
last_name = models.CharField(max_length=50, db_column='lastName', blank=True) # Field name made lowercase.
email = models.CharField(max_length=255, blank=True)
def __unicode__(self):
return "%s %s" % (self.first_name, self.last_name)
def get_user(self):
from models import User
if not hasattr(self,'_user'):
self._user = User.objects.get_for_id(self.pk)
return self._user
def set_user(self, user=None):
self._user=user
user = property(get_user, set_user)
class Meta:
db_table = u'People'
Infine, in settings.py :
AUTH_PROFILE_MODULE = 'myauth.LegacyPerson'
Si tratta di una soluzione più semplice, ma almeno funziona! Significa che ogni volta che voglio il record legacy devo chiamare user_profile
, e significa che c'è una query aggiuntiva per ogni record utente, ma questa è una fiera-off, perché in realtà non è molto probabile che farò un controllo incrociato che spesso.