简体   繁体   中英

Allowing both email and username login in django project

I'm creating a django project for a school, and there are three main kinds of users - parents, teachers, and students. For parents and teachers, I would like them to login using email (they are currently using email logins for a legacy system).

However, for students, I would like them to login using the conventional username approach (since young kids don't have emails). Is this possible to do in Django or is there only one User Authentication model allowed?

You can create separate AuthenticationEmailBackend just for logging by email and add it to AUTHENTICATION_BACKENDS in settings. In this way different AUTHENTICATION_BACKENDS are used as alternatives if authentication fails for previous AUTHENTICATION_BACKENDS .

app/auth.py

from django.contrib.auth import get_user_model
from django.contrib.auth.models import User


class AuthenticationEmailBackend(object):
    def authenticate(self, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if getattr(user, 'is_active', False) and user.check_password(password):
                return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

settings.py

AUTHENTICATION_BACKENDS = (
    "django.contrib.auth.backends.ModelBackend",
    ...
    "app.auth.AuthenticationEmailBackend",
)

If you leave default django.contrib.auth.backends.ModelBackend in a list users can login by either username or email.

Seems request parameter is needed in authenticate method from Django 1.11:

def authenticate(self, request, username=None, password=None)

According to what is said in Django documentation .

A simple backend which allows you to login with either an email address or a username.

It should be combined with another backend for checking permissions:

settings.py:

AUTHENTICATION_BACKENDS = (
    'myproject.accounts.backends.EmailOrUsernameModelBackend',
    'django.contrib.auth.backends.ModelBackend' )

account/backends.py:

from django.conf import settings
from django.contrib.auth.models import User

class EmailOrUsernameModelBackend(object):
    def authenticate(self, username=None, password=None):
        if '@' in username:
            kwargs = {'email': username}
        else:
            kwargs = {'username': username}
        try:
            user = User.objects.get(**kwargs)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

and for case-insensitive :

class EmailOrUsernameModelBackend(object):
    def authenticate(self, username=None, password=None):
        # user_model = get_user_model()
        if '@' in username:
            # kwargs = {'email': username}
            field = 'email'
        else:
            # kwargs = {'username': username}
            field = 'username'
        try:

            case_insensitive_username_field = '{}__iexact'.format(field)
            user = User._default_manager.get(**{case_insensitive_username_field: username})

            # user = User.objects.get(**kwargs)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

For Django 3.0:

# myapp/backends.py

from django.contrib.auth.backends import BaseBackend

from .models import MyUser


class EmailAuthenticationBackend(BaseBackend):

def authenticate(self, request, **kwargs):
    email = kwargs['username'].lower()  # If you made email case insensitive add lower()
    password = kwargs['password']
    try:
        my_user = MyUser.objects.get(email=email)
    except MyUser.DoesNotExist:
        return None
    else:
        if my_user.is_active and my_user.check_password(password):
            return my_user
    return None

def get_user(self, user_id):
    try:
        return MyUser.objects.get(pk=user_id)
    except MyUser.DoesNotExist:
        return None

This works for Django 2.0 and probably previous versions:

# myapp/backends.py

from django.contrib.auth.backends import ModelBackend

from .models import MyUser


class EmailAuthenticationBackend(ModelBackend):

    def authenticate(self, request, **kwargs):
        email = kwargs['username']
        password = kwargs['password']
        try:
            my_user = MyUser.objects.get(email=email)
        except MyUser.DoesNotExist:
            return None
        else:
            if my_user.is_active and my_user.check_password(password):
                return my_user
        return None

(Not sure if it is a good idea to extend the ModelBackend, you can crete your own class)

And then, for both versions:

# settings.py

AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "myapp.backends.EmailAuthenticationBackend",
]

in models.py

class UserDet(models.Model):
       email = models.EmailField(max_length=20)
 
       userName = models.CharField(max_length=20) 

in views.py

def user_login(request):
    response_data={}
    if request.session.has_key('login_id'):  #If user already logedin send to dashboard else check userid and password
        return render(request, '/dashboard.html')
    else:
        if request.method == 'POST':  # If request method is post then only process request
            #print('here in login')
            try:

                username = request.POST.get('username')
                password = request.POST.get('pass')

                if '@' in username: #check for email
                    try:
                         
                        for u in UserDet.objects.filter(email=username):
                            #username=u['userName']
                            
                            username=u.userName
                    except:
                        response_data['code']=1000
                        response_data['status']='fail'
                        return HttpResponse(json.dumps(response_data), content_type="application/json")

                if not request.POST.get('rememberme', None):  # check user select the remember me or not if yes then create session that expire after long time
                    #print('seeing it 0')
                    request.session.set_expiry(0)
                 
                
                user = authenticate(username=username, password=password) # Check user exist or not
                
                if user:  #if user exist then
                    if user.is_active: #check user is active or not if active then successfully loged in else send error
                        login(request,user)
                        
                        update_last_login(None, user)
                        request.session['login_id'] = user.id
                        response_data['code']=800
                        response_data['status']='success'
                        return HttpResponse(json.dumps(response_data), content_type="application/json")
                        #return render(request, '/dashboard.html')
                    else:
                        response_data['code']=900  #Error for User is not active
                        response_data['status']='fail'
                        return HttpResponse(json.dumps(response_data), content_type="application/json")
                        #return HttpResponse("Your account was inactive.")
                else: #Error or Invalid username or password
                    #print("Someone tried to login and failed.")
                    #print("They used username: {} and password: {}".format(username,password))
                    response_data['code']=1000
                    response_data['status']='fail'
                    return HttpResponse(json.dumps(response_data), content_type="application/json")
            
            except:
                response_data['code']=1001
                response_data['status']='fail'
                return HttpResponse(json.dumps(response_data), content_type="application/json")
        else: #Return to index
                return redirect('/', {})


 

  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM