问题 在Django 1.5 / 1.6中设置两种不同类型的用户


请注意 - 这是更新版本 我原来的问题 关于这个问题,但值得再次询问Django如何处理用户和身份验证。

我正在一个有两种不同用户的网站上工作 - 让我们打电话给他们 Customers 和 Store Owners。两者都在网站上注册,但功能却截然不同。 Customers 只需一个个人资料,就可以在他们喜欢的商店中购物。 Store Owners 拥有一个帐户,但可以访问多个商店,每个商店可以有多个 Store Owners

模型的确切细节无关紧要,但这两类用户需要非常不同的字段。理想情况下,模型看起来像这样:

Customer
  email (username)
  password
  name
  address
  time_zone
  preferred_shipping
  favorite_stores (many-to-many field)
  ...

Store Owner
  email (username)
  password
  name
  balance
  stores_owned (many-to-many field on Stores)
  stores_managed (many-to-many field on Stores)
  ...

最初,当Django的自定义用户支持不佳时,我有一个 UserProfile 带有一些附加字段的类 OneToOne 上 User,然后另外 Customer 和 StoreOwner 那些课程 OneToOne 上 UserProfile。这不是很好。

鉴于Django 1.5 / 1.6的变化,我试图想出最好的结构方法。现在,我有以下内容:

class CustomerUser(AbstractBaseUser):
    ...

class StoreOwnerUser(AbstractBaseUser):
    ...

但因为会有两种类型的用户,我无法设置 AUTH_USER_MODEL 只有其中一个。

构造这个的最佳方法是什么,以便我可以有两个不同类型的用户使用不同的字段,而不会在用户身份验证,用户创建或管理员方面造成任何问题?

此外,我如何能够从登录单独判断此用户是否是 CustomerUser 或者a StoreOwnerUser


11134
2017-12-26 04:20


起源

您可能想要创建一个基本用户,添加一个布尔标志,表示用户是否是客户或商店所有者,并使用一对一关系附加另一个作为配置文件的模型。 - user1876508
自问这个问题差不多一年了,但我发现自己处于类似情况。问题:你最终采取了什么方法?另外,您是否考虑过商店所有者可能想要注册为客户(使用相同的电子邮件地址)的情况?您的设计如何迎合这种情况?最后,您是否考虑过为商店所有者和客户提供单独的登录视图(这有助于轻松区分登录用户类型)? - Hari Mahadevan


答案:


您的用户类型似乎有一些常见功能和不常见的功能。如果您的用户类型中存在Django的默认用户模型不支持开箱即用的常见功能,则应直接对其进行子类化。

为用户类型添加额外的,不常见的功能最好不要通过子类化,而是使用配置文件。我的理由是因为您对这些用户类型的身份验证没有根本改变,但有关用户的详细信息取决于用户的类型。为了适应这种情况,您可以创建一个包含这些详细信息的单独模型,并将您的User类引用为OneToOne / ForeignKey关系(取决于您的设计)。

您可以对用户创建过程进行修改,以确定应该是哪种用户类型,并将其关联的OneToOneField / ForeignKey(取决于您的设计)设置为适当的客户类型模型。

通过这种方式,您应该只有一个AUTH_USER_MODEL,并且您应该能够处理不同客户类型的详细信息。


5
2017-12-26 04:29



谢谢 - 这是有道理的。但是我如何从登录中知道登录的用户是否是 StoreOwner 要么 Customer?我会在基地吗? UserProfile 一级 storeowner_or_customer 领域,或类似的东西? - jdotjdot
这样的事情。 UserProfile必须对用户可用,并且它是您每次创建用户时设置的内容。我建议您将两个配置文件模型作为您的两种用户类型(StoreOwner / Customer),然后您可以在User模型中添加“type”字段,两个用户类型模型通过OneToOne关系引用该字段。在您的代码中,首先检查类型(即user_obj.type ==“Customer”或类似的东西),如果为true,则可以直接访问相关详细信息user_obj.customer.address。 - tsurantino


构建这个的最佳方法是什么,这样我才能拥有两个   不同类型的用户使用不同的字段,而不会导致任何问题   用户身份验证,用户创建或管理员的问题?

实际上你只有一种类型的用户。只是某些用户设置了特定属性而其他用户没有。考虑一下django如何拥有“用户”和“管理员”。他们就是这样的例子 同一型号,但具有不同的属性和权限。

你应该类似地接近它。为整个应用程序提供一个用户模型。您可以在自定义用户类中设置属性/方法,以标识此用户设置的标志(这将决定用户的“类型”)。

此外,我将如何从登录单独告知此用户是否是   一个 CustomerUser 或者a StoreOwnerUser

你可以使用 user_passes_test decorator,它接受一个函数名称的参数,并且只在函数返回真值时才处理视图。


3
2017-12-26 04:38



我非常喜欢这个想法,但最终我不会在模型中有大量未使用的字段吗?是否存在影响,特别是对于表单生成? - jdotjdot
没有, 特别是如果你正确使用模型的反向关系。例如,您不需要 stores_owned 和 stores_managed 字段,因为您可以使用ORM获取此信息。例如,你可以有一个方法 User 那会回来的 self.stores_set.count() 为“商店管理”;如果您要在管理员中将其显示为该模型列表视图的一部分,那么您将在模型中使用该方法,否则它将是某个位于缓存中的值。 - Burhan Khalid


  1. 创建一个扩展Django的抽象基础用户的BaseUser
  2. 创建两个名为CustomerUser和StoreOwnerUser的子类,它们扩展了BaseUser

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    
    class BaseUser(AbstractUser):
        # all the common fields go here, for example:
        email = models.EmailField(max_length=10,unique=True)
        name = models.CharField(max_length=120)
    
    class StoreOwnerUser(BaseUser):
        # All Store Owner specific attribute goes here
        balance = models.some_balance_field()
        stores_owned = models.some_stores_owned_field()
    
        class Meta:
        verbose_name = 'Store Owner'
    
    class CustomerUser(BaseUser):
        # All Customer specific attribute goes here
        customer_id = models.CharField(max_length=30, unique=True)
        address =  models.some_address
        time_zone = models.something...
        ...
    
        class Meta:
            verbose_name = 'Customer'
    

1
2017-08-07 08:30



什么将被指定为 AUTH_USER_MODEL?在登录时也是: user = authenticate(username = username, password = password)。从哪个表(CustomerUser或StoreOwnerUser) authenticate 功能检查从? - Sibtain
你可以做这个实现 stackoverflow.com/questions/30495979/... - Saber Solooki