Чи є хороший спосіб зробити це в django без прокатки власної системи автентифікації? Я хочу, щоб ім’я користувача було електронною адресою користувача замість того, щоб вони створювали ім’я користувача.
Будь ласка, порадьте, дякую.
Чи є хороший спосіб зробити це в django без прокатки власної системи автентифікації? Я хочу, щоб ім’я користувача було електронною адресою користувача замість того, щоб вони створювали ім’я користувача.
Будь ласка, порадьте, дякую.
Відповіді:
Для всіх, хто хоче зробити це, я рекомендую поглянути на django-email-as-username, яке є досить комплексним рішенням, що включає виправлення адміністратора та createsuperuserкоманд управління, серед інших частин.
Редагувати : Починаючи з Django 1.5, ви повинні розглянути можливість користувацької моделі користувача замість django-email-as-username .
Ось що ми робимо. Це не "повне" рішення, але воно робить більшу частину того, що ви шукаєте.
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)
Ось один із способів зробити це, щоб прийняти як ім’я користувача, так і електронну адресу:
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" зберігає неоднозначне повідомлення про помилку (проти конкретного "жодного користувача за вказаною електронною поштою не знайдено"), яке потрібно для запобігання витоку інформації про те, чи зареєстрована електронна адреса для облікового запису у вашій службі. Якщо ця інформація не захищена у вашій архітектурі, можливо, буде більш дружнім повідомлення з більш інформативним повідомленням про помилку.
Тепер Django пропонує повний приклад розширеної системи автентифікації з адміном та формою: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example
Ви можете в основному скопіювати / вставити його та адаптувати ( date_of_birthу моєму випадку це мені не потрібно ).
Він фактично доступний з Django 1.5 і досі доступний на сьогодні (django 1.7).
Якщо ви збираєтеся розширити користувальницьку модель, вам все одно доведеться впровадити користувацьку модель користувача.
Ось приклад для Django 1.8. Django 1.7 зажадає трохи більше роботи, переважно зміна форм за замовчуванням (просто подивіться на UserChangeForm& UserCreationFormin 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поле до своєї Userмоделі з їх null=Trueатрибутом. У цьому записі в коду вставки я хотів показати реалізацію. pastebin.com/W1PgLrD9
Інші альтернативи виглядають для мене занадто складними, тому я написав фрагмент, який дозволяє автентифікувати за допомогою імені користувача, електронної пошти або того й іншого, а також увімкнути або вимкнути регістр. Я завантажив його в pip як django-подвійну автентифікацію .
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
Остання версія реєстрації django дозволяє виконати якісні налаштування і може виконати цю роботу - документи тут https://bitbucket.org/ubernostrum/django-registration/src/fad7080fe769/docs/backend-api.rst
Ви також можете знайти цікаву дискусію на цю тему за посиланням нижче:
Найпростіший спосіб - це пошук імені користувача на основі електронної пошти у поданні для входу. Таким чином ви можете залишити все інше в спокої:
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 (електронна пошта): можна замінити на if в імені користувача. Таким чином ви можете скинути функцію _is_valid_email. Це насправді залежить від того, як ви визначите своє ім’я користувача. Це не буде працювати, якщо ви дозволите в своїх іменах символу '@'.
Я думаю, що найшвидший спосіб - це створити форму, успадковувану з 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}}це краще.
Не впевнений, чи намагаються люди це зробити, але я знайшов приємний (і чистий) спосіб запитати лише електронну пошту, а потім встановити ім’я користувача як електронну пошту в поданні перед збереженням.
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'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()