2009-04-22 88 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

相關問題