Skip to content

Commit 53284ef

Browse files
jcugatJosep Cugat
andauthored
Import latest changes from Django (#65)
Co-authored-by: Josep Cugat <[email protected]>
1 parent 8928ffa commit 53284ef

File tree

6 files changed

+264
-158
lines changed

6 files changed

+264
-158
lines changed

src/custom_user/admin.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
from .models import EmailUser
88

99

10+
@admin.register(EmailUser)
1011
class EmailUserAdmin(UserAdmin):
11-
12-
"""EmailUser Admin model."""
12+
"""
13+
EmailUser Admin model.
14+
"""
1315

1416
fieldsets = (
1517
(None, {"fields": ("email", "password")}),
@@ -22,13 +24,19 @@ class EmailUserAdmin(UserAdmin):
2224
"is_superuser",
2325
"groups",
2426
"user_permissions",
25-
)
27+
),
2628
},
2729
),
2830
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
2931
)
3032
add_fieldsets = (
31-
(None, {"classes": ("wide",), "fields": ("email", "password1", "password2")}),
33+
(
34+
None,
35+
{
36+
"classes": ("wide",),
37+
"fields": ("email", "password1", "password2"),
38+
},
39+
),
3240
)
3341

3442
# The forms to add and change user instances
@@ -46,7 +54,3 @@ class EmailUserAdmin(UserAdmin):
4654
"groups",
4755
"user_permissions",
4856
)
49-
50-
51-
# Register the new EmailUserAdmin
52-
admin.site.register(EmailUser, EmailUserAdmin)

src/custom_user/apps.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44

55
class CustomUserConfig(AppConfig):
6-
7-
"""Default configuration for custom_user."""
6+
"""
7+
Default configuration for custom_user.
8+
"""
89

910
name = "custom_user"
1011
verbose_name = "Custom User"

src/custom_user/forms.py

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
11
"""EmailUser forms."""
22
from django import forms
3-
from django.contrib.auth import get_user_model
3+
from django.contrib.auth import get_user_model, password_validation
44
from django.contrib.auth.forms import ReadOnlyPasswordHashField
5+
from django.core.exceptions import ValidationError
56
from django.utils.translation import gettext_lazy as _
67

78

89
class EmailUserCreationForm(forms.ModelForm):
9-
1010
"""
1111
A form for creating new users.
1212
1313
Includes all the required fields, plus a repeated password.
14-
1514
"""
1615

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

22-
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
21+
password1 = forms.CharField(
22+
label=_("Password"),
23+
strip=False,
24+
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
25+
help_text=password_validation.password_validators_help_text_html(),
26+
)
2327
password2 = forms.CharField(
2428
label=_("Password confirmation"),
25-
widget=forms.PasswordInput,
26-
help_text=_("Enter the same password as above, for verification."),
29+
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
30+
strip=False,
31+
help_text=_("Enter the same password as before, for verification."),
2732
)
2833

29-
class Meta: # noqa: D101
34+
class Meta:
3035
model = get_user_model()
3136
fields = ("email",)
3237

