避免在Django allauth的自定义用户模型中创建用户名字段。

3

我正在使用自定义用户模型和allauth,需要省略用户名字段。我已经查看了文档以及有关使用ACCOUNT_USER_MODEL_USERNAME_FIELD = None的大量stackoverflow答案,但所有这些仍然会在我的数据库中留下一个用户名字段。

现在,由于数据库仍然有一个带有唯一约束条件的username字段,并且在将上述设置设置为None后,allauth不会将用户名放入该字段中,这导致在第一个用户创建后面临IntegrityError。我知道我可以通过将上述设置设置为'username'来解决这个问题,但是我很好奇,如何只是不使用用户名,因为我永远不会使用它。

我的模型:

class CustomUser(AbstractUser):
    # Custom user model for django-allauth
    first_name = None
    last_name = None

    def delete(self):
        # Custom delete - make sure user storage is also purged
        # Purge the user storage
        purge_userstore(self.email)
        # Call the original delete method to take care of everything else
        super(CustomUser, self).delete()

这段代码并没有做很多事情,除了覆盖delete函数。在这个主题中,这种覆盖是无关紧要的,但是为了完整起见,我将其包含在内。它还将first_namelast_name设置为None,这可以完美地按预期从数据库中删除那些字段。我尝试将user设为None,但没有任何效果。我还尝试将username设置为None,但这会引发一个带有ACCOUNT_USER_MODEL_USERNAME_FIELD = NoneFieldNotFound错误。

我的设置(相关部分):

AUTHENTICATION_BACKENDS = (
    "django.contrib.auth.backends.ModelBackend",
    "allauth.account.auth_backends.AuthenticationBackend",
)
AUTH_USER_MODEL = 'custom_account.CustomUser'
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_AUTHENTICATION_METHOD = 'email'

我的迁移文件:

migrations.CreateModel(
            name='CustomUser',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('password', models.CharField(max_length=128, verbose_name='password')),
                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
                ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
                ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
                ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
                ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
                ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
                ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
                ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
            ],
....

这个迁移的生成让我非常困惑。为什么 username 字段还在?即使我明确设置了 ACCOUNT_UNIQUE_EMAIL = True,为什么它仍被设为唯一约束?

注意: 这个迁移文件是从一个干净的状态生成的,这是我呈现的代码生成的第一个也是唯一一个迁移文件。

起初,我以为我的设置根本没有被读取。但是我在shell中检查了 django.conf.settingsallauth.account.app_settings 中的这些更改,它们都已经更新了。这里到底发生了什么?

注意: 在我搜索的众多stackoverflow问题中,这个 问题特别完美地解释了我的问题。只有一个小问题,allauth的创建者建议使用 ACCOUNT_USER_MODEL_USERNAME_FIELD = "username" 作为模型,因为“很明显使用了 username 字段”。但是答案并没有解释当你根本不想使用 username 字段时该怎么办。

1个回答

7

看起来,摆脱username字段的唯一方法是覆盖AbstractUser的username字段和/或从头开始使用完全自定义模型。虽然覆盖AbstractBaseUser也应该可以,但AbstractBaseUser提供的功能较少。

class CustomUser(AbstractUser):
    # Custom user model for django-allauth
    # Remove unnecessary fields
    username = None
    first_name = None
    last_name = None
    # Set the email field to unique
    email = models.EmailField(_('email address'), unique=True)
    # Get rid of all references to the username field
    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

这个模型将删除username字段,并使email字段唯一,同时更改所有对USERNAME_FIELD的引用为'email'。请注意,REQUIRED_FIELDS应该为空,因为USERNAME_FIELD不能在其中。当使用allauth时,这不是问题,电子邮件和密码要求由allauth管理。

我在问题中提到的设置应保持不变,特别是-

ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_REQUIRED = True

1
我已经进行了相当多的搜索,这是我找到的最好/最简单的答案之一。但是,当运行python manage.py createsuperuser时,它会因为没有用户名而生气(TypeError: create_superuser() missing 1 required positional argument: 'username')...这是否意味着您必须实现自定义UserManager类? - M Leonard
是的,覆盖管理类是必要的。原因是,如果我们检查create_superuser方法的实现,我们可以看到它期望以下签名:def create_superuser(self, username, email=None, password=None, **extra_fields):。这意味着它需要username参数。如果我们想修改这个行为,我们必须覆盖管理类。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接