Accepting email address as username in Django

后端 未结 12 2043
感动是毒
感动是毒 2020-11-28 19:48

Is there a good way to do this in django without rolling my own authentication system? I want the username to be the user\'s email address instead of them creating a userna

相关标签:
12条回答
  • 2020-11-28 20:32

    Django now provides a full example of an extended authentication system with admin and form: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example

    You can basically copy/paste it and adapt (I didn't need the date_of_birth in my case).

    It is actually available since Django 1.5 and is still available as of now (django 1.7).

    0 讨论(0)
  • 2020-11-28 20:34

    Not sure if people are trying to accomplish this, but I found nice (and clean) way to only ask for the email and then set the username as the email in the view before saving.

    My UserForm only requires the email and password:

    class UserForm(forms.ModelForm):
        password = forms.CharField(widget=forms.PasswordInput())
    
        class Meta:
            model = User
            fields = ('email', 'password')
    

    Then in my view I add the following logic:

    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()
    
    0 讨论(0)
  • 2020-11-28 20:41

    If you're going to extend user model, you will have to implement custom user model anyway.

    Here is an example for Django 1.8. Django 1.7 would require a little bit more work, mostly changing default forms (just take a look at UserChangeForm & UserCreationForm in django.contrib.auth.forms - that's what you need in 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 讨论(0)
  • 2020-11-28 20:44

    Here is one way to do it so that both username and email are accepted:

    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
    

    Don't know if there is some setting to set the default Authentication form but you can also override the url in urls.py

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

    Raising the ValidationError will prevent 500 errors when an invalid email is submitted. Using the super's definition for "invalid_login" keeps the error message ambiguous (vs a specific "no user by that email found") which would be required to prevent leaking whether an email address is signed up for an account on your service. If that information is not secure in your architecture it might be friendlier to have a more informative error message.

    0 讨论(0)
  • 2020-11-28 20:44
         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()
    

    working perfectly... for django 1.11.4

    0 讨论(0)
  • 2020-11-28 20:47

    Latest version of django-registration allows some nice customisation and might do the job - docs here https://bitbucket.org/ubernostrum/django-registration/src/fad7080fe769/docs/backend-api.rst

    0 讨论(0)
提交回复
热议问题