@@ -35,8 +40,7 @@ def clean_email(self):
3540
Clean form email.
3641
3742
:return str email: cleaned email
38-
:raise forms.ValidationError: Email is duplicated
39-
43+
:raise ValidationError: Email is duplicated
4044
"""
4145
# Since EmailUser.email is unique, this check is redundant,
4246
# but it sets a nicer error message than the ORM. See #13147.
@@ -45,7 +49,7 @@ def clean_email(self):
4549
get_user_model()._default_manager.get(email=email)
4650
except get_user_model().DoesNotExist:
4751
return email
48-
raise forms.ValidationError(
52+
raise ValidationError(
4953
self.error_messages["duplicate_email"],
5054
code="duplicate_email",
5155
)
@@ -55,28 +59,37 @@ def clean_password2(self):
5559
Check that the two password entries match.
5660
5761
:return str password2: cleaned password2
58-
:raise forms.ValidationError: password2 != password1
59-
62+
:raise ValidationError: password2 != password1
6063
"""
6164
password1 = self.cleaned_data.get("password1")
6265
password2 = self.cleaned_data.get("password2")
6366
if password1 and password2 and password1 != password2:
64-
raise forms.ValidationError(
67+
raise ValidationError(
6568
self.error_messages["password_mismatch"],
6669
code="password_mismatch",
6770
)
6871
return password2
6972

73+
def _post_clean(self):
74+
super()._post_clean()
75+
# Validate the password after self.instance is updated with form data
76+
# by super().
77+
password = self.cleaned_data.get("password2")
78+
if password:
79+
try:
80+
password_validation.validate_password(password, self.instance)
81+
except ValidationError as error:
82+
self.add_error("password2", error)
83+
7084
def save(self, commit=True):
7185
"""
7286
Save user.
7387
7488
Save the provided password in hashed format.
7589
7690
:return custom_user.models.EmailUser: user
77-
7891
"""
79-
user = super(EmailUserCreationForm, self).save(commit=False)
92+
user = super().save(commit=False)
8093
user.set_password(self.cleaned_data["password1"])
8194
if commit:
8295
user.save()
@@ -90,39 +103,28 @@ class EmailUserChangeForm(forms.ModelForm):
90103
91104
Includes all the fields on the user, but replaces the password field
92105
with admin's password hash display field.
93-
94106
"""
95107

96108
password = ReadOnlyPasswordHashField(
97109
label=_("Password"),
98110
help_text=_(
99-
"Raw passwords are not stored, so there is no way to see "
100-
"this user's password, but you can change the password "
101-
'using <a href="%(url)s">this form</a>.'
102-
)
103-
% {"url": "../password/"},
111+
"Raw passwords are not stored, so there is no way to see this "
112+
"user's password, but you can change the password using "
113+
'<a href="{}">this form</a>.'
114+
),
104115
)
105116

106-
class Meta: # noqa: D101
117+
class Meta:
107118
model = get_user_model()
108119
exclude = ()
109120

110121
def __init__(self, *args, **kwargs):
111-
"""Init the form."""
112-
super(EmailUserChangeForm, self).__init__(*args, **kwargs)
113-
f = self.fields.get("user_permissions", None)
114-
if f is not None:
115-
f.queryset = f.queryset.select_related("content_type")
116-
117-
def clean_password(self):
118-
"""
119-
Clean password.
120-
121-
Regardless of what the user provides, return the initial value.
122-
This is done here, rather than on the field, because the
123-
field does not have access to the initial value.
124-
125-
:return str password:
126-
127-
"""
128-
return self.initial["password"]
122+
super().__init__(*args, **kwargs)
123+
password = self.fields.get("password")
124+
if password:
125+
password.help_text = password.help_text.format("../password/")
126+
user_permissions = self.fields.get("user_permissions")
127+
if user_permissions:
128+
user_permissions.queryset = user_permissions.queryset.select_related(
129+
"content_type"
130+
)

src/custom_user/models.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111

1212

1313
class EmailUserManager(BaseUserManager):
14-
15-
"""Custom manager for EmailUser."""
14+
"""
15+
Custom manager for EmailUser.
16+
"""
1617

1718
def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
1819
"""
@@ -24,7 +25,6 @@ def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
2425
:param bool is_superuser: whether user admin or not
2526
:return custom_user.models.EmailUser user: user
2627
:raise ValueError: email is not set
27-
2828
"""
2929
now = timezone.now()
3030
if not email:
@@ -51,25 +51,31 @@ def create_user(self, email, password=None, **extra_fields):
5151
:param str email: user email
5252
:param str password: user password
5353
:return custom_user.models.EmailUser user: regular user
54-
5554
"""
56-
is_staff = extra_fields.pop("is_staff", False)
57-
return self._create_user(email, password, is_staff, False, **extra_fields)
55+
extra_fields.setdefault("is_staff", False)
56+
extra_fields.setdefault("is_superuser", False)
57+
return self._create_user(email, password, **extra_fields)
5858

59-
def create_superuser(self, email, password, **extra_fields):
59+
def create_superuser(self, email, password=None, **extra_fields):
6060
"""
6161
Create and save an EmailUser with the given email and password.
6262
6363
:param str email: user email
6464
:param str password: user password
6565
:return custom_user.models.EmailUser user: admin user
66-
6766
"""
68-
return self._create_user(email, password, True, True, **extra_fields)
67+
extra_fields.setdefault("is_staff", True)
68+
extra_fields.setdefault("is_superuser", True)
6969

70+
if extra_fields.get("is_staff") is not True:
71+
raise ValueError("Superuser must have is_staff=True.")
72+
if extra_fields.get("is_superuser") is not True:
73+
raise ValueError("Superuser must have is_superuser=True.")
74+
75+
return self._create_user(email, password, **extra_fields)
7076

71-
class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
7277

78+
class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
7379
"""
7480
Abstract User with the same behaviour as Django's default User.
7581
@@ -84,7 +90,6 @@ class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
8490
* password
8591
* last_login
8692
* is_superuser
87-
8893
"""
8994

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

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

130135

131136
class EmailUser(AbstractEmailUser):
132-
133137
"""
134138
Concrete class of AbstractEmailUser.
135139
136140
Use this if you don't need to extend EmailUser.
137-
138141
"""
139142

140-
class Meta(AbstractEmailUser.Meta): # noqa: D101
143+
class Meta(AbstractEmailUser.Meta):
141144
swappable = "AUTH_USER_MODEL"

0 commit comments

Comments
 (0)