« Les clés étrangères » bases de données à travers très distinctes dans Django
-
08-10-2019 - |
Question
J'ai écrit un site Django qui utilise deux bases de données différentes. L'un est local, nous allons appeler de lui, « Django », base de données qui stocke toutes les tables standards d'une jolie installation standard - auth, les sites, les commentaires, etc. -. Et quelques tables supplémentaires
La plupart des données, y compris les utilisateurs, vient d'une base de données sur un autre serveur, que l'appel de la base de données, il « Legacy ».
Je cherche des suggestions sur les moyens propres, pythonique de relier les deux bases de données, notamment en ce qui concerne les utilisateurs.
J'utilise un modèle proxy, ce qui fonctionne très bien quand je peux l'utiliser de façon explicite, mais que je rencontrerai des problèmes quand j'accéder à l'objet utilisateur comme un objet lié (par exemple, lorsque vous utilisez le django commentaires intégrés système).
Voici ce que l'apparence de code comme:
models.py : (pointe vers la base de données 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 : (points à la base de données "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'
Je l'ai aussi fouetté mon propre middleware, donc request.user
est l'objet proxy User
également.
Le vrai problème est quand j'utilise quelque chose qui a l'utilisateur comme un objet lié, en particulier dans un modèle où j'ai encore moins de contrôle.
Dans le modèle:
{{ 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 %}
Court de créer une version enveloppée du système de commentaires qui utilise mon proxy modèle de l'utilisateur à la place, il n'y a rien que je puisse faire?
La solution
Voici comment je l'a résolu. Je me suis arrêté en utilisant le proxy de l'utilisateur tout à fait.
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
et 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'
Enfin, settings.py :
AUTH_PROFILE_MODULE = 'myauth.LegacyPerson'
Ceci est une solution plus simple, mais au moins ça marche! Cela signifie que chaque fois que je veux que le dossier de l'héritage que je dois appeler user_profile
, et cela signifie qu'il ya une requête supplémentaire pour chaque enregistrement de l'utilisateur, mais c'est un compromis équitable parce qu'en fait, il est peu probable que je vais faire un contrôle qui lui souvent.