Django에서 이메일 주소를 사용자 이름으로 수락


답변:


35

이 작업을 수행하려는 다른 사람에게는 django-email-as-username 을 살펴볼 것을 권합니다. 이것은 관리자 및 createsuperuser관리 명령 패치를 포함하는 매우 포괄적 인 솔루션 입니다.

편집 : Django 1.5부터 django-email-as-username 대신 사용자 정의 사용자 모델을 사용하는 것을 고려해야 합니다.


3
커스텀 사용자 모델이 최선의 선택이라고 결정했다면 , caktus 그룹 블로그 에서이 튜토리얼 을 확인하고 싶을 것입니다. 이것은 django 문서에 제공된 이 예제 와 비슷 하지만 프로덕션 환경에 필요한 몇 가지 세부 사항 (예 : 권한)을 처리합니다.
— gaboroncancio

4
Django 1.5 이상의 경우 django-custom-user 앱에는이를 구현하는 미리 준비된 사용자 정의 사용자 모델이 포함되어 있습니다.
— Josh Kelley 2015 년

29

여기에 우리가하는 일이 있습니다. "완전한"솔루션은 아니지만 원하는 기능을 많이 수행합니다.

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)

나를 위해 작동합니다. 나는 이것이 미래의 유지 보수 자들에게 혼란스러워 보일 수 있지만.
— Nick Bolton

그것은 아마도 내가 본 것 중 가장 현명한 대답 일 것입니다.
— Emmet B

관리자에서 사용자 만들기 : exclude = ( 'email',) 내게 key [ 'email']이 UserForm에서 누락되었다는 오류를 던집니다.
— CristiC777

22

다음은 사용자 이름과 이메일이 모두 허용되도록하는 한 가지 방법입니다.

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

기본 인증 양식을 설정하는 설정이 있는지 모르겠지만 urls.py에서 URL을 재정의 할 수도 있습니다.

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

ValidationError를 발생 시키면 유효하지 않은 이메일이 제출 될 때 500 오류를 방지 할 수 있습니다. "invalid_login"에 대한 super의 정의를 사용하면 이메일 주소가 귀하의 서비스 계정에 등록되었는지 여부를 유출하는 것을 방지하는 데 필요한 오류 메시지가 모호하게 유지됩니다 (특정 "해당 이메일로 사용자가 없음"과 비교). 해당 정보가 아키텍처에서 안전하지 않은 경우 더 많은 정보를 제공하는 오류 메시지가있는 것이 더 편할 수 있습니다.


auth.views.login을 사용하지 않습니다. custom을 사용하면 내 url url (r'accounts / login ','login_view ',)입니다. EmailAuthenticationForm을 제공하는 경우 오류는 login_view ()에 예기치 않은 키워드 인수 'authentication_form'이 있습니다.
— Raja Simon

이것은 나를 위해 깨끗하게 작동했으며 실제로 내 사용자 지정 사용자 프로필을 사용했습니다 (내 아키텍처에서 사용자는 이메일 주소가 첨부되지 않았 음을 보장하기 때문입니다). 사용자 모델을 사용자 지정하거나 사용자 이름을 이메일과 동일하게 만드는 것보다 훨씬 더 멋지게 보입니다 (예를 들어 사용자 이름을 노출하면서 친구의 이메일을 비공개로 유지할 수 있기 때문입니다).
— owenfi

8

Django는 이제 관리자 및 양식이 포함 된 확장 인증 시스템의 전체 예제를 제공합니다. https://docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example

기본적으로 복사 / 붙여 넣기 및 적용 할 수 있습니다 ( date_of_birth제 경우에는 필요하지 않음 ).

실제로 Django 1.5부터 사용할 수 있으며 현재까지도 사용할 수 있습니다 (django 1.7).


나는 djangoproject 튜토리얼을 따르고 완벽하게 작동합니다
— Evhz

7

사용자 모델을 확장하려는 경우 어쨌든 사용자 지정 사용자 모델을 구현해야합니다.

다음은 Django 1.8의 예입니다. 조금 필요 장고 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'

나는 당신의 접근 방식과 작동을 테스트했지만, 제 경우에는 모델에 username필드를 추가했습니다 SiteUser. python manage.py makemigrations ...명령을 실행하면 다음과 같은 출력이 ERRORS: <class 'accounts.admin.UserAdmin'>: (admin.E033) The value of 'ordering[0]' refers to 'username', which is not an attribute of 'accounts.User'.
— 나오기 때문입니다

