Django'da kullanıcı adı olarak e-posta adresini kabul etme


Yanıtlar:


35

Bunu yapmak isteyenler için, oldukça kapsamlı bir çözüm olan django-email-as-username'e bir göz atmanızı tavsiye ederim createsuperuser.

Düzenleme : Django 1.5'ten itibaren , django-e-posta-kullanıcı adı yerine özel bir kullanıcı modeli kullanmayı düşünmelisiniz .


3
Özel bir kullanıcı modelinin en iyi seçenek olduğuna karar verirseniz , caktus grup blogunda bu eğiticiye göz atmak isteyebilirsiniz , django belgelerinde verilen bu örneğe benzer, ancak bir üretim ortamı için gereken bazı ayrıntılara dikkat edin (örneğin İzinler).
gaboroncancio

4
Django 1.5 ve üstü için, django-özel-kullanıcı uygulaması, bunu uygulayan bir hazır özel kullanıcı modeli içerir.
Josh Kelley

29

İşte yaptığımız şey. Bu "eksiksiz" bir çözüm değil, ancak aradığınız şeyin çoğunu yapıyor.

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)

Benim için çalışıyor. Gelecekteki bakımcılar için bunun kafa karıştırıcı olduğunu görebiliyorum.
Nick Bolton

bu muhtemelen SO'da gördüğüm en akıllı cevap.
Emmet B

on admin Bu Kullanıcıyı Oluştur: exclude = ('email',) bana ['email'] anahtarının UserForm'da eksik olduğuna dair bir hata at, bu apaçık ortada.
CristiC777

22

Hem kullanıcı adının hem de e-postanın kabul edilmesi için bunu yapmanın bir yolu:

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

Varsayılan Kimlik Doğrulama formunu ayarlamak için bir ayar olup olmadığını bilmiyorum, ancak urls.py'deki url'yi de geçersiz kılabilirsiniz.

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

ValidationError'ı yükseltmek, geçersiz bir e-posta gönderildiğinde 500 hatayı önleyecektir. Üstün "valid_login "için tanımını kullanmak, hizmetinizdeki bir hesap için bir e-posta adresine kaydolup kaydolmadığında sızıntıyı önlemek için gerekli olan hata mesajını belirsiz tutar (belirli bir" bu e-postayla hiçbir kullanıcı bulunamadı "karşısında). Bu bilgiler mimarinizde güvenli değilse, daha bilgilendirici bir hata mesajı almak daha kolay olabilir.


Auth.views.login kullanmıyorum. Özel kullanarak bu benim url url'm (r'accounts / login ',' login_view ',). EmailAuthenticationForm veriyorsam, hata şu şekildedir: login_view () beklenmedik bir anahtar kelime argümanı 'authentication_form' aldı
Raja Simon

Bu benim için temiz bir şekilde çalıştı, aslında özel kullanıcı profilimi kullandım (mimarimde Kullanıcının e-posta adresinin eklenmesi garanti edilmediğinden). Kullanıcı modelini özelleştirmekten veya kullanıcı adını bir e-postaya eşit yapmaktan çok daha güzel görünüyor (çünkü bu, örneğin kullanıcı adını ifşa ederken arkadaşın e-postasını gizli tutmaya izin veriyor).
owenfi

8

Django şimdi, yönetici ve form ile genişletilmiş bir kimlik doğrulama sisteminin tam bir örneğini sunuyor: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example

Temelde kopyalayabilir / yapıştırabilir ve uyarlayabilirsiniz ( date_of_birthbenim durumumda buna ihtiyacım yoktu ).

Aslında Django 1.5'ten beri mevcuttur ve şu an itibariyle hala mevcuttur (django 1.7).


Djangoproject öğreticisini takip ediyorum ve mükemmel çalışıyor
Evhz

7

Kullanıcı modelini genişletecekseniz, yine de özel kullanıcı modelini uygulamanız gerekecektir.

İşte Django 1.8 için bir örnek. Bir az şey gerektirecektir Django 1.7 çoğunlukla varsayılan formları (sadece bakmak değişen, daha fazla iş bit UserChangeForm& UserCreationFormiçinde django.contrib.auth.forms- Eğer 1.7 in gerekenler olduğunu).

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'

Yaklaşımınızı test ettim ve işe yarıyor, ancak benim durumumda usernamealanı SiteUsermodele 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'.
eklemiştim

Ben eklemek ettik usernamebenim için alanını Userkendi barındıran model null=Trueözelliğinde. Bu yapıştırma kutusu girişinde uygulamayı göstermek istedim. pastebin.com/W1PgLrD9
bgarcial

