Commit f0425c72 authored by Berker Peksag's avatar Berker Peksag Committed by Tim Graham
Browse files

Refs #19353 -- Added tests for using custom user models with built-in auth forms.

Also updated topics/auth/customizing.txt to reflect that subclasses of
UserCreationForm and UserChangeForm can be used with custom user models.

Thanks Baptiste Mispelon for the initial documentation.
parent d4dc7756
Loading
Loading
Loading
Loading
+29 −31
Original line number Diff line number Diff line
@@ -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 <built-in-auth-forms>` and
:ref:`views <built-in-auth-views>` make certain assumptions about the user
model that they are working with.
Django's built-in :ref:`forms <built-in-auth-forms>` and :ref:`views
<built-in-auth-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``.
The following forms are compatible with any subclass of
:class:`~django.contrib.auth.models.AbstractBaseUser`:

* :class:`~django.contrib.auth.forms.PasswordResetForm`
* :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`

  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 make assumptions about the user model and can be used as-is
if those assumptions are met:

* :class:`~django.contrib.auth.forms.SetPasswordForm`
* :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.

  Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
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.PasswordChangeForm`
* :class:`~django.contrib.auth.forms.UserCreationForm`
* :class:`~django.contrib.auth.forms.UserChangeForm`

  Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
If your custom user model is a simple subclass of ``AbstractUser``, then you
can extend these forms in this manner::

* :class:`~django.contrib.auth.forms.AdminPasswordChangeForm`
    from django.contrib.auth.forms import UserCreationForm
    from myapp.models import CustomUser

  Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
    class CustomUserCreationForm(UserCreationForm):

        class Meta(UserCreationForm.Meta):
            model = CustomUser
            fields = UserCreationForm.Meta.fields + ('custom_field',)

Custom users and :mod:`django.contrib.admin`
--------------------------------------------
+35 −0
Original line number Diff line number Diff line
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):