python - page - register and login django
Aceptar dirección de correo electrónico como nombre de usuario en Django (12)
¿Hay una buena manera de hacer esto en django sin rodar mi propio sistema de autenticación? Quiero que el nombre de usuario sea la dirección de correo electrónico del usuario en lugar de que creen un nombre de usuario.
Por favor asesorar, gracias.
Creo que la forma más rápida es crear un formulario heredado de UserCreateForm
y luego anular el campo de username
con forms.EmailField
. Luego, para cada nuevo usuario de registro, debe iniciar sesión con su dirección de correo electrónico.
Por ejemplo:
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>
...
Django ahora proporciona un ejemplo completo de un sistema de autenticación extendido con administrador y formulario: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#a-full-example
Básicamente puedes copiarlo y pegarlo y adaptarlo (no necesité la date_of_birth
en mi caso).
En realidad está disponible desde Django 1.5 y todavía está disponible a partir de ahora (django 1.7).
Esta es una forma de hacerlo para que se acepten tanto el nombre de usuario como el correo electrónico:
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
No sé si hay alguna configuración para establecer el formulario de autenticación predeterminado, pero también puede anular la url en urls.py
url(r''^accounts/login/$'', ''django.contrib.auth.views.login'', { ''authentication_form'': EmailAuthenticationForm }, name=''login''),
Elevar ValidationError evitará 500 errores cuando se envíe un correo electrónico no válido. Usar la definición del super para "invalid_login" mantiene el mensaje de error ambiguo (frente a un "no usuario encontrado por ese correo electrónico" específico) que se requeriría para evitar filtraciones de si una dirección de correo electrónico está registrada para una cuenta en su servicio. Si esa información no es segura en su arquitectura, podría ser más amigable tener un mensaje de error más informativo.
Esto es lo que hacemos. No es una solución "completa", pero hace mucho de lo que estás buscando.
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)
La última versión de django-registration permite una buena personalización y puede hacer el trabajo - docs aquí https://bitbucket.org/ubernostrum/django-registration/src/fad7080fe769/docs/backend-api.rst
La forma más fácil es buscar el nombre de usuario basado en el correo electrónico en la vista de inicio de sesión. De esa forma puedes dejar todo lo demás solo:
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))
En su plantilla configure la siguiente variable en consecuencia, es decir,
<form method="post" class="form-login" action="{% url ''login'' %}?next={{ request.GET.next }}" accept-charset="UTF-8">
Y proporcione a sus entradas de nombre de usuario / contraseña los nombres correctos, es decir, nombre de usuario, contraseña.
ACTUALIZAR :
Alternativamente, la llamada if _is_valid_email (email): puede reemplazarse con ''@'' en username. De esta forma, puede descartar la función _is_valid_email. Esto realmente depende de cómo defines tu nombre de usuario. No funcionará si permite el carácter ''@'' en sus nombres de usuario.
No estoy seguro de si la gente está tratando de lograr esto, pero encontré una buena (y limpia) forma de solo solicitar el correo electrónico y luego establecer el nombre de usuario como el correo electrónico en la vista antes de guardar.
Mi UserForm solo requiere el correo electrónico y la contraseña:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = (''email'', ''password'')
Entonces, en mi opinión, agrego la siguiente lógica:
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()
Otras alternativas parecen demasiado complejas para mí, así que escribí un fragmento que permite autenticar usando nombre de usuario, correo electrónico o ambos, y también habilita o deshabilita las mayúsculas y minúsculas. Lo cargué a pip como 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
Si va a extender el modelo de usuario, tendrá que implementar un modelo de usuario personalizado de todos modos.
Aquí hay un ejemplo para Django 1.8. Django 1.7 requeriría un poco más de trabajo, en su mayoría cambiando los formularios por defecto (solo echa un vistazo a UserChangeForm
y UserCreationForm
en django.contrib.auth.forms
, eso es lo que necesitas en 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''
También puede encontrar una discusión interesante sobre este tema en el siguiente enlace:
Para cualquier otra persona que quiera hacer esto, recomendaría echar un vistazo a django-email-as-username que es una solución bastante completa, que incluye parchear los comandos de administración de createsuperuser
administrador, entre otros bits y piezas.
Editar : a partir de Django 1.5 en adelante, debería considerar usar un modelo de usuario personalizado en lugar de django-email-as-username .
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()
trabajando perfectamente ... para django 1.11.4