使用 auth_user,电子邮件保存在 last_name 列下

Posted

技术标签:

【中文标题】使用 auth_user,电子邮件保存在 last_name 列下【英文标题】:Using auth_user, email is saved under last_name column 【发布时间】:2018-11-04 05:08:56 【问题描述】:

我正在使用 Django 提供的用户模型。一切正常,但经过一次更改后,我尝试将 email 字段设为 unique

我的forms.py:

from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User



class SignUpForm(UserCreationForm):
    first_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
    last_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
    email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
    User._meta.get_field('email')._unique = True

    class Meta:
        model = User
        fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2', )

我添加到forms.py 以使电子邮件字段unique 的代码行是:

User._meta.get_field('email')._unique = True

我的views.py:

def signup(request):
    if request.method == 'POST':
        form = SignUpForm(request.POST)
        if form.is_valid():
            user = form.save(commit=False)
            user.is_active = False
            user.save()
            user.refresh_from_db()
            private_key = rsa.generate_private_key(
                public_exponent=65537,
                key_size=4096,
                backend=default_backend()
            )
            public_key = private_key.public_key()
            pem_private_key = private_key.private_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PrivateFormat.PKCS8,
                encryption_algorithm=serialization.NoEncryption()
            )
            user.profile.public_key = public_key.public_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PublicFormat.SubjectPublicKeyInfo
            )
            raw_password = form.cleaned_data.get('password1').encode()
            user.profile.salt = os.urandom(16)
            kdf = Scrypt(
                salt=user.profile.salt,
                length=32,
                n=2 ** 20,
                r=8,
                p=1,
                backend=default_backend()
            )
            key = base64.urlsafe_b64encode(kdf.derive(raw_password))
            f = Fernet(key)
            user.profile.encrypted_private_key = f.encrypt(pem_private_key)
            user.save()
            current_site = get_current_site(request)
            subject = 'Activate Your MySite Account'
            message = render_to_string('accounts/account_activation_email.html', 
                'user': user,
                'domain': current_site.domain,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
                'token': account_activation_token.make_token(user),
            )
            user.email_user(subject, message)
            return redirect('account_activation_sent')
    else:
        form = SignUpForm()
    return render(request, 'accounts/signup.html', 'form': form)

这是我注册用户的地方,我还添加了一个配置文件模型,我在其中为用户创建了一些密钥对,所有这些都按预期工作。我还发送了一封电子邮件激活以激活用户。

我的models.py:

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    email_confirmed = models.BooleanField(default=False)
    encrypted_private_key = models.CharField(max_length=500, blank=True)
    public_key = models.CharField(max_length=30, blank=True)
    salt = models.CharField(max_length=16, blank=True)

@receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)
    instance.profile.save()

在这里,我使用信号创建用户配置文件。

我的 html 文件:

% extends 'base.html' %

% block content %
  <h2>Sign up</h2>
  <form method="post">
    % csrf_token %
    % for field in form %
      <p>
         field.label_tag <br>
         field 
        % if field.help_text %
          <small style="color: grey"> field.help_text </small>
        % endif %
        % for error in field.errors %
          <p style="color: red"> error </p>
        % endfor %
      </p>
    % endfor %
    <button type="submit">Sign up</button>
  </form>
% endblock %

当我添加那行代码以在auth_user unique 中创建email 字段时,我必须先执行makemigrations,然后再执行migrateemail 字段确实变为 unique,当我尝试使用现有电子邮件注册时收到错误代码。但是在观察数据库时,我注意到电子邮件保存在last_name 列中,这真的很奇怪。

我试图通过删除那行代码并再次应用迁移来扭转这种情况,但同样的事情再次发生。

在我使电子邮件字段独一无二之前,一切都按预期工作。

【问题讨论】:

我觉得你在表单类下写这个User._meta.get_field('email')._unique = True很奇怪。我认为在models.py 文件或特殊的monkeypatch.py 文件中修补它可能会更好。 你是对的,我会将该行添加到我的 models.py 文件中。知道为什么将电子邮件保存在姓氏字段下吗? 所以我将该行移至models.py,现在问题已解决。不知道这是否是错误的,或者它是否是一个神奇地消失的错误,但非常感谢! 【参考方案1】:

您是否有理由重新发明密码加密过程?通常这样做通常不如 Django 的原生解决方案安全。

您是否考虑过创建一个使用独特电子邮件的自定义用户模型(绕过表单中的魔法)? 另外,我建议您查看 django-registration 或 django-allauth(我更喜欢 all-auth)。这些将有效地完成您尝试为您做的所有事情。

https://github.com/pennersr/django-allauth https://github.com/ubernostrum/django-registration

【讨论】:

我并没有完全重新发明身份验证过程。我仍在使用内置身份验证,我只是扩展了用户配置文件并添加了密钥对和其他一些东西。我这样做是为了实验和实践,没有任何东西会投入生产。这更像是一个项目。

以上是关于使用 auth_user,电子邮件保存在 last_name 列下的主要内容,如果未能解决你的问题,请参考以下文章

没有这样的表:main.auth_user__old

Django:异常类型:IntegrityError 唯一约束失败:auth_user.username

auth组件

如何使用 django 向默认 auth_user 模型添加额外的模型字段

/sign-up/ 关系“auth_user”处的编程错误不存在第 1 行:从“auth_user”中选择(1)作为“a”在哪里“auth_user”。“userna [重复]

数据库错误:没有这样的表:auth_user。扩展 AbstractUser 并使用模型在管理员上注册