Czy jest dobry sposób na zrobienie tego w django bez konieczności zmiany własnego systemu uwierzytelniania? Chcę, aby nazwa użytkownika była adresem e-mail użytkownika, zamiast tworzyć nazwę użytkownika.
Proszę o poradę, dziękuję.
python
django
authentication
damon
źródło
źródło
Odpowiedzi:
Każdemu, kto chciałby to zrobić, polecam przyjrzenie się django-email-as-username, które jest dość kompleksowym rozwiązaniem, które obejmuje łatanie administratora i
createsuperuser
poleceń zarządzania, wśród innych drobiazgów.Edycja : Począwszy od Django 1.5, powinieneś rozważyć użycie niestandardowego modelu użytkownika zamiast django-email-as-username .
źródło
Oto, co robimy. To nie jest „kompletne” rozwiązanie, ale robi wiele z tego, czego szukasz.
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)
źródło
Oto jeden sposób, aby to zrobić, aby zaakceptować zarówno nazwę użytkownika, jak i adres e-mail:
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
Nie wiem, czy jest jakieś ustawienie, aby ustawić domyślny formularz uwierzytelniania, ale możesz również zastąpić adres URL w urls.py
url(r'^accounts/login/$', 'django.contrib.auth.views.login', { 'authentication_form': EmailAuthenticationForm }, name='login'),
Podniesienie wartości ValidationError zapobiegnie 500 błędom w przypadku przesłania nieprawidłowego e-maila. Korzystanie z definicji super dla „invalid_login” powoduje, że komunikat o błędzie jest niejednoznaczny (w porównaniu z konkretnym „brakiem użytkownika według tego adresu e-mail”), co byłoby wymagane, aby zapobiec wyciekowi informacji, czy adres e-mail jest zarejestrowany dla konta w Twojej usłudze. Jeśli te informacje nie są bezpieczne w Twojej architekturze, może być bardziej przyjazne wyświetlenie bardziej informacyjnego komunikatu o błędzie.
źródło
Django udostępnia teraz pełny przykład rozszerzonego systemu uwierzytelniania z administratorem i formularzem: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example
Możesz go w zasadzie skopiować / wkleić i dostosować (nie potrzebowałem
date_of_birth
w moim przypadku).W rzeczywistości jest dostępny od Django 1.5 i jest nadal dostępny od teraz (django 1.7).
źródło
Jeśli zamierzasz rozszerzyć model użytkownika, i tak będziesz musiał zaimplementować niestandardowy model użytkownika.
Oto przykład dla Django 1.8. Django 1.7 wymagałoby trochę więcej pracy, głównie zmiany domyślnych formularzy (wystarczy spojrzeć na
UserChangeForm
&UserCreationForm
indjango.contrib.auth.forms
- to jest to, czego potrzebujesz w 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'
źródło
username
pole doSiteUser
modelu, ponieważ wykonującpython manage.py makemigrations ...
polecenie otrzymuję takie wyjście:ERRORS: <class 'accounts.admin.UserAdmin'>: (admin.E033) The value of 'ordering[0]' refers to 'username', which is not an attribute of 'accounts.User'.
username
pole do mojegoUser
modelu z ichnull=True
atrybutem. W tym wpisie z wklejania chciałem pokazać implementację. pastebin.com/W1PgLrD9Inne alternatywy wydają mi się zbyt skomplikowane, więc napisałem fragment, który pozwala na uwierzytelnianie przy użyciu nazwy użytkownika, adresu e-mail lub obu, a także włączanie lub wyłączanie rozróżniania wielkości liter. Wgrałem go do pip jako 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
źródło
Najnowsza wersja rejestracji django pozwala na niezłą personalizację i może załatwić sprawę - dokumenty znajdziesz tutaj https://bitbucket.org/ubernostrum/django-registration/src/fad7080fe769/docs/backend-api.rst
źródło
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()
działa idealnie ... dla django 1.11.4
źródło
interesującą dyskusję na ten temat znajdziesz również pod poniższym linkiem:
http://groups.google.com/group/django-users/browse_thread/thread/c943ede66e6807c/2fbf2afeade397eb#2fbf2afeade397eb
źródło
Najłatwiejszym sposobem jest wyszukanie nazwy użytkownika na podstawie adresu e-mail w widoku logowania. W ten sposób możesz zostawić wszystko inne w spokoju:
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))
W swoim szablonie ustaw odpowiednio następną zmienną, tj
<form method="post" class="form-login" action="{% url 'login' %}?next={{ request.GET.next }}" accept-charset="UTF-8">
I podaj swoją nazwę użytkownika / hasło, wprowadzając odpowiednie nazwy, tj. Nazwę użytkownika, hasło.
AKTUALIZACJA :
Alternatywnie, if _is_valid_email (email): call można zastąpić znakiem „@” w nazwie użytkownika. W ten sposób możesz porzucić funkcję _is_valid_email. To naprawdę zależy od tego, jak zdefiniujesz swoją nazwę użytkownika. Nie zadziała, jeśli pozwolisz na znak „@” w swoich nazwach użytkowników.
źródło
Myślę, że najszybszym sposobem jest utworzenie formularza dziedziczącego z
UserCreateForm
, a następnie nadpisanieusername
pola za pomocąforms.EmailField
. Następnie dla każdego nowego użytkownika rejestrującego się musi zalogować się przy użyciu swojego adresu e-mail.Na przykład:
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> ...
źródło
UserCreationForm
klasie? I proszę nie polecać pisania,<input …
kiedy z pewnością{{form.username}}
jest lepiej.Nie jestem pewien, czy ludzie próbują to osiągnąć, ale znalazłem dobry (i czysty) sposób, aby poprosić tylko o e-mail, a następnie ustawić nazwę użytkownika jako e-mail w widoku przed zapisaniem.
My UserForm wymaga tylko adresu e-mail i hasła:
class UserForm(forms.ModelForm): password = forms.CharField(widget=forms.PasswordInput()) class Meta: model = User fields = ('email', 'password')
Następnie, moim zdaniem, dodaję następującą logikę:
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()
źródło