Multiple USERNAME_FIELD in django user model

后端 未结 5 1880
清歌不尽
清歌不尽 2020-12-16 02:22

My custom user model:

class MyUser(AbstractBaseUser):
    username = models.CharField(unique=True,max_length=30)
    email = models.EmailField(unique=True,ma         


        
相关标签:
5条回答
  • 2020-12-16 02:54

    We can do that by implementing our own Email authentication backend.

    You can do something like below:

    Step-1 Substite the custom User model in settings:

    Since we would not be using Django's default User model for authentication, we need to define our custom MyUser model in settings.py. Specify MyUser as the AUTH_USER_MODEL in the project's settings.

    AUTH_USER_MODEL = 'myapp.MyUser'
    

    Step-2 Write the logic for the custom authentication backend:

    To write our own authentication backend, we need to implement atleast two methods i.e. get_user(user_id) and authenticate(**credentials).

    from django.contrib.auth import get_user_model
    from django.contrib.auth.models import check_password
    
    class MyEmailBackend(object):
        """
        Custom Email Backend to perform authentication via email
        """
        def authenticate(self, username=None, password=None):
            my_user_model = get_user_model()
            try:
                user = my_user_model.objects.get(email=username)
                if user.check_password(password):
                    return user # return user on valid credentials
            except my_user_model.DoesNotExist:
                return None # return None if custom user model does not exist 
            except:
                return None # return None in case of other exceptions
    
        def get_user(self, user_id):
            my_user_model = get_user_model()
            try:
                return my_user_model.objects.get(pk=user_id)
            except my_user_model.DoesNotExist:
                return None
    

    Step-3 Specify the custom authentication backend in settings:

    After writing the custom authentication backend, specify this authentication backend in the AUTHENTICATION_BACKENDS setting.

    AUTHENTICATION_BACKENDS contains the list of authentication backends to be used. Django tries authenticating across all of its authentication backends. If the first authentication method fails, Django tries the second one, and so on, until all backends have been attempted.

    AUTHENTICATION_BACKENDS = (
        'my_app.backends.MyEmailBackend', # our custom authentication backend
        'django.contrib.auth.backends.ModelBackend' # fallback to default authentication backend if first fails 
        )
    

    If authentication via MyEmailBackend fails i.e user could not be authenticated via email, then we use the Django's default authentication ModelBackend which will try to authenticate via username field of MyUser model.

    0 讨论(0)
  • 2020-12-16 02:58

    No, you cannot have more than one field defined in USERNAME_FIELD.

    One option would be to write your own custom login to check for both fields yourself. https://docs.djangoproject.com/en/1.8/topics/auth/customizing/

    i.e. change the backend to your own. AUTHENTICATION_BACKENDS then write an authenticate method and check the username on both fields in the DB.

    PS you may want to use unique_together on your model so you don't run into problems.

    Another option would be to use the actual field username to store both string and email.

    0 讨论(0)
  • 2020-12-16 03:03

    The USERNAME_FIELD setting does not support a list. You could create a custom authentication backend that tries to look up the user on the 'email' or 'username' fields.

    from django.db.models import Q
    
    from django.contrib.auth import get_user_model
    
    MyUser = get_user_model()
    
    class UsernameOrEmailBackend(object):
        def authenticate(self, username=None, password=None, **kwargs):
            try:
               # Try to fetch the user by searching the username or email field
                user = MyUser.objects.get(Q(username=username)|Q(email=username))
                if user.check_password(password):
                    return user
            except MyUser.DoesNotExist:
                # Run the default password hasher once to reduce the timing
                # difference between an existing and a non-existing user (#20760).
                MyUser().set_password(password)
    

    Then, in your settings.py set AUTHENTICATION_BACKENDS to your authentication backend:

     AUTHENTICATION_BACKENDS = ('path.to.UsernameOrEmailBackend,)\
    

    Note that this solution isn't perfect. For example, password resets would only work with the field specified in your USERNAME_FIELD setting.

    0 讨论(0)
  • 2020-12-16 03:03

    If your USERNAME_FIELD is username and the user logs in with email, maybe you can write a code that fetches the username using the provided email and then use that username along with the password to authenticate.

    0 讨论(0)
  • 2020-12-16 03:13

    Unfortunately, not out-of-the box.

    The auth contrib module asserts that the USERNAME_FIELD value is mono-valued.

    See https://github.com/django/django/search?q=USERNAME_FIELD

    If you want to have a multi-valued USERNAME_FIELD, you will either have to write the corresponding logic or to find a package that allow it.

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