Accepting email address as username in Django

后端 未结 12 2042
感动是毒
感动是毒 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:22

    Here's what we do. It isn't a "complete" solution, but it does much of what you're looking for.

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

    The easiest way is to lookup the username based on the email in the login view. That way you can leave everything else alone:

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

    In your template set the next variable accordingly, i.e.

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

    And give your username / password inputs the right names, i.e. username, password.

    UPDATE:

    Alternatively, the if _is_valid_email(email): call can be replaced with if '@' in username. That way you can drop the _is_valid_email function. This really depends on how you define your username. It will not work if you allow the '@' character in your usernames.

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

    you can also find an interesting discussion on this topic at the below link :

    http://groups.google.com/group/django-users/browse_thread/thread/c943ede66e6807c/2fbf2afeade397eb#2fbf2afeade397eb

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

    For anyone else wanting to do this, I'd recommend taking a look at django-email-as-username which is a pretty comprehensive solution, that includes patching up the admin and the createsuperuser management commands, amongst other bits and pieces.

    Edit: As of Django 1.5 onwards you should consider using a custom user model instead of django-email-as-username.

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

    Other alternatives look too complex for me, so I wrote a snippet that allows to authenticate using username, email, or both, and also enable or disable case sensitive. I uploaded it to pip as 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
    
    0 讨论(0)
  • 2020-11-28 20:29

    I think the most quickly way is to create a form inherit from UserCreateForm, and then override the username field with forms.EmailField. Then for every new registration user, they need to signon with their email address.

    For example:

    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 讨论(0)
提交回复
热议问题