浏览代码

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.
Berker Peksag 9 年之前
父节点
当前提交
f0425c7260
共有 2 个文件被更改,包括 64 次插入31 次删除
  1. 29 31
      docs/topics/auth/customizing.txt
  2. 35 0
      tests/auth_tests/test_forms.py

+ 29 - 31
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 <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
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):