【问题标题】:Django - Changing Auth username to Autofield breaks the codeDjango - 将 Auth 用户名更改为 Autofield 会破坏代码
【发布时间】:2018-06-26 11:51:19
【问题描述】:

我有一个应用程序需要将用户名字段更改为Autofield

用例 - 我正在构建一个应用程序,当创建用户时,用户名应该类似于银行应用程序中的 CustomerID(整数)。

有两种方法:-

1) 有一个id (PK),它的默认实现。复制对象的id,并将其分配给用户名列,以允许用户通过 id 登录。 权衡

  • 两个重复的列。
  • 我如何调用 create_user 函数,因为它需要用户名。

2) 允许通过 ID 登录,更新 AuthBackend 并允许通过 ID 登录。 权衡

  • 用户名的值是多少。我应该将其保留为电子邮件吗?
  • 不确定,允许通过 ID 登录是否是个好主意。

唷,讨论够了。现在是实现(有问题的)部分。:-

这是我的模型。

 class User(AbstractUser):
    username = models.BigAutoField(primary_key=True)
    user_id = models.UUIDField(default=uuid.uuid4, unique=True,
                               editable=False)

一个模型不能有多个AutoField,因此username是PK。

我的迁移工作得很好。表就创建好了。

但是,当我使用创建用户时

User.objects.create_user(email=email, password=password)  #expecting the username to be auto-generated.

它说用户名是强制性的。

我检查了Django code UserManager functioncreate_user,并更新了参数username=None

class CustomUserManager(UserManager):
    use_in_migrations = True

    def create_user(self, username=None, email=None, password=None,
                    **extra_fields):
        return super(CustomUserManager, self).create_user(username=username,
                                                          email=email,
                                                          password=password,
                                                          extra_fields=extra_fields)

还是会抛出异常:-

In [4]: User.objects.create_user(email='ab@ab.com', password='abcd1234@')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-4-de9c49dd635d> in <module>()
----> 1 User.objects.create_user(email='ab@ab.com', password='abcd1234@')

/Users/praful/Desktop/code/seynse/authentication/app/modules/users/models.py in create_user(self, username, email, password, **extra_fields)
     17                                                           email=email,
     18                                                           password=password,
---> 19                                                           extra_fields=extra_fields)
     20
     21

/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/django/contrib/auth/models.py in create_user(self, username, email, password, **extra_fields)
    157         extra_fields.setdefault('is_staff', False)
    158         extra_fields.setdefault('is_superuser', False)
--> 159         return self._create_user(username, email, password, **extra_fields)
    160
    161     def create_superuser(self, username, email, password, **extra_fields):

/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/django/contrib/auth/models.py in _create_user(self, username, email, password, **extra_fields)
    146         """
    147         if not username:
--> 148             raise ValueError('The given username must be set')
    149         email = self.normalize_email(email)
    150         username = self.model.normalize_username(username)

ValueError: The given username must be set

在这种情况下可以做什么?如何将用户名更改为AutoField

【问题讨论】:

  • 你还是用username=None调用超级方法
  • 但是用户名将如何自动生成?
  • 另外,覆盖没有任何区别。但主要问题是如何自动生成用户名?
  • 我认为你需要覆盖的不仅仅是调用create_user,你需要覆盖所有经理github.com/django/django/blob/master/django/contrib/auth/…
  • 我做到了。我也超越了_create_user。但问题是如何为用户分配自动递增的值?

标签: python django django-users


【解决方案1】:

您必须为您的用户模型创建一个自定义管理器。它可以继承自BaseManager,但不能继承自默认的UserManager

class CustomUserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """
        Create and save a user with the given email, and password.
        """
        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=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        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_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=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)


class User(AbstractUser):
    username = models.BigAutoField(primary_key=True)
    user_id = models.UUIDField(default=uuid.uuid4, unique=True,
                               editable=False)

    objects = CustomUserManager()

    def __str__(self):
        return str(self.username)

请注意,我已完全删除 username,因为为 AutoField 传递显式值没有意义。

【讨论】:

  • 它几乎可以工作。对象正在被创建,但在返回对象时会分解。引发错误。 TypeError: __str__ returned non-string (type int)
  • 那是因为 AbstractUser.__str__ 直接返回用户名而不将其转换为字符串。我会编辑我的答案
猜你喜欢
  • 2013-01-20
  • 1970-01-01
  • 2011-09-27
  • 2017-03-30
  • 2012-03-27
  • 2014-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多