我想自定义Django的内置身份验证系统,用电子邮件替换用户名(即使用电子邮件/密码组合代替用户名/密码组合进行登录),因此我制作了自定义User
模型通过继承AbstractBaseUser
和PermissionsMixin
以及自定义UserManager
,然后设置settings.AUTH_USER_MODEL = 'myapp.User'
以启用它。
一切正常,但问题是,django.contrib.auth.views.LoginView
生成的登录表单使用name="username" type="text"
并为电子邮件输入字段标记了标识id_username
(尽管使用了正确的标签字符串"电子邮件地址")。登录表单无论如何都有效,但语义错误,HTML5客户端验证不会以这种方式工作。请注意,这不是注册表中的问题;它按预期将电子邮件字段命名为email
而不是username
。
以下是代码:
models.py:
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils.translation import ugettext_lazy as _
from .managers import UserManager
class User(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField(_('first name'), max_length=30)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('email address'), unique=True)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
is_active = models.BooleanField(_('active'), default=False)
verification_code = models.CharField(_('verification code'), max_length=32,
blank=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name']
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def __str__(self):
return self.email
def get_full_name(self):
'''
Return 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):
'''
Return short name (first name) for the user.
'''
return self.first_name
managers.py
from django.contrib.auth.base_user import BaseUserManager
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
'''
Create and save 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)
settings.py
...
# Redirect to my URL after login
LOGIN_REDIRECT_URL = '/'
# Custom login backend (using email instead of username)
AUTH_USER_MODEL = 'myapp.User'
...
urls.py
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
url(r'^$', views.home, name='home'),
url(r'^accounts/register/$', views.register, name='register'),
url(r'^accounts/login/$', views.login, name='login'),
url(r'^accounts/logout/$', auth_views.LogoutView.as_view(),
{'next_page': '/'}, name='logout'),
url(r'^accounts/verify/(?P<code>[a-zA-Z0-9]{32})$', views.verify,
name='verify'),
]
views.py
...
from django.contrib.auth.views import LoginView
from django.http import HttpResponseRedirect
from django.urls import reverse
from . import util
from .forms import RegistrationForm
...
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.verification_code = get_random_string(length=32)
user.save()
res = util.send_verification_email(request, user)
if not res.ok:
logging.error(res.text)
return HttpResponse(status=500)
return render(request, 'registration/registration_complete.html')
else:
form = RegistrationForm()
token = {}
token.update(csrf(request))
token['form'] = form
return render(request, 'registration/register.html', token)
def login(request):
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('home'))
else:
return LoginView.as_view()(request)
...
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import User
class RegistrationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = ('first_name', 'last_name', 'email')
widgets = {
'first_name': forms.TextInput(attrs={'placeholder': 'John',
'autofocus': 'true'}),
'last_name': forms.TextInput(attrs={'placeholder': 'Doe'}),
'email': forms.TextInput(attrs={'placeholder':
'johndoe@example.com'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['email'].widget.attrs.pop('autofocus')
模板使用{{ form }}
上的for循环生成表单。
我意识到我正在使用forms.py
中定义的自定义注册表单,但我不知道如何使用登录表单执行相同操作,我无法在线查找任何内容或在文档中。
我错过了什么?
答案 0 :(得分:4)
我根本不明白你为什么要使用LoginView。记录用户是just a few lines of code,并且在您自己的视图中非常容易。
此外,直接从另一个视图调用视图确实不是一个好习惯。
但是如果你真的想使用LoginView,你需要继承AuthenticationForm,然后将该表单类传递给LoginView调用:
return LoginView.as_view(authentication_form=MyAuthFormClass)(request)