Подтвердить что ты не робот

Принятие адреса электронной почты в качестве имени пользователя в Django

Есть ли хороший способ сделать это в django, не сворачивая мою собственную систему аутентификации? Я хочу, чтобы имя пользователя было адресом электронной почты пользователя, а не создавало имя пользователя.

Пожалуйста, посоветуйте, спасибо.

4b9b3361

Ответ 1

Для тех, кто хочет это сделать, я рекомендую взглянуть на django-email-as-username, который является довольно комплексным решением, которое включает исправление команд администрирования и createsuperuser, среди других бит и фрагментов.

Изменить. Начиная с версии Django 1.5 вам следует использовать пользовательскую модель вместо django-email-as-username.

Ответ 2

Вот что мы делаем. Это не "полное" решение, но оно многое делает для вас.

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

Ответ 3

Вот один из способов сделать это так, чтобы были приняты как имя пользователя, так и адрес электронной почты:

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

Не знаю, есть ли какой-либо параметр для установки формы аутентификации по умолчанию, но вы также можете переопределить url в urls.py

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

Поднятие ValidationError предотвратит 500 ошибок при отправке недействительного сообщения. Использование супер определения для "invalid_login" делает сообщение об ошибке неоднозначным (по сравнению с конкретным "нет пользователя по указанному адресу" ), который должен был бы предотвратить утечку того, зарегистрирован ли адрес электронной почты для учетной записи в вашей службе. Если эта информация не защищена в вашей архитектуре, может быть более дружелюбным иметь более информативное сообщение об ошибке.

Ответ 4

Django теперь предоставляет полный пример расширенной системы аутентификации с администратором и формой: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example

Вы можете в принципе скопировать/вставить его и адаптировать (мне не нужен date_of_birth в моем случае).

Он действительно доступен с Django 1.5 и по-прежнему доступен на данный момент (django 1.7).

Ответ 5

Если вы собираетесь расширить модель пользователя, вам все равно придется реализовать пользовательскую модель.

Вот пример Django 1.8. Django 1.7 потребует немного больше работы, в основном меняя формы по умолчанию (просто взгляните на UserChangeForm и UserCreationForm в django.contrib.auth.forms), что вам нужно в 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'

Ответ 6

Другие альтернативы выглядят слишком сложными для меня, поэтому я написал фрагмент, который позволяет аутентифицироваться с использованием имени пользователя, электронной почты или и того, и другого, а также включать или отключать регистр. Я загрузил его в pip как 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

Ответ 8

     if user_form.is_valid():
        # Save the user 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()

отлично работает... для django 1.11.4

Ответ 10

Самый простой способ - найти имя пользователя на основе электронной почты в окне входа в систему. Таким образом, вы можете оставить все остальное в одиночестве:

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

В вашем шаблоне установите следующую переменную соответственно, то есть

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

И укажите, что ваш логин/пароль вводит правильные имена, то есть имя пользователя, пароль.

ОБНОВЛЕНИЕ:

В качестве альтернативы, если _is_valid_email (email): вызов можно заменить на "@" в имени пользователя. Таким образом вы можете отказаться от функции _is_valid_email. Это зависит от того, как вы определяете свое имя пользователя. Он не будет работать, если вы разрешите символ "@" в ваших именах пользователей.

Ответ 11

Я думаю, что самый быстрый способ - создать наследование формы из UserCreateForm, а затем переопределить поле username с помощью forms.EmailField. Затем для каждого нового пользователя регистрации им необходимо подписаться с адресом электронной почты.

Например:

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

Ответ 12

Не уверен, что люди пытаются это сделать, но я нашел хороший (и чистый) способ запросить только электронное письмо, а затем сохранить имя пользователя в качестве сообщения электронной почты в представлении перед сохранением.

My UserForm требует только адрес электронной почты и пароль:

class UserForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput())

    class Meta:
        model = User
        fields = ('email', 'password')

Тогда, на мой взгляд, я добавляю следующую логику:

if user_form.is_valid():
            # Save the user 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()