해당 속성을 사용하여 모델에 username필드를 추가했습니다 . 이 붙여 넣기 bin 항목에서 구현을 보여주고 싶었습니다. pastebin.com/W1PgLrD9Usernull=True
— bgarcial

2

다른 대안은 나에게 너무 복잡해 보이므로 사용자 이름, 이메일 또는 둘 다를 사용하여 인증하고 대소 문자 구분을 활성화 또는 비활성화 할 수있는 스 니펫을 작성했습니다. django-dual-authentication 으로 pip에 업로드했습니다 .

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


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

완벽하게 작동합니다 ... 장고 1.11.4에서



0

가장 쉬운 방법은 로그인보기에서 이메일을 기반으로 사용자 이름을 조회하는 것입니다. 이렇게하면 다른 모든 것을 그대로 둘 수 있습니다.

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

그리고 사용자 이름 / 암호 입력에 올바른 이름, 즉 사용자 이름, 암호를 제공하십시오.

업데이트 :

또는 if _is_valid_email (email) : 호출은 사용자 이름에서 if '@'로 대체 될 수 있습니다. 이렇게하면 _is_valid_email 함수를 삭제할 수 있습니다. 이것은 실제로 사용자 이름을 정의하는 방법에 따라 다릅니다. 사용자 이름에 '@'문자를 허용하면 작동하지 않습니다.


1
사용자 이름에도 '@'기호가있을 수 있으므로이 코드는 버그가 있습니다. 따라서 '@'가 있으면 이메일이 필요하지 않습니다.
— MrKsn 2014 년

정말 당신에게 달려 있습니다, 나는 사용자 이름에 @ 기호를 허용하지 않습니다. 그렇게하면 사용자 이름으로 사용자 개체를 검색하는 다른 필터 쿼리를 추가 할 수 있습니다. 추신. 사용자 이름은 이메일이 될 수도 있으므로 사용자 관리를 설계 할 때주의해야합니다.
— radtek 2014 년

또한 django.core.validators에서 import validate_email을 확인하십시오. validate_email ('your@email.com ')으로 ValidationError 블록을 제외하고 시도 할 수 있습니다. 앱에 따라 여전히 버그가있을 수 있습니다.
— radtek 2014 년

물론, 로그인 로직이 어떻게 설정되어 있는지에 따라 다릅니다. 사용자 이름에 '@'의 가능성이 django의 기본값이기 때문에 언급했습니다. 사용자 이름이 'Gladi @ tor'인 사용자가 이메일이라고 생각하여 로그인 할 수없는 경우 누군가 귀하의 코드를 복사하여 문제가 발생할 수 있습니다.
— MrKsn

좋은 전화. 나는 사람들이 그들이 복사하는 것이 무엇인지 이해하기를 바랍니다. 이메일 유효성 검사를 추가하고 현재 유효성 검사를 주석 처리하고 대안으로 남겨 둡니다. @가없는 사용자 이름과 별도로 유효성 검사를 사용하지 않은 유일한 다른 이유는 코드가 django에 덜 의존하게 만드는 것입니다. 상황은 버전에서 버전으로 이동하는 경향이 있습니다. 건배!
— radtek 2014 년

0

나는 가장 빠르게 방법에서 양식 상속을 만드는 것입니다 생각 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>
...

몇 가지 이유로 비추천했습니다. 이 답변 은 동일한 작업을 수행하는 더 좋은 방법입니다. 그리고 클래스에서 직접 사용할 위젯을 정의 할 수 있는데 왜 서브 UserCreationForm클래스입니까? 그리고 <input …확실히 {{form.username}}더 나은 때를 작성하지 않는 것이 좋습니다 .
— Jonas G. Drange

0

사람들이이 작업을 수행하려고하는지 확실하지 않지만 이메일 만 요청한 다음 저장하기 전에보기에서 사용자 이름을 이메일로 설정하는 좋은 방법을 찾았습니다.

내 사용자 양식에는 이메일과 비밀번호 만 필요합니다.

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's 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()

1
사용자 이름이 30 자이고 이메일이 75 자이기 때문에 사용자 이름에 이메일을 저장하지 못하면 문제가 발생합니까?
— user2233706
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.