如何在 django-allauth 中使用电子邮件完成用户(设置密码)注册?



我想用一些基本信息(姓名,公司...)和电子邮件注册用户。用户提交表单后,我想发送一封电子邮件,其中包含一个链接,该链接将用户带到设置其密码的页面。

我已经尝试了这个问题的解决方案: Django AllAuth - 如何手动发送重置密码电子邮件? 电子邮件已发送,电子邮件中的链接正常工作,但我在我的网站上收到断言错误。 错误出在setup_user_email(request, user, addresses)函数内部的/allauth/account/utils.py中。行:assert not EmailAddress.objects.filter(user=user).exists()

我在这里做错了什么?

我 models.py:

from django.contrib.auth.models import AbstractUser
from django.db import models

from allauth.account.views import PasswordResetView
from django.conf import settings
from django.dispatch import receiver
from django.http import HttpRequest
from django.middleware.csrf import get_token

class Kompanija(models.Model):
naziv = models.CharField(max_length=50)
adresa = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.naziv
class Meta:
verbose_name = "Kompanija"
verbose_name_plural = "Kompanije"
class CustomUser(AbstractUser):
ime = models.CharField(max_length=30, default='')
prezime = models.CharField(max_length=30, default='')
kompanija = models.ForeignKey(Kompanija, on_delete=models.CASCADE, null=True, blank=True)
is_premium = models.BooleanField('premium status', default=False)
def __str__(self):
return self.ime + " " + self.prezime
@receiver(models.signals.post_save, sender=settings.AUTH_USER_MODEL)
def send_reset_password_email(sender, instance, created, **kwargs):
if created:
request = HttpRequest()
request.method = 'POST'
if settings.DEBUG:
request.META['HTTP_HOST'] = 'www.zahtjevi.com'
else:
request.META['HTTP_HOST'] = 'www.zahtjevi.com'
request.POST = {
'email': instance.email,
'csrfmiddlewaretoken': get_token(HttpRequest())
}
PasswordResetView.as_view()(request)

我 adapter.py:

from allauth.account.adapter import DefaultAccountAdapter
from .models import Kompanija
class UserAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=True):
user = super(UserAccountAdapter, self).save_user(request, user, form, commit=False)
user.ime = form.cleaned_data.get('ime')
user.prezime = form.cleaned_data.get('prezime')
user.kompanija = Kompanija.objects.get(id=form.cleaned_data.get('kompanija'))
user.is_active = True
user.save()

我 forms.py:

from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from allauth.account.forms import SignupForm
from .models import CustomUser, Kompanija
class CustomUserCreationForm(UserCreationForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta(UserCreationForm):
model = CustomUser
fields = ('username', 'email', 'ime', 'prezime')
class CustomUserChangeForm(UserChangeForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = CustomUser
fields = ('username', 'email', 'ime', 'prezime')

class CustomSignupForm(SignupForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = CustomUser
def signup(self, request, user):
user.ime = self.cleaned_data['ime']
user.prezime = self.cleaned_data['prezime']
user.kompanija = Kompanija.objects.get(id=self.cleaned_data['kompanija'])
user.is_active = True
user.save()
return user

class CustomUserAdminForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ('email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium')

更新 1:

错误回溯:

Environment:
Request Method: POST
Request URL: https://www.zahtjevi.com/accounts/signup/
Django Version: 2.2.3
Python Version: 3.7.0
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'debug_toolbar',
'allauth',
'allauth.account',
'allauth.socialaccount',
'korisnici.apps.KorisniciConfig',
'pages.apps.PagesConfig',
'projekti.apps.ProjektiConfig',
'zahtjevi.apps.ZahtjeviConfig']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware']
Traceback:
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
34.             response = get_response(request)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
115.                 response = self.process_exception_by_middleware(e, request)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/views/generic/base.py" in view
71.             return self.dispatch(request, *args, **kwargs)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
45.         return bound_method(*args, **kwargs)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/views/decorators/debug.py" in sensitive_post_parameters_wrapper
76.             return view(request, *args, **kwargs)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py" in dispatch
214.         return super(SignupView, self).dispatch(request, *args, **kwargs)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py" in dispatch
80.                                             **kwargs)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py" in dispatch
192.                                                           **kwargs)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/views/generic/base.py" in dispatch
97.         return handler(request, *args, **kwargs)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py" in post
103.             response = self.form_valid(form)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py" in form_valid
230.         self.user = form.save(self.request)
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/forms.py" in save
407.         setup_user_email(request, user, [])
File "/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/utils.py" in setup_user_email
254.     assert not EmailAddress.objects.filter(user=user).exists()
Exception Type: AssertionError at /accounts/signup/
Exception Value: 

