Multiple USERNAME_FIELD in django user model

落爺英雄遲暮 提交于 2020-01-11 04:53:09

问题


My custom user model:

class MyUser(AbstractBaseUser):
    username = models.CharField(unique=True,max_length=30)
    email = models.EmailField(unique=True,max_length=75)
    is_staff = models.IntegerField(default=False)
    is_active = models.IntegerField(default=False)
    date_joined = models.DateTimeField(default=None)

    # Use default usermanager
    objects = UserManager()

    USERNAME_FIELD = 'email'

Is there a way to specify multiple USERNAME_FIELD ? Something like ['email','username'] so that users can login via email as well as username ?


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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.



来源:https://stackoverflow.com/questions/31370118/multiple-username-field-in-django-user-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!