«Внешние ключи» в очень отдельных базах данных в Django - PullRequest
4 голосов
/ 08 января 2011

Я пишу сайт Django, который использует две разные базы данных.Одна из них - локальная, назовем ее «Django», база данных, в которой хранятся все стандартные таблицы из довольно стандартной установки - аутентификация, сайты, комментарии и т. Д. - плюс несколько дополнительных таблиц.

Большая часть данных, включая пользователей, поступает из базы данных на другом сервере, назовем ее базой данных «Legacy».

Я ищу предложения по чистым, питонским способам соединения двух баз данных, особенно в отношении пользователей.

Я использую модель прокси, которая прекрасно работает, когда я могу использовать ее явно, но у меня возникают проблемы, когда я обращаюсь к объекту пользователя как связанному объекту (например, при использовании встроенногов системе комментариев django).

Вот как выглядит код:

models.py : (указывает на базу данных 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 : (указывает на базу данных "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'

Я также запустил свое промежуточное ПО, поэтому request.user является прокси User объектомтакже.

Реальная проблема заключается в том, что я использую что-то, для чего пользователь является связанным объектом, особенно в шаблоне, где у меня еще меньше контроля.

В шаблоне:

{{ 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 %}

Если не считать обернутой версии системы комментариев, которая вместо этого использует мою модель прокси-пользователя, могу ли я что-нибудь еще сделать?

1 Ответ

3 голосов
/ 10 января 2011

Вот как я это решил. Я полностью прекратил использование прокси-сервера пользователя.

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

и 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'

Наконец, в settings.py :

AUTH_PROFILE_MODULE = 'myauth.LegacyPerson'

Это более простое решение, но, по крайней мере, оно работает! Это означает, что всякий раз, когда я хочу унаследованную запись, я должен вызывать user_profile, и это означает, что для каждой записи пользователя есть дополнительный запрос, но это справедливый компромисс, потому что на самом деле маловероятно, что я буду часто делайте перекрестные проверки.

...