更新 2:

当用户提交注册表单时,将引发此错误。 在我的forms.py中,我有一个向用户显示的CustomSignupForm。 调用的观点来自 django-allauth(我在我的 urls.pypath('accounts/', include('allauth.urls')),

我试图从我的表单中删除save(),但错误仍然存在,如果我删除settings.py内的ACCOUNT_ADAPTER变量,错误仍然存在......

更新 3:

从 django-debug-toolbar 我看到:

SELECT (1) AS `a` 
FROM `account_emailaddress` 
WHERE `account_emailaddress`.`email` LIKE 'dario@igl.hr' 
LIMIT 1
2 similar queries.   Duplicated 2 times.
/bin/user_wsgi_wrapper.py in __call__(202)
app_iterator = self.app(environ, start_response)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/contrib/staticfiles/handlers.py in __call__(65)
return self.application(environ, start_response)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/views/generic/base.py in view(71)
return self.dispatch(request, *args, **kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/views/decorators/debug.py in sensitive_post_parameters_wrapper(76)
return view(request, *args, **kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py in dispatch(214)
return super(SignupView, self).dispatch(request, *args, **kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py in dispatch(80)
**kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py in dispatch(192)
**kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/views/generic/base.py in dispatch(97)
return handler(request, *args, **kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py in post(103)
response = self.form_valid(form)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py in form_valid(230)
self.user = form.save(self.request)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/forms.py in save(404)
adapter.save_user(request, user, self)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/adapter.py in save_user(243)
user.save()
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/contrib/auth/base_user.py in save(66)
super().save(*args, **kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/dispatch/dispatcher.py in send(175)
for receiver in self._live_receivers(sender)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/dispatch/dispatcher.py in <listcomp>(175)
for receiver in self._live_receivers(sender)
/home/filozof/nadzor/korisnici/models.py in send_reset_password_email(51)
PasswordResetView.as_view()(request)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/views/generic/base.py in view(71)
return self.dispatch(request, *args, **kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/views/generic/base.py in dispatch(97)
return handler(request, *args, **kwargs)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py in post(103)
response = self.form_valid(form)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/views.py in form_valid(644)
form.save(self.request)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/forms.py in save(516)
temp_key = token_generator.make_token(user)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/contrib/auth/tokens.py in make_token(21)
return self._make_token_with_timestamp(user, self._num_days(self._today()))
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/django/contrib/auth/tokens.py in _make_token_with_timestamp(60)
self._make_hash_value(user, timestamp),
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/forms.py in _make_hash_value(41)
sync_user_email_addresses(user)
/home/filozof/.virtualenvs/django2/lib/python3.7/site-packages/allauth/account/utils.py in sync_user_email_addresses(350)
and EmailAddress.objects.filter(email__iexact=email).exists():

问题出在 allauthSignupFormsave()方法上:它首先保存用户,然后在最后设置用户的EmailAddress(它有一个单独的用户电子邮件地址模型)。

def save(self, request):
...
adapter.save_user(request, user, self)  # this calls your post-save signal
self.custom_signup(request, user)  # this should call your custom signup method in your custom signup form
setup_user_email(request, user, [])  # this raises the exception
return user

现在,密码重置视图/窗体可确保正确设置用户的EmailAddress(因此它将user.email添加为EmailAddress模型)。由于这是由您的post_save信号完成的,因此它会在调用setup_user_email之前发生。

您应该:

  1. 删除post_save信号处理程序,因为它总是会在setup_user_email之前调用。
  2. 代码以将重置密码电子邮件发送到表单的save()方法中(首先调用上述super().save()方法,然后发送密码重置电子邮件。

    # in your custom form
    def save(self, request):
    user = super().save(request)
    new_request = HttpRequest()
    new_request.method = 'POST'
    new_request.META['HTTP_HOST'] = 'www.zahtjevi.com'
    new_request.POST = {
    'email': user.email,
    'csrfmiddlewaretoken': get_token(new_request)
    }
    PasswordResetView.as_view()(new_request)
    return user
    

注意:我感觉您的自定义表单根本没有被使用,您是否按照此处的规定在ACCOUNT_FORMS中定义了它?

注意2(已编辑):表单的signup()方法是超凡的,它与自定义适配器的save_user()方法执行相同的操作。删除两者之一,可能是窗体的signup()方法,这样您就不会点击 db 两次来保存user对象。