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