【问题标题】:Django 1.7 multisite User modelDjango 1.7 多站点用户模型
【发布时间】:2015-01-09 23:58:30
【问题描述】:

我想为一个 Django 应用程序提供服务,该应用程序通过单个数据库但不同的用户集为多个网站提供服务。像博客应用程序一样思考,它将被具有不同主题的多个域使用,但通过向模型添加站点字段来使用相同的数据库。

我使用Django's SitesFramework 来完成这项工作。但问题是,我无法为不同站点分离用户模型。我想使用具有每个站点唯一的站点字段和电子邮件字段的相同用户模型。

我试图像这样扩展AbstractUser 模型:

from django.contrib.auth.models import AbstractUser
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager

class Member(AbstractUser):
    username = None
    site = models.ForeignKey(Site)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    on_site = CurrentSiteManager()

    class Meta:
        unique_together = ('site', 'email')

但是给出了那个错误:'Member.email' must be unique because it is named as the 'USERNAME_FIELD'.

解决该问题的最佳做法是什么?

【问题讨论】:

    标签: python django django-models django-authentication django-sites


    【解决方案1】:

    我希望这种方法对您有所帮助:

    1) 保存前编写用户名:

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    from django.contrib.sites.models import Site
    from django.contrib.sites.managers import CurrentSiteManager
    
    class Member(AbstractUser):
        site = models.ForeignKey(Site)
        on_site = CurrentSiteManager()
    
        USERNAME_FIELD = 'username'
        REQUIRED_FIELDS = []
    
        class Meta:
            unique_together = ('site', 'email')
    
    from django.db.models.signals import pre_save
    from django.dispatch import receiver
    
    @receiver(pre_save, sender=Member)
    def compose_username(sender, instance, **kwargs):
        instance.username = "{0}__{1}".format( instance.email, instance.site_id ) 
    

    2) 然后在您的自定义身份验证后端覆盖ModelBackend

    from django.contrib.auth.backends import ModelBackend
    from django.contrib.auth import get_user_model
    
    class MyModelBackend(ModelBackend):
    
        def authenticate(self, username=None, password=None, **kwargs):
            UserModel = get_user_model()
            site = kwargs.get('site')
            identifier = "{0}__{1}".format( username, site )
            try:
                user = UserModel.objects.get(username=identifier)
                if user.check_password(password):
                    return user
            except UserModel.DoesNotExist:
                # Run the default password hasher once to reduce the timing
                # difference between an existing and a non-existing user (#20760).
                UserModel().set_password(password)
    

    3) 请记住在设置中设置您的自定义后端:

    AUTH_USER_MODEL='s1.Member'
    SITE_ID = 1
    AUTHENTICATION_BACKENDS = ( 'MyApp.MyModule.MyModelBackend',)
    

    4) 验证时包含站点:

    >>> from s1.models import Member as M
    >>> m1 = M()
    >>> m1.site_id = 1
    >>> m1.email = 'peter@hello.com'
    >>> m1.save()
    >>> m1.set_password('hi')
    >>> m1.save()
    >>> 
    >>> from django.contrib.auth import authenticate, login
    >>> u=authenticate(username='peter@hello.com', password='hi', site=1)
    >>> u
    <Member: peter@hello.com_at_1>
    >>> 
    

    【讨论】:

    【解决方案2】:

    如果您想将电子邮件保留为 USERNAME_FIELD,根据用户模型中的定义,它必须始终是唯一的,您将无法为每个站点重复它。

    我能想到的方法可能不止一种,但我想我会做以下事情:

    • 首先,我不会扩展 AbstractUser 模型并创建一个 对站点的 OneToOne 依赖项。因为实际上允许用户属于 到多个站点。所以在这里,最好的选择 imo 是创建一个 具有用户外键和站点字段的成员模型并制作 那些unique_together。因此,每个站点只有一个成员,并且用户保持唯一。这也是在现实生活中更好地代表案例的原因。

    • 现在,在为站点注册新用户时,只需先检查用户(电子邮件地址)是否已存在,如果存在,只需为该用户分配一个新成员。如果没有,也创建一个新用户。

    首先编辑问题,“如果用户想用不同的用户名、密码或电子邮件注册到另一个网站怎么办?”

    如果根据我的 cmets,可以共享站点的用户帐户(当然用户知道这一点)在注册过程中,如果用户已经存在给定电子邮件,那么他可以得知,由于站点-a 已经存在该地址的帐户,因此该用户帐户将被分配给站点-b 的成员资格。然后,可以发送一封带有验证链接的电子邮件,确认后,将创建新成员并将其分配给有效用户。

    另一种方法

    如果我的假设是错误的,可以甚至希望在站点之间共享用户,那么我想这里需要一种全新的方法:

    按照您的做法扩展 AbstractUser,但不要将电子邮件用作 USERNAME_FIELD,而是使用由 &lt;email&gt;_&lt;site_id&gt; 组成的新字段(这将始终是唯一的,因为这两个字段是唯一的)...该字段可以是叫unique_site_id左右。该字段可以在提交登录和登录表单后填充。

    【讨论】:

    • 如果访问者使用相同的电子邮件和用户名注册但密码不同怎么办?还是相同的电子邮件地址但不同的用户名?还是相同的用户名但不同的电子邮件地址?
    • 好吧,如果电子邮件是正确的,它是同一个用户,所以用户不必创建一个新帐户。 ... 如果用户的个人信息不一样,可能会询问用户是否要更新他的数据? ...或者用户应该“认为”他正在为每个站点创建一个新的/单独的帐户? ...我在想它可能更像 stackoverflow/-exchange 站点...一个登录/配置文件用于多个相关站点。
    • ...也许我误解了在站点之间共享数据库的意义。我认为这些网站在某种程度上是相关的,并且会在它们之间共享一些数据,包括用户。
    • 我的项目不像stackoverflow。它是一个新闻网站平台,使用许多不同且不相关的新闻网站。所有网站都使用相同的数据库,但从访问者的角度来看,用户对这些网站的关系一无所知。您标题为“另一种方法”的方法似乎是一个很好的解决方法。我试试看。
    猜你喜欢
    • 1970-01-01
    • 2016-05-13
    • 2012-11-05
    • 2011-12-15
    • 2014-12-22
    • 1970-01-01
    • 2019-06-07
    • 2018-05-28
    • 2015-08-20
    相关资源
    最近更新 更多