【问题标题】:Django - Allow duplicate usernamesDjango - 允许重复的用户名
【发布时间】:2010-01-08 15:22:39
【问题描述】:

我正在 django 中开展一个项目,该项目要求在他们自己的 username 命名空间中拥有不同的用户组。

例如,我可能有多个“组织”,username 在该组织中应该是唯一的。

我知道我可以通过使用另一个包含用户名/组织 ID 的模型来做到这一点,但这仍然会在默认的 django auth User 上留下这个无用的(和必需的)字段,我必须用一些东西来填充。

我已经编写了自己的身份验证后端,用于根据 LDAP 对用户进行身份验证。但是,正如我之前提到的,我仍然被如何填充/忽略默认 django 用户上的 username 字段的问题所困扰。

有没有办法为 Django auth 用户删除 username 的唯一性约束?

【问题讨论】:

    标签: python django authentication


    【解决方案1】:

    我不确定这是否正是您想要的,但我认为您可以使用类似于 this answer 中的 hack。

    下面的代码可以工作,只要它在 Django 加载你的模型时被执行的地方。

    from django.contrib.auth.models import User
    
    User._meta.get_field('username')._unique = False
    

    请注意,如果 auth_user 表已经创建,这不会更改数据库唯一约束。因此,您需要在运行syncdb 之前进行此更改。或者,如果您不想重新创建 auth_user 表,则可以进行此更改,然后手动更改数据库表以删除约束。

    【讨论】:

    • 我不确定这是否可行。 unique 属性作为对该字段的约束传播到数据库。一旦完成,调整 Django 元信息将无济于事。如果您永远不会真正使用(即显示/修改)用户名,那么任何使它独一无二的东西就足够了。例如。 DrBloodmoney 的建议。
    • 这个确实工作(我试过了),但由于它正在访问_meta,它依赖于内部实现而不是文档化的接口,所以我很谨慎用它。 +1 不过,它确实有效。
    • 嗯,它适用于 NOT NULL 约束(我已经尝试过了),所以我不明白为什么不这样做。只要“MyUserModel”是从“auth.User”继承的类,并且该行直接放在模型之后,这似乎就可以了。只是它有点丑陋。
    • 这实际上工作得很好,无需子类化auth.User。你可以做User._meta.get_field('username').unique = False。它确实有效,只要它存在于 Django 加载模型时加载的文件中。我接受了这个答案,因为它很实用,但我仍然觉得这不是一个很好的解决方案,因为它可能会随着 Django 的更新而中断(它依赖于未记录的内部功能)。
    • 如果可行,我需要有人向我解释。有(至少)2个问题:1)该字段是_unique,而不是unique。 (至少在 1.1.1 中是这样。在 0.97pre 中是 unique。) 2) 不管名称如何,设置 _unique 字段不会改变 数据库强制约束该字段是 unique,这是在 syncdb 时间创建的约束,并且不会通过设置此字段而消失。我等待启蒙。真的,我愿意。
    【解决方案2】:

    您可以做的是扩展用户模型。对于 User 表,生成一个不会在站点中显示的用户名(例如 A_123、A_345)。

    然后创建一个扩展 User 的新模型。

    class AppUser(User):
        username = models.CharField...
        organization = models.CharField...
    

    然后您创建一个使用 AppUser 而不是 User 对象的自定义身份验证后端。

    【讨论】:

      【解决方案3】:

      我个人并没有被要求找到解决这个问题的方法,但解决这个问题的一种方法(从 SAAS 的角度来看)是在用户名前加上组织标识符(假设是唯一的组织)。例如:subdomain.yoursite.com 将等同于具有用户名的用户:subdomain_username。您只需在登录子域时编写一些业务逻辑,即可将其添加到用户名上。

      【讨论】:

      • 是的,这行得通,我考虑过,但是用户名已经只有 30 个字符,添加前缀很快就会将大小减小到无法使用的大小。
      【解决方案4】:

      我也遇到过这个问题。我正在做一个项目,我必须使用电子邮件和手机号码。作为登录字段,但它们都不应该是唯一的,因为它们是不同类型的用户,并且一个用户可以拥有多个用户实体,而且该项目只需要一个身份验证用户表(忙碌的权利!)。

      所以我扩展了 AbstractBaseUser 类,我可以在其中更改 USERNAME_FIELD 属性。方法如下:-

      from django.contrib.auth.models import AbstractUser
      from django.contrib.auth.models import PermissionsMixin
      
      # Custom model for User
      class User(AbstractBaseUser, PermissionsMixin):
      
          first_name = models.CharField(max_length=100, blank=False)
          last_name = models.CharField(max_length=100, blank=True)
          password = models.CharField(max_length=255, blank=False)
          email = models.EmailField(max_length=255, blank=False)
          mobile = models.CharField(max_length=12)
          user_type = models.ForeignKey(UserTypes, on_delete=models.DO_NOTHING)
          is_approved = models.BooleanField(default=False)
      
          objects = UserManager()
          # Here's the Catch
          USERNAME_FIELD = 'id'
      
          def get_full_name(self):
              '''
              Returns the first_name plus the last_name, with a space in between.
              '''
              full_name = '%s %s' % (self.first_name, self.last_name)
              return full_name.strip()
      
          def get_short_name(self):
              '''
              Returns the short name for the user.
              '''
              return self.first_name
      
          def email_user(self, subject, message, from_email=None, **kwargs):
              '''
              Sends an email to this User.
              '''
              send_mail(subject, message, from_email, [self.email], **kwargs)
      
          class Meta:
              db_table = 'user'
      

      是的,很惊讶? USERNAME_FIELD 应该是一个唯一字段,它是该属性的约束。我无法使用电子邮件或手机号码。作为独特的领域。

      然后我创建了一个自定义管理器来删除用户名字段(参考 = https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html#abstractbaseuser

      from django.contrib.auth.base_user import BaseUserManager
      
      
      class UserManager(BaseUserManager):
          use_in_migrations = True
      
          def _create_user(self, email, password, **extra_fields):
              """
              Creates and saves a User with the given email and password.
              """
              if not email:
                  raise ValueError('The given email must be set')
              email = self.normalize_email(email)
              user = self.model(email=email, **extra_fields)
              user.set_password(password)
              user.save(using=self._db)
              return user
      
          def create_user(self, email, password=None, **extra_fields):
              extra_fields.setdefault('is_superuser', False)
              return self._create_user(email, password, **extra_fields)
      
          def create_superuser(self, email, password, **extra_fields):
              extra_fields.setdefault('is_superuser', True)
      
              if extra_fields.get('is_superuser') is not True:
                  raise ValueError('Superuser must have is_superuser=True.')
      
              return self._create_user(email, password, **extra_fields)
      

      这样就可以了。

      【讨论】:

      • 你有没有发现任何副作用? USERNAME_FIELD 是否被任何底层 django 或 DRF 模块实际使用? (我还没有彻底搜索这个..)
      【解决方案5】:

      我面临着完全相同的问题,并且我已经阅读了很多(关于如何在 1.5 中解决该问题),我只是想到了一个更简单的解决方案。如果您只是添加一个带有组织 ID 的固定长度前缀来存储用户名怎么办?

      即组织 id = 115,选择的用户名 =“john”,固定长度为 6。因此在数据库中存储为用户名“000115_john”。

      当您登录时,您只需加入两个参数并尝试使用 Django 提供的内容进行身份验证。我不确定固定长度是否是绝对必要的,但如果用户选择只有数字的用户名,可以避免不良结果。

      【讨论】:

      • 如果您需要答案,请将其作为问题发布。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-08
      • 1970-01-01
      • 2018-11-19
      • 2021-05-22
      • 2018-05-19
      相关资源
      最近更新 更多