2

Diğer alternatifler benim için çok karmaşık görünüyor, bu yüzden kullanıcı adı, e-posta veya her ikisini kullanarak kimlik doğrulamaya izin veren ve ayrıca büyük / küçük harfe duyarlı olanı etkinleştiren veya devre dışı bırakan bir pasaj yazdım. Bunu pip'e django-dual-authentication olarak yükledim .

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

mükemmel çalışıyor ... django 1.11.4 için



0

En kolay yol, giriş görünümündeki e-postaya göre kullanıcı adını aramaktır. Bu şekilde diğer her şeyi rahat bırakabilirsiniz:

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

Şablonunuzda sonraki değişkeni buna göre ayarlayın, yani

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

Ve kullanıcı adı / şifre girişlerinize doğru isimleri, yani kullanıcı adı, şifreyi verin.

GÜNCELLEME :

Alternatif olarak, if _is_valid_email (e-posta): çağrısı, kullanıcı adındaki if '@' ile değiştirilebilir. Bu şekilde _is_valid_email işlevini bırakabilirsiniz. Bu gerçekten kullanıcı adınızı nasıl tanımladığınıza bağlıdır. Kullanıcı adlarınızda '@' karakterine izin verirseniz çalışmayacaktır.


1
Bu kod hatalıdır, çünkü kullanıcı adı '@' sembolüne de sahip olabilir, bu nedenle '@' varsa, bir e-posta gerekli değildir.
MrKsn

gerçekten size bağlı, kullanıcı adının @ sembolüne sahip olmasına izin vermiyorum. Bunu yaparsanız, kullanıcı adına göre Kullanıcı nesnesinde arama yapmak için başka bir filtre sorgusu ekleyebilirsiniz. PS. kullanıcı adı aynı zamanda bir e-posta da olabilir, bu nedenle kullanıcı yönetiminizi nasıl tasarladığınıza dikkat etmeniz gerekir.
radtek

Ayrıca django.core.validators'dan validate_email'i içe aktarın. ValidationError bloğu dışında, validate_email ('@epostanız.com ') dışında bir deneme yapabilirsiniz. Uygulamanıza bağlı olarak hala hatalı olabilir.
radtek

Elbette haklısınız, giriş mantığının nasıl kurulduğuna bağlı. Kullanıcı adındaki '@' olasılığı django'nun varsayılanı olduğundan bahsetmiştim. Giriş kodu bir e-posta olduğunu düşündüğü için kullanıcı adı 'Gladi @ tor' olan kullanıcı giriş yapamadığında birileri kodunuzu kopyalayabilir ve sorun yaşayabilir.
MrKsn

İyi karar. Umarım insanlar neyi kopyaladıklarını anlarlar. E-posta doğrulamasını ekleyeceğim ve mevcut doğrulamayı yorumlayacağım, alternatif olarak bırakacağım. Sanırım doğrulamayı kullanıcı isimlerimde @ olmaması dışında kullanmamamın tek nedeni, kodu django'ya daha az bağımlı hale getirmesidir. İşler sürümden sürüme geçme eğilimindedir. Şerefe!
radtek

0

Bence en hızlı yol, formdan devralacak bir form oluşturmak UserCreateFormve ardından usernamealanı ile geçersiz kılmaktır forms.EmailField. Daha sonra her yeni kayıt kullanıcısı için e-posta adresleriyle oturum açmaları gerekir.

Örneğin:

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

Birkaç nedenden dolayı olumsuz oy verildi. Bu cevap , aynı şeyi yapmanın daha iyi bir yoludur. Ve doğrudan UserCreationFormsınıfta hangi parçacığın kullanılacağını yalnızca tanımlayabiliyorsanız neden alt sınıf? Ve lütfen daha iyi <input …olduğunda yazmayı önermeyin {{form.username}}.
Jonas G. Drange

0

İnsanların bunu başarmaya çalıştığından emin değilim, ancak yalnızca e-postayı istemenin güzel (ve temiz) bir yolunu buldum ve ardından kaydetmeden önce görünümde kullanıcı adını e-posta olarak ayarladım.

UserForm'um yalnızca e-posta ve şifre gerektirir:

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

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

Sonra benim görüşüme göre aşağıdaki mantığı ekliyorum:

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
E-postayı kullanıcı adında saklamak sorunlara neden olamaz çünkü kullanıcı adı 30 karakter, e-posta 75 karakterdir?
user2233706
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.