summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2018-07-02 18:10:36 -0400
committerTim Graham <timograham@gmail.com>2018-07-02 18:57:12 -0400
commit78f502cd0bc834422c3f189e852564fe4b6459f2 (patch)
treeb52ddb2484cebb94be32e74ffdf5e71962b1c382
parentfb05b9432451d77890e24f66af35e9ca4a71867f (diff)
[2.1.x] Fixed #29449 -- Reverted "Fixed #28757 -- Allowed using contrib.auth forms without installing contrib.auth."
This reverts commit 3333d935d2914cd80cf31f4803821ad5c0e2a51d due to a crash if USERNAME_FIELD isn't a CharField. Backport of f3fa86a89b3b85242f49b2b9acf58b5ea35acc1f from master
-rw-r--r--django/contrib/auth/forms.py11
-rw-r--r--docs/topics/auth/customizing.txt32
-rw-r--r--docs/topics/auth/default.txt9
-rw-r--r--tests/auth_tests/test_forms.py140
4 files changed, 60 insertions, 132 deletions
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
index 6b9d2dd7b8..dda6a07f02 100644
--- a/django/contrib/auth/forms.py
+++ b/django/contrib/auth/forms.py
@@ -7,6 +7,7 @@ from django.contrib.auth import (
from django.contrib.auth.hashers import (
UNUSABLE_PASSWORD_PREFIX, identify_hasher,
)
+from django.contrib.auth.models import User
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMultiAlternatives
@@ -82,9 +83,9 @@ class UserCreationForm(forms.ModelForm):
)
class Meta:
- model = UserModel
- fields = (UserModel.USERNAME_FIELD,)
- field_classes = {UserModel.USERNAME_FIELD: UsernameField}
+ model = User
+ fields = ("username",)
+ field_classes = {'username': UsernameField}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -131,9 +132,9 @@ class UserChangeForm(forms.ModelForm):
)
class Meta:
- model = UserModel
+ model = User
fields = '__all__'
- field_classes = {UserModel.USERNAME_FIELD: UsernameField}
+ field_classes = {'username': UsernameField}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt
index 3dc5227751..534b2838bd 100644
--- a/docs/topics/auth/customizing.txt
+++ b/docs/topics/auth/customizing.txt
@@ -820,20 +820,11 @@ are working with.
The following forms are compatible with any subclass of
:class:`~django.contrib.auth.models.AbstractBaseUser`:
-* :class:`~django.contrib.auth.forms.AuthenticationForm`
+* :class:`~django.contrib.auth.forms.AuthenticationForm`: Uses the username
+ field specified by :attr:`~models.CustomUser.USERNAME_FIELD`.
* :class:`~django.contrib.auth.forms.SetPasswordForm`
* :class:`~django.contrib.auth.forms.PasswordChangeForm`
* :class:`~django.contrib.auth.forms.AdminPasswordChangeForm`
-* :class:`~django.contrib.auth.forms.UserCreationForm`
-* :class:`~django.contrib.auth.forms.UserChangeForm`
-
-The forms that handle a username use the username field specified by
-:attr:`~models.CustomUser.USERNAME_FIELD`.
-
-.. versionchanged:: 2.1
-
- In older versions, ``UserCreationForm`` and ``UserChangeForm`` need to be
- rewritten to work with custom user models.
The following forms make assumptions about the user model and can be used as-is
if those assumptions are met:
@@ -844,6 +835,25 @@ if those assumptions are met:
default) that can be used to identify the user and a boolean field named
``is_active`` to prevent password resets for inactive users.
+Finally, the following forms are tied to
+:class:`~django.contrib.auth.models.User` and need to be rewritten or extended
+to work with a custom user model:
+
+* :class:`~django.contrib.auth.forms.UserCreationForm`
+* :class:`~django.contrib.auth.forms.UserChangeForm`
+
+If your custom user model is a simple subclass of ``AbstractUser``, then you
+can extend these forms in this manner::
+
+ from django.contrib.auth.forms import UserCreationForm
+ from myapp.models import CustomUser
+
+ class CustomUserCreationForm(UserCreationForm):
+
+ class Meta(UserCreationForm.Meta):
+ model = CustomUser
+ fields = UserCreationForm.Meta.fields + ('custom_field',)
+
Custom users and :mod:`django.contrib.admin`
--------------------------------------------
diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt
index 94a8fc592f..4fcd465d89 100644
--- a/docs/topics/auth/default.txt
+++ b/docs/topics/auth/default.txt
@@ -1508,12 +1508,9 @@ provides several built-in forms located in :mod:`django.contrib.auth.forms`:
A :class:`~django.forms.ModelForm` for creating a new user.
- It has three fields: one named after the
- :attr:`~django.contrib.auth.models.CustomUser.USERNAME_FIELD` from the
- user model, and ``password1`` and ``password2``.
-
- It verifies that ``password1`` and ``password2`` match, validates the
- password using
+ It has three fields: ``username`` (from the user model), ``password1``,
+ and ``password2``. It verifies that ``password1`` and ``password2`` match,
+ validates the password using
:func:`~django.contrib.auth.password_validation.validate_password`, and
sets the user's password using
:meth:`~django.contrib.auth.models.User.set_password()`.
diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py
index 52d61cfe87..825138755d 100644
--- a/tests/auth_tests/test_forms.py
+++ b/tests/auth_tests/test_forms.py
@@ -1,9 +1,7 @@
import datetime
import re
-from importlib import reload
from unittest import mock
-import django
from django import forms
from django.contrib.auth.forms import (
AdminPasswordChangeForm, AuthenticationForm, PasswordChangeForm,
@@ -13,7 +11,7 @@ from django.contrib.auth.forms import (
from django.contrib.auth.models import User
from django.contrib.auth.signals import user_login_failed
from django.contrib.sites.models import Site
-from django.core import mail, signals
+from django.core import mail
from django.core.mail import EmailMultiAlternatives
from django.forms.fields import CharField, Field, IntegerField
from django.test import SimpleTestCase, TestCase, override_settings
@@ -29,24 +27,6 @@ from .models.with_integer_username import IntegerUsernameUser
from .settings import AUTH_TEMPLATES
-def reload_auth_forms(sender, setting, value, enter, **kwargs):
- if setting == 'AUTH_USER_MODEL':
- reload(django.contrib.auth.forms)
-
-
-class ReloadFormsMixin:
-
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- signals.setting_changed.connect(reload_auth_forms)
-
- @classmethod
- def tearDownClass(cls):
- signals.setting_changed.disconnect(reload_auth_forms)
- super().tearDownClass()
-
-
class TestDataMixin:
@classmethod
@@ -57,10 +37,9 @@ class TestDataMixin:
cls.u4 = User.objects.create(username='empty_password', password='')
cls.u5 = User.objects.create(username='unmanageable_password', password='$')
cls.u6 = User.objects.create(username='unknown_password', password='foo$bar')
- cls.u7 = ExtensionUser.objects.create(username='extension_client', date_of_birth='1998-02-24')
-class UserCreationFormTest(ReloadFormsMixin, TestDataMixin, TestCase):
+class UserCreationFormTest(TestDataMixin, TestCase):
def test_user_already_exists(self):
data = {
@@ -196,25 +175,19 @@ class UserCreationFormTest(ReloadFormsMixin, TestDataMixin, TestCase):
)
def test_custom_form(self):
- with override_settings(AUTH_USER_MODEL='auth_tests.ExtensionUser'):
- from django.contrib.auth.forms import UserCreationForm
- self.assertEqual(UserCreationForm.Meta.model, ExtensionUser)
-
- class CustomUserCreationForm(UserCreationForm):
- class Meta(UserCreationForm.Meta):
- fields = UserCreationForm.Meta.fields + ('date_of_birth',)
+ class CustomUserCreationForm(UserCreationForm):
+ class Meta(UserCreationForm.Meta):
+ model = ExtensionUser
+ fields = UserCreationForm.Meta.fields + ('date_of_birth',)
- data = {
- 'username': 'testclient',
- 'password1': 'testclient',
- 'password2': 'testclient',
- 'date_of_birth': '1988-02-24',
- }
- form = CustomUserCreationForm(data)
- self.assertTrue(form.is_valid())
- # reload_auth_forms() reloads the form.
- from django.contrib.auth.forms import UserCreationForm
- self.assertEqual(UserCreationForm.Meta.model, User)
+ data = {
+ 'username': 'testclient',
+ 'password1': 'testclient',
+ 'password2': 'testclient',
+ 'date_of_birth': '1988-02-24',
+ }
+ form = CustomUserCreationForm(data)
+ self.assertTrue(form.is_valid())
def test_custom_form_with_different_username_field(self):
class CustomUserCreationForm(UserCreationForm):
@@ -288,30 +261,6 @@ class UserCreationFormTest(ReloadFormsMixin, TestDataMixin, TestCase):
['The password is too similar to the first name.'],
)
- def test_with_custom_user_model(self):
- with override_settings(AUTH_USER_MODEL='auth_tests.ExtensionUser'):
- data = {
- 'username': 'test_username',
- 'password1': 'test_password',
- 'password2': 'test_password',
- }
- from django.contrib.auth.forms import UserCreationForm
- self.assertEqual(UserCreationForm.Meta.model, ExtensionUser)
- form = UserCreationForm(data)
- self.assertTrue(form.is_valid())
-
- def test_customer_user_model_with_different_username_field(self):
- with override_settings(AUTH_USER_MODEL='auth_tests.CustomUser'):
- from django.contrib.auth.forms import UserCreationForm
- self.assertEqual(UserCreationForm.Meta.model, CustomUser)
- data = {
- 'email': 'testchange@test.com',
- 'password1': 'test_password',
- 'password2': 'test_password',
- }
- form = UserCreationForm(data)
- self.assertTrue(form.is_valid())
-
# To verify that the login form rejects inactive users, use an authentication
# backend that allows them.
@@ -677,7 +626,7 @@ class PasswordChangeFormTest(TestDataMixin, TestCase):
self.assertEqual(form.cleaned_data['new_password2'], data['new_password2'])
-class UserChangeFormTest(ReloadFormsMixin, TestDataMixin, TestCase):
+class UserChangeFormTest(TestDataMixin, TestCase):
def test_username_validity(self):
user = User.objects.get(username='testclient')
@@ -751,51 +700,22 @@ class UserChangeFormTest(ReloadFormsMixin, TestDataMixin, TestCase):
self.assertEqual(form.initial['password'], form['password'].value())
def test_custom_form(self):
- with override_settings(AUTH_USER_MODEL='auth_tests.ExtensionUser'):
- from django.contrib.auth.forms import UserChangeForm
- self.assertEqual(UserChangeForm.Meta.model, ExtensionUser)
-
- class CustomUserChangeForm(UserChangeForm):
- class Meta(UserChangeForm.Meta):
- fields = ('username', 'password', 'date_of_birth')
-
- data = {
- 'username': 'testclient',
- 'password': 'testclient',
- 'date_of_birth': '1998-02-24',
- }
- form = CustomUserChangeForm(data, instance=self.u7)
- self.assertTrue(form.is_valid())
- form.save()
- self.assertEqual(form.cleaned_data['username'], 'testclient')
- self.assertEqual(form.cleaned_data['date_of_birth'], datetime.date(1998, 2, 24))
- # reload_auth_forms() reloads the form.
- from django.contrib.auth.forms import UserChangeForm
- self.assertEqual(UserChangeForm.Meta.model, User)
-
- def test_with_custom_user_model(self):
- with override_settings(AUTH_USER_MODEL='auth_tests.ExtensionUser'):
- from django.contrib.auth.forms import UserChangeForm
- self.assertEqual(UserChangeForm.Meta.model, ExtensionUser)
- data = {
- 'username': 'testclient',
- 'date_joined': '1998-02-24',
- 'date_of_birth': '1998-02-24',
- }
- form = UserChangeForm(data, instance=self.u7)
- self.assertTrue(form.is_valid())
+ class CustomUserChangeForm(UserChangeForm):
+ class Meta(UserChangeForm.Meta):
+ model = ExtensionUser
+ fields = ('username', 'password', 'date_of_birth',)
- def test_customer_user_model_with_different_username_field(self):
- with override_settings(AUTH_USER_MODEL='auth_tests.CustomUser'):
- from django.contrib.auth.forms import UserChangeForm
- self.assertEqual(UserChangeForm.Meta.model, CustomUser)
- user = CustomUser.custom_objects.create(email='test@test.com', date_of_birth='1998-02-24')
- data = {
- 'email': 'testchange@test.com',
- 'date_of_birth': '1998-02-24',
- }
- form = UserChangeForm(data, instance=user)
- self.assertTrue(form.is_valid())
+ user = User.objects.get(username='testclient')
+ data = {
+ 'username': 'testclient',
+ 'password': 'testclient',
+ 'date_of_birth': '1998-02-24',
+ }
+ form = CustomUserChangeForm(data, instance=user)
+ self.assertTrue(form.is_valid())
+ form.save()
+ self.assertEqual(form.cleaned_data['username'], 'testclient')
+ self.assertEqual(form.cleaned_data['date_of_birth'], datetime.date(1998, 2, 24))
def test_password_excluded(self):
class UserChangeFormWithoutPassword(UserChangeForm):