Skip to content

Commit

Permalink
Added support for Django 5.1
Browse files Browse the repository at this point in the history
  • Loading branch information
ataylor32 committed Aug 10, 2024
1 parent 88e234f commit 541f25e
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 66 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ jobs:
tox-env: py311-django50
- python-version: "3.12"
tox-env: py312-django50
- python-version: "3.10"
tox-env: py310-django51
- python-version: "3.11"
tox-env: py311-django51
- python-version: "3.12"
tox-env: py312-django51
steps:
- uses: actions/checkout@v3

Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ takes a tiny amount of effort to use.
The only difference between CUser and the vanilla Django ``User`` is email
address is the ``USERNAME_FIELD`` (and username does not exist).

CUser supports Django 3.2 - 5.0. If you need to use CUser with
CUser supports Django 3.2 - 5.1. If you need to use CUser with
Django 1.8 - 3.1, you must install an older, unmaintained version of
CUser, as noted in the "Install & Set up" section.

Expand Down Expand Up @@ -53,7 +53,7 @@ that, you may follow the remaining steps below just the way they are.

.. code-block:: shell
# Django 3.2 - 5.0
# Django 3.2 - 5.1
pip install django-username-email
# Django 3.1 (unmaintained)
Expand Down
23 changes: 17 additions & 6 deletions cuser/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import django
from django.contrib import admin
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
Expand All @@ -19,18 +20,28 @@ class UserAdmin(BaseUserAdmin):
'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2'),
}),
)
form = UserChangeForm
add_form = UserCreationForm
list_display = ('email', 'first_name', 'last_name', 'is_staff')
search_fields = ('email', 'first_name', 'last_name')
ordering = ('email',)

def get_fieldsets(self, request, obj=None):
if not obj:
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ['email', 'password1', 'password2'],
}),
)

if django.VERSION >= (5, 1):
add_fieldsets[0][1]['fields'].insert(1, 'usable_password')

return add_fieldsets

return super().get_fieldsets(request, obj)


if CUSER_SETTINGS['register_proxy_auth_group_model']:
admin.site.unregister(StockGroup)
Expand Down
150 changes: 94 additions & 56 deletions cuser/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,65 +86,103 @@ def get_invalid_login_error(self):
)


class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given email and
password.
"""
error_messages = {
'password_mismatch': _('The two password fields didn’t match.'),
}
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(attrs={'autofocus': True}),
)
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(attrs={'autocomplete': 'new-password'}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)

class Meta:
model = CUser
fields = ("email",)
if django.VERSION >= (5, 1):
from django.contrib.auth.forms import SetPasswordMixin

def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
class UserCreationForm(SetPasswordMixin, forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given email and
password.
"""

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 forms.ValidationError as error:
self.add_error('password2', error)

def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
if django.VERSION >= (4, 2) and hasattr(self, "save_m2m"):
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(attrs={'autofocus': True}),
)
password1, password2 = SetPasswordMixin.create_password_fields()
usable_password = SetPasswordMixin.create_usable_password_field()

class Meta:
model = CUser
fields = ("email",)

def clean(self):
self.validate_passwords()
return super().clean()

def _post_clean(self):
super()._post_clean()
# Validate the password after self.instance is updated with form data
# by super().
self.validate_password_for_user(self.instance)

def save(self, commit=True):
user = super().save(commit=False)
user = self.set_password_and_save(user, commit=commit)
if commit and hasattr(self, "save_m2m"):
self.save_m2m()
return user
return user
else:
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given email and
password.
"""
error_messages = {
'password_mismatch': _('The two password fields didn’t match.'),
}
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(attrs={'autofocus': True}),
)
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(attrs={'autocomplete': 'new-password'}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)

class Meta:
model = CUser
fields = ("email",)

def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.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 forms.ValidationError as error:
self.add_error('password2', error)

def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
if django.VERSION >= (4, 2) and hasattr(self, "save_m2m"):
self.save_m2m()
return user


class UserChangeForm(forms.ModelForm):
Expand Down
2 changes: 1 addition & 1 deletion cuser/templates/admin/cuser/cuser/add_form.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends "admin/change_form.html" %}
{% extends "admin/auth/user/add_form.html" %}
{% load i18n %}

{% block form_top %}
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
'Framework :: Django :: 4.1',
'Framework :: Django :: 4.2',
'Framework :: Django :: 5.0',
'Framework :: Django :: 5.1',
'Operating System :: OS Independent',
],
keywords='user email username',
Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ envlist =
py{38,39,310}-django40
py{38,39,310,311}-django41
py{38,39,310,311,312}-django42
py{310,311,312}-django50
py{310,311,312}-django{50,51}

[testenv]
deps =
Expand All @@ -13,6 +13,7 @@ deps =
django41: Django>=4.1,<4.2
django42: Django>=4.2,<5.0
django50: Django>=5.0,<5.1
django51: Django>=5.1,<5.2
py36: pylint==2.9.6
py{37,38,39,310,311,312}: pylint==2.17.2
commands =
Expand Down

0 comments on commit 541f25e

Please sign in to comment.