“外键"跨 Django 中非常独立的数据库 [英] "Foreign Keys" across very separate databases in Django

查看:76
本文介绍了“外键"跨 Django 中非常独立的数据库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个使用两个不同数据库的 Django 站点.一个是本地数据库,我们称之为Django",该数据库存储来自非常标准的安装的所有标准表——身份验证、站点、评论等——以及一些额外的表.

I've writing a Django site that uses two different databases. One is the local, let's call it, "Django", database that stores all of the standard tables from a pretty standard install -- auth, sites, comments, etc. -- plus a few extra tables.

包括用户在内的大部分数据都来自另一台服务器上的数据库,我们称之为传统"数据库.

Most of the data, including users, comes from a database on another server, let's call it the "Legacy" database.

我正在寻找有关连接两个数据库的干净、pythonic 方式的建议,特别是关于用户的建议.

I'm looking for suggestions on clean, pythonic ways of connecting the two databases, particularly in regards to users.

我正在使用代理模型,当我可以显式使用它时效果很好,但是当我将用户对象作为相关对象访问时遇到了问题(例如,当使用内置的 django 注释时)系统).

I'm using a proxy model, which works great when I can explicitly use it, but I run into problems when I'm accessing the user object as a related object (for example, when using the built-in django comments system).

代码如下:

models.py:(指向Django数据库)

models.py: (points to the Django database)

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"数据库)

legacy_models.py: (points to the "Legacy" database)

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 对象.

I've also whipped up my own middleware, so request.user is the proxy User object also.

真正的问题是当我使用将用户作为相关对象的东西时,尤其是在我控制更少的模板中.

The real problem is when I'm using something that has user as a related object, particularly in a template where I have even less control.

在模板中:

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

除了创建使用我的代理用户模型的评论系统的包装版本之外,还有什么我可以做的吗?

Short of creating a wrapped version of the comments system which uses my proxy User model instead, is there anything else I can do?

推荐答案

这是我的解决方法.我完全停止使用用户代理.

Here's how I resolved it. I stopped using the User proxy altogether.

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,这意味着每个用户记录都有一个额外的查询,但这是一个公平的权衡,因为实际上它不是我很可能会经常进行交叉检查.

This is a simpler solution, but at least it works! It does mean that whenever I want the legacy record I have to call user_profile, and it means that there's an additional query for each user record, but this is a fair trade-off because actually it isn't very likely that I will be doing a cross check that often.

这篇关于“外键"跨 Django 中非常独立的数据库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