在Django中接受电子邮件地址作为用户名
有没有一个好的方法来做到这一点在django没有滚动我自己的身份validation系统? 我希望用户名是用户的电子邮件地址,而不是他们创build一个用户名。
请指教,谢谢。
对于其他人想要这样做,我build议看看django-email-as-username这是一个非常全面的解决scheme,其中包括修补pipe理员和createsuperuser
pipe理命令,其他createsuperuser
。
编辑 :从Django 1.5开始,你应该考虑使用自定义用户模型,而不是django-email-as-username 。
这是我们所做的。 这不是一个“完整的”解决scheme,但是它可以做很多你正在寻找的东西。
from django import forms from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User class UserForm(forms.ModelForm): class Meta: model = User exclude = ('email',) username = forms.EmailField(max_length=64, help_text="The person's email address.") def clean_email(self): email = self.cleaned_data['username'] return email class UserAdmin(UserAdmin): form = UserForm list_display = ('email', 'first_name', 'last_name', 'is_staff') list_filter = ('is_staff',) search_fields = ('email',) admin.site.unregister(User) admin.site.register(User, UserAdmin)
这是一种方法,以便用户名和电子邮件都被接受:
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist from django.forms import ValidationError class EmailAuthenticationForm(AuthenticationForm): def clean_username(self): username = self.data['username'] if '@' in username: try: username = User.objects.get(email=username).username except ObjectDoesNotExist: raise ValidationError( self.error_messages['invalid_login'], code='invalid_login', params={'username':self.username_field.verbose_name}, ) return username
不知道是否有一些设置来设置默认的身份validation表单,但您也可以覆盖urls.py中的url
url(r'^accounts/login/$', 'django.contrib.auth.views.login', { 'authentication_form': EmailAuthenticationForm }, name='login'),
提交ValidationError将防止在提交无效电子邮件时发生500个错误。 使用超级的定义为“invalid_login”保持错误消息不明确(相对于一个特定的“找不到该电子邮件的用户”),这将需要防止泄漏电子邮件地址是否注册您的服务帐户。 如果这些信息在您的体系结构中不安全,那么提供更多信息的错误消息可能会更友好。
现在Django提供了一个扩展的身份validation系统的完整示例,其中包含admin和form: https : //docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example
你可以基本上复制/粘贴并适应(我不需要在我的情况下date_of_birth
)。
它实际上是从Django 1.5开始提供的,至今仍然可用(django 1.7)。
如果要扩展用户模型,则必须实现自定义用户模型。
这里是Django 1.8的一个例子。 Django 1.7需要更多的工作,主要是改变默认的表单(只要看看django.contrib.auth.forms
中的UserChangeForm
& UserCreationForm
– 这就是1.7中你需要的)。
user_manager.py:
from django.contrib.auth.models import BaseUserManager from django.utils import timezone class SiteUserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): today = timezone.now() if not email: raise ValueError('The given email address must be set') email = SiteUserManager.normalize_email(email) user = self.model(email=email, is_staff=False, is_active=True, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, password, **extra_fields): u = self.create_user(email, password, **extra_fields) u.is_staff = True u.is_active = True u.is_superuser = True u.save(using=self._db) return u
models.py:
from mainsite.user_manager import SiteUserManager from django.contrib.auth.models import AbstractBaseUser from django.contrib.auth.models import PermissionsMixin class SiteUser(AbstractBaseUser, PermissionsMixin): email = models.EmailField(unique=True, blank=False) is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False) is_staff = models.BooleanField(default=False) USERNAME_FIELD = 'email' objects = SiteUserManager() def get_full_name(self): return self.email def get_short_name(self): return self.email
forms.py:
from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.forms import UserChangeForm, UserCreationForm from mainsite.models import SiteUser class MyUserCreationForm(UserCreationForm): class Meta(UserCreationForm.Meta): model = SiteUser fields = ("email",) class MyUserChangeForm(UserChangeForm): class Meta(UserChangeForm.Meta): model = SiteUser class MyUserAdmin(UserAdmin): form = MyUserChangeForm add_form = MyUserCreationForm fieldsets = ( (None, {'fields': ('email', 'password',)}), ('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser',)}), ('Groups', {'fields': ('groups', 'user_permissions',)}), ) add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'password1', 'password2')} ), ) list_display = ('email', ) list_filter = ('is_active', ) search_fields = ('email',) ordering = ('email',) admin.site.register(SiteUser, MyUserAdmin)
settings.py:
AUTH_USER_MODEL = 'mainsite.SiteUser'
其他替代scheme对我来说太复杂了,所以我写了一个代码片段,允许使用用户名,电子邮件或两者进行身份validation,并启用或禁用区分大小写。 我把它作为django-dual-authentication来上传到pip。
from django.contrib.auth.backends import ModelBackend from django.contrib.auth import get_user_model from django.conf import settings ################################### """ DEFAULT SETTINGS + ALIAS """ ################################### try: am = settings.AUTHENTICATION_METHOD except: am = 'both' try: cs = settings.AUTHENTICATION_CASE_SENSITIVE except: cs = 'both' ##################### """ EXCEPTIONS """ ##################### VALID_AM = ['username', 'email', 'both'] VALID_CS = ['username', 'email', 'both', 'none'] if (am not in VALID_AM): raise Exception("Invalid value for AUTHENTICATION_METHOD in project " "settings. Use 'username','email', or 'both'.") if (cs not in VALID_CS): raise Exception("Invalid value for AUTHENTICATION_CASE_SENSITIVE in project " "settings. Use 'username','email', 'both' or 'none'.") ############################ """ OVERRIDDEN METHODS """ ############################ class DualAuthentication(ModelBackend): """ This is a ModelBacked that allows authentication with either a username or an email address. """ def authenticate(self, username=None, password=None): UserModel = get_user_model() try: if ((am == 'email') or (am == 'both')): if ((cs == 'email') or cs == 'both'): kwargs = {'email': username} else: kwargs = {'email__iexact': username} user = UserModel.objects.get(**kwargs) else: raise except: if ((am == 'username') or (am == 'both')): if ((cs == 'username') or cs == 'both'): kwargs = {'username': username} else: kwargs = {'username__iexact': username} user = UserModel.objects.get(**kwargs) finally: try: if user.check_password(password): return user except: # Run the default password hasher once to reduce the timing # difference between an existing and a non-existing user. UserModel().set_password(password) return None def get_user(self, username): UserModel = get_user_model() try: return UserModel.objects.get(pk=username) except UserModel.DoesNotExist: return None
最新版本的django注册允许一些很好的定制,并可能做的工作 – 文档在这里https://bitbucket.org/ubernostrum/django-registration/src/fad7080fe769/docs/backend-api.rst
if user_form.is_valid(): # Save the user's form data to a user object without committing. user = user_form.save(commit=False) user.set_password(user.password) #Set username of user as the email user.username = user.email #commit user.save()
工作完美…为Django 1.11.4
您也可以在下面的链接中find关于此主题的有趣讨论:
最简单的方法是在login视图中根据电子邮件查找用户名。 这样你就可以独自离开一切:
from django.contrib.auth import authenticate, login as auth_login def _is_valid_email(email): from django.core.validators import validate_email from django.core.exceptions import ValidationError try: validate_email(email) return True except ValidationError: return False def login(request): next = request.GET.get('next', '/') if request.method == 'POST': username = request.POST['username'].lower() # case insensitivity password = request.POST['password'] if _is_valid_email(username): try: username = User.objects.filter(email=username).values_list('username', flat=True) except User.DoesNotExist: username = None kwargs = {'username': username, 'password': password} user = authenticate(**kwargs) if user is not None: if user.is_active: auth_login(request, user) return redirect(next or '/') else: messages.info(request, "<stvrong>Error</strong> User account has not been activated..") else: messages.info(request, "<strong>Error</strong> Username or password was incorrect.") return render_to_response('accounts/login.html', {}, context_instance=RequestContext(request))
在你的模板中设置相应的下一个variables,即
<form method="post" class="form-login" action="{% url 'login' %}?next={{ request.GET.next }}" accept-charset="UTF-8">
并给你的用户名/密码input正确的名称,即用户名,密码。
更新 :
或者,if _is_valid_email(email):call可以用用户名中的“@”代替。 这样你可以放弃_is_valid_email函数。 这真的取决于你如何定义你的用户名。 如果您在用户名中允许使用“@”字符,则不起作用。
我想最快的方法是从UserCreateForm
创build一个表单inheritance,然后用forms.EmailField
覆盖username
字段。 然后对于每个新的注册用户,他们需要用他们的电子邮件地址login。
例如:
urls.py
... urlpatterns += url(r'^signon/$', SignonView.as_view(), name="signon")
views.py
from django.contrib.auth.models import User from django.contrib.auth.forms import UserCreationForm from django import forms class UserSignonForm(UserCreationForm): username = forms.EmailField() class SignonView(CreateView): template_name = "registration/signon.html" model = User form_class = UserSignonForm
signon.html
... <form action="#" method="post"> ... <input type="email" name="username" /> ... </form> ...
不知道是否有人试图做到这一点,但我发现很好(和干净)的方式只要求电子邮件,然后在保存之前设置用户名为电子邮件的视图。
我的UserForm只需要电子邮件和密码:
class UserForm(forms.ModelForm): password = forms.CharField(widget=forms.PasswordInput()) class Meta: model = User fields = ('email', 'password')
然后在我看来,我添加以下逻辑:
if user_form.is_valid(): # Save the user's form data to a user object without committing. user = user_form.save(commit=False) user.set_password(user.password) #Set username of user as the email user.username = user.email #commit user.save()