diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt index b4d4e7a7eb..e35179d8ae 100644 --- a/docs/topics/auth/customizing.txt +++ b/docs/topics/auth/customizing.txt @@ -733,47 +733,45 @@ the "Model design considerations" note of :ref:`specifying-custom-user-model`. Custom users and the built-in auth forms ---------------------------------------- -As you may expect, built-in Django's :ref:`forms ` and -:ref:`views ` make certain assumptions about the user -model that they are working with. +Django's built-in :ref:`forms ` and :ref:`views +` make certain assumptions about the user model that they +are working with. -If your user model doesn't follow the same assumptions, it may be necessary to define -a replacement form, and pass that form in as part of the configuration of the -auth views. - -* :class:`~django.contrib.auth.forms.UserCreationForm` - - Depends on the :class:`~django.contrib.auth.models.User` model. - Must be re-written for any custom user model. - -* :class:`~django.contrib.auth.forms.UserChangeForm` - - Depends on the :class:`~django.contrib.auth.models.User` model. - Must be re-written for any custom user model. - -* :class:`~django.contrib.auth.forms.AuthenticationForm` - - Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`, - and will adapt to use the field defined in ``USERNAME_FIELD``. - -* :class:`~django.contrib.auth.forms.PasswordResetForm` - - Assumes that the user model has a field named ``email`` that can be used to - identify the user and a boolean field named ``is_active`` to prevent - password resets for inactive users. +The following forms are compatible with any subclass of +:class:`~django.contrib.auth.models.AbstractBaseUser`: +* :class:`~django.contrib.auth.forms.AuthenticationForm`: Uses the username + field specified by :attr:`~models.CustomUser.USERNAME_FIELD`. * :class:`~django.contrib.auth.forms.SetPasswordForm` - - Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser` - * :class:`~django.contrib.auth.forms.PasswordChangeForm` - - Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser` - * :class:`~django.contrib.auth.forms.AdminPasswordChangeForm` - Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser` +The following forms make assumptions about the user model and can be used as-is +if those assumptions are met: +* :class:`~django.contrib.auth.forms.PasswordResetForm`: Assumes that the user + model has a field named ``email`` 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/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py index 1885326cf9..c0afe2912d 100644 --- a/tests/auth_tests/test_forms.py +++ b/tests/auth_tests/test_forms.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +import datetime import re from django import forms @@ -19,6 +20,7 @@ from django.utils.encoding import force_text from django.utils.text import capfirst from django.utils.translation import ugettext as _ +from .models.custom_user import ExtensionUser from .settings import AUTH_TEMPLATES @@ -122,6 +124,21 @@ class UserCreationFormTest(TestDataMixin, TestCase): form['password2'].errors ) + def test_custom_form(self): + 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()) + class AuthenticationFormTest(TestDataMixin, TestCase): @@ -407,6 +424,24 @@ class UserChangeFormTest(TestDataMixin, TestCase): # value to render correctly self.assertEqual(form.initial['password'], form['password'].value()) + def test_custom_form(self): + class CustomUserChangeForm(UserChangeForm): + class Meta(UserChangeForm.Meta): + model = ExtensionUser + fields = ('username', 'password', 'date_of_birth',) + + 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)) + @override_settings(TEMPLATES=AUTH_TEMPLATES) class PasswordResetFormTest(TestDataMixin, TestCase):