Skip to content

Commit

Permalink
Import latest changes from Django (#65)
Browse files Browse the repository at this point in the history
Co-authored-by: Josep Cugat <[email protected]>
  • Loading branch information
jcugat and Josep Cugat authored Feb 13, 2022
1 parent 8928ffa commit 53284ef
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 158 deletions.
20 changes: 12 additions & 8 deletions src/custom_user/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
from .models import EmailUser


@admin.register(EmailUser)
class EmailUserAdmin(UserAdmin):

"""EmailUser Admin model."""
"""
EmailUser Admin model.
"""

fieldsets = (
(None, {"fields": ("email", "password")}),
Expand All @@ -22,13 +24,19 @@ class EmailUserAdmin(UserAdmin):
"is_superuser",
"groups",
"user_permissions",
)
),
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(None, {"classes": ("wide",), "fields": ("email", "password1", "password2")}),
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
},
),
)

# The forms to add and change user instances
Expand All @@ -46,7 +54,3 @@ class EmailUserAdmin(UserAdmin):
"groups",
"user_permissions",
)


# Register the new EmailUserAdmin
admin.site.register(EmailUser, EmailUserAdmin)
5 changes: 3 additions & 2 deletions src/custom_user/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@


class CustomUserConfig(AppConfig):

"""Default configuration for custom_user."""
"""
Default configuration for custom_user.
"""

name = "custom_user"
verbose_name = "Custom User"
Expand Down
82 changes: 42 additions & 40 deletions src/custom_user/forms.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
"""EmailUser forms."""
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _


class EmailUserCreationForm(forms.ModelForm):

"""
A form for creating new users.
Includes all the required fields, plus a repeated password.
"""

error_messages = {
"duplicate_email": _("A user with that email already exists."),
"password_mismatch": _("The two password fields didn't match."),
}

password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput,
help_text=_("Enter the same password as above, for verification."),
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)

class Meta: # noqa: D101
class Meta:
model = get_user_model()
fields = ("email",)

Expand All @@ -35,8 +40,7 @@ def clean_email(self):
Clean form email.
:return str email: cleaned email
:raise forms.ValidationError: Email is duplicated
:raise ValidationError: Email is duplicated
"""
# Since EmailUser.email is unique, this check is redundant,
# but it sets a nicer error message than the ORM. See #13147.
Expand All @@ -45,7 +49,7 @@ def clean_email(self):
get_user_model()._default_manager.get(email=email)
except get_user_model().DoesNotExist:
return email
raise forms.ValidationError(
raise ValidationError(
self.error_messages["duplicate_email"],
code="duplicate_email",
)
Expand All @@ -55,28 +59,37 @@ def clean_password2(self):
Check that the two password entries match.
:return str password2: cleaned password2
:raise forms.ValidationError: password2 != password1
:raise ValidationError: password2 != password1
"""
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
raise ValidationError(
self.error_messages["password_mismatch"],
code="password_mismatch",
)
return password2

def _post_clean(self):
super()._post_clean()
# Validate the password after self.instance is updated with form data
# by super().
password = self.cleaned_data.get("password2")
if password:
try:
password_validation.validate_password(password, self.instance)
except ValidationError as error:
self.add_error("password2", error)

def save(self, commit=True):
"""
Save user.
Save the provided password in hashed format.
:return custom_user.models.EmailUser: user
"""
user = super(EmailUserCreationForm, self).save(commit=False)
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
Expand All @@ -90,39 +103,28 @@ class EmailUserChangeForm(forms.ModelForm):
Includes all the fields on the user, but replaces the password field
with admin's password hash display field.
"""

password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=_(
"Raw passwords are not stored, so there is no way to see "
"this user's password, but you can change the password "
'using <a href="%(url)s">this form</a>.'
)
% {"url": "../password/"},
"Raw passwords are not stored, so there is no way to see this "
"user's password, but you can change the password using "
'<a href="{}">this form</a>.'
),
)

class Meta: # noqa: D101
class Meta:
model = get_user_model()
exclude = ()

def __init__(self, *args, **kwargs):
"""Init the form."""
super(EmailUserChangeForm, self).__init__(*args, **kwargs)
f = self.fields.get("user_permissions", None)
if f is not None:
f.queryset = f.queryset.select_related("content_type")

def clean_password(self):
"""
Clean password.
Regardless of what the user provides, return the initial value.
This is done here, rather than on the field, because the
field does not have access to the initial value.
:return str password:
"""
return self.initial["password"]
super().__init__(*args, **kwargs)
password = self.fields.get("password")
if password:
password.help_text = password.help_text.format("../password/")
user_permissions = self.fields.get("user_permissions")
if user_permissions:
user_permissions.queryset = user_permissions.queryset.select_related(
"content_type"
)
33 changes: 18 additions & 15 deletions src/custom_user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@


class EmailUserManager(BaseUserManager):

"""Custom manager for EmailUser."""
"""
Custom manager for EmailUser.
"""

def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
"""
Expand All @@ -24,7 +25,6 @@ def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
:param bool is_superuser: whether user admin or not
:return custom_user.models.EmailUser user: user
:raise ValueError: email is not set
"""
now = timezone.now()
if not email:
Expand All @@ -51,25 +51,31 @@ def create_user(self, email, password=None, **extra_fields):
:param str email: user email
:param str password: user password
:return custom_user.models.EmailUser user: regular user
"""
is_staff = extra_fields.pop("is_staff", False)
return self._create_user(email, password, is_staff, False, **extra_fields)
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)

def create_superuser(self, email, password, **extra_fields):
def create_superuser(self, email, password=None, **extra_fields):
"""
Create and save an EmailUser with the given email and password.
:param str email: user email
:param str password: user password
:return custom_user.models.EmailUser user: admin user
"""
return self._create_user(email, password, True, True, **extra_fields)
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)

if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")

return self._create_user(email, password, **extra_fields)

class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):

class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
"""
Abstract User with the same behaviour as Django's default User.
Expand All @@ -84,7 +90,6 @@ class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
* password
* last_login
* is_superuser
"""

email = models.EmailField(
Expand All @@ -110,7 +115,7 @@ class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []

class Meta: # noqa: D101
class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
abstract = True
Expand All @@ -129,13 +134,11 @@ def email_user(self, subject, message, from_email=None, **kwargs):


class EmailUser(AbstractEmailUser):

"""
Concrete class of AbstractEmailUser.
Use this if you don't need to extend EmailUser.
"""

class Meta(AbstractEmailUser.Meta): # noqa: D101
class Meta(AbstractEmailUser.Meta):
swappable = "AUTH_USER_MODEL"
Loading

0 comments on commit 53284ef

Please sign in to comment.