2009-04-22 133 views
78

有没有好的方法可以在django中做到这一点,而无需滚动我自己的身份验证系统?我希望用户名是用户的电子邮件地址,而不是创建用户名。在Django中接受电子邮件地址作为用户名

请指教,谢谢。

+0

Duplicate:http://stackoverflow.com/questions/704168/how-to-use-email-instead-of-username-for-user-authentication – 2009-04-22 17:45:35

回答

27

这是我们所做的。这不是一个“完整的”解决方案,但它可以满足你的要求。

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) 
+0

有帮助。谢谢。 – 2009-12-26 20:34:31

+0

适合我。尽管我可以看到这会让未来的维护者感到困惑。 – 2010-02-02 10:10:39

+1

我应该在哪里放这个代码? – 2016-07-29 15:08:48

34

对于任何人想要做到这一点,我建议考虑看看django-email-as-username这是一个非常全面的解决方案,包括修补管理员和createsuperuser管理命令等等。

编辑:从Django 1.5开始,您应该考虑使用custom user model而不是django-email-as-username

+3

如果您决定自定义用户模型是最佳选择,则可能需要检查[本教程](http://www.caktusgroup.com/blog/2013/08/07/migrating-custom-user-model- django /)在caktus组博客上,它类似于[本示例](https://docs.djangoproject.com/en/1.7/topics/auth/customizing/#a-full-example)在Django文档中给出,但要小心生产环境所需的一些细节(例如权限)。 – gaboroncancio 2015-01-22 22:55:16

+4

对于Django 1.5及以上版本,[django-custom-user](https://github.com/jcugat/django-custom-user)应用程序包含一个实现此功能的自定义用户模型。 – 2015-03-07 02:10:54

0

最简单的方法是查找基于登录查看电子邮件的用户名。这样,你可以别的先不谈一切:

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)) 

在模板中设置下一个变量相应,即

<form method="post" class="form-login" action="{% url 'login' %}?next={{ request.GET.next }}" accept-charset="UTF-8"> 

,给您的用户名/密码输入正确的名称,即用户名,密码。

UPDATE

另外,如果该_is_valid_email(电子邮件):调用可以与如果用户名换成 '@'。这样你可以放弃_is_valid_email函数。这真的取决于你如何定义你的用户名。如果您在用户名中允许输入“@”字符,它将不起作用。

+1

此代码有问题,因为用户名还可以有'@'符号,所以如果存在'@',则不需要电子邮件。 – MrKsn 2014-10-06 18:41:31

+0

真的取决于你,我不允许用户名拥有@符号。如果你这样做,你可以添加另一个过滤器查询来通过用户名搜索用户对象。 PS。用户名也可以是电子邮件,所以你必须小心设计你的用户管理。 – radtek 2014-10-06 23:10:21

+0

另外检查出来,从django.core.validators导入validate_email。您可以尝试使用validate_email('[email protected]')除ValidationError块外的尝试。它可能仍然是错误的,这取决于你的应用程序。 – radtek 2014-10-06 23:15:06

21

下面是做这件事,使双方的用户名和电子邮件接受:

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 

不知道是否有一些设置来设置默认的认证形式,但你也可以重写URL中urls.py

url(r'^accounts/login/$', 'django.contrib.auth.views.login', { 'authentication_form': EmailAuthenticationForm }, name='login'), 

提高ValidationError将防止在提交无效电子邮件时出现500错误。对于“invalid_login”使用超级定义可以使错误消息保持不明确(与找到该电子邮件的特定“未找到用户”),防止电子邮件地址是否为您的服务上的帐户注册而出现泄漏。如果这些信息在您的体系结构中不安全,那么提供更多信息错误消息可能会更友好。

2

其他替代方案对我来说看起来太复杂了,所以我写了一个代码片段,允许使用用户名,电子邮件或两者进行身份验证,并启用或禁用区分大小写。我把它上传到pip,作为django-dual-authentication

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 
7

如果要扩展用户模型,则必须实现自定义用户模型。

下面是Django 1.8的示例。 Django 1.7将需要更多的工作,主要是更改默认表单(只需看看UserChangeForm & UserCreationFormdjango.contrib.auth.forms-这就是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' 
0

我认为最快速的方法是创建一个从UserCreateForm继承的表单,然后用forms.EmailField覆盖username字段。然后,对于每个新的注册用户,他们需要使用他们的电子邮件地址进行注册。

例如:

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> 
... 
0

不知道人们是否试图完成这项工作,但我发现很好(和干净)的方式只要求电子邮件,然后在保存前将用户名设置为视图中的电子邮件。

我的窗体只需要电子邮件地址和密码:

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() 
1
 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

相关问题