summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2016-09-30 22:06:02 +0200
committerMarkus Holtermann <info@markusholtermann.eu>2016-11-25 14:15:49 +0100
commitcb7bbf97a74fa7800865e3615f196ad65dc4f281 (patch)
tree9b22a2a0acdcac0b8ce933349d9738fb08c729f3
parenteb42d8d5d9b0968ce12d09760afff684bee56a3a (diff)
Fixed #25966 -- Made get_user_model() work at import time.
This makes it equivalent to: `from django.contrib.auth.models import User`. Thanks Aymeric Augustin for the initial patch and Tim Graham for the review.
-rw-r--r--django/contrib/auth/__init__.py2
-rw-r--r--django/contrib/auth/backends.py6
-rw-r--r--django/contrib/auth/forms.py4
-rw-r--r--django/contrib/auth/handlers/modwsgi.py4
-rw-r--r--django/contrib/auth/management/commands/changepassword.py4
-rw-r--r--django/contrib/auth/views.py4
-rw-r--r--django/test/signals.py22
-rw-r--r--docs/ref/applications.txt4
-rw-r--r--docs/releases/1.11.txt3
-rw-r--r--docs/topics/auth/customizing.txt30
10 files changed, 63 insertions, 20 deletions
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index 7adb8a022f..04a7f7ddcb 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -172,7 +172,7 @@ def get_user_model():
Returns the User model that is active in this project.
"""
try:
- return django_apps.get_model(settings.AUTH_USER_MODEL)
+ return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False)
except ValueError:
raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
except LookupError:
diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py
index 379f1c4450..8df95db5ee 100644
--- a/django/contrib/auth/backends.py
+++ b/django/contrib/auth/backends.py
@@ -3,6 +3,8 @@ from __future__ import unicode_literals
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
+UserModel = get_user_model()
+
class ModelBackend(object):
"""
@@ -10,7 +12,6 @@ class ModelBackend(object):
"""
def authenticate(self, request, username=None, password=None, **kwargs):
- UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
@@ -97,7 +98,6 @@ class ModelBackend(object):
return False
def get_user(self, user_id):
- UserModel = get_user_model()
try:
user = UserModel._default_manager.get(pk=user_id)
except UserModel.DoesNotExist:
@@ -139,8 +139,6 @@ class RemoteUserBackend(ModelBackend):
user = None
username = self.clean_username(remote_user)
- UserModel = get_user_model()
-
# Note that this could be accomplished in one try-except clause, but
# instead we use get_or_create when creating unknown users since it has
# built-in safeguards for multiple threads.
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
index dbdc08db64..a5d0375f58 100644
--- a/django/contrib/auth/forms.py
+++ b/django/contrib/auth/forms.py
@@ -22,6 +22,8 @@ from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.translation import ugettext, ugettext_lazy as _
+UserModel = get_user_model()
+
class ReadOnlyPasswordHashWidget(forms.Widget):
def render(self, name, value, attrs):
@@ -179,7 +181,6 @@ class AuthenticationForm(forms.Form):
super(AuthenticationForm, self).__init__(*args, **kwargs)
# Set the label for the "username" field.
- UserModel = get_user_model()
self.username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
if self.fields['username'].label is None:
self.fields['username'].label = capfirst(self.username_field.verbose_name)
@@ -254,7 +255,6 @@ class PasswordResetForm(forms.Form):
that prevent inactive users and users with unusable passwords from
resetting their password.
"""
- UserModel = get_user_model()
active_users = UserModel._default_manager.filter(**{
'%s__iexact' % UserModel.get_email_field_name(): email,
'is_active': True,
diff --git a/django/contrib/auth/handlers/modwsgi.py b/django/contrib/auth/handlers/modwsgi.py
index 6b91ed9c6e..6bd29e79b1 100644
--- a/django/contrib/auth/handlers/modwsgi.py
+++ b/django/contrib/auth/handlers/modwsgi.py
@@ -2,6 +2,8 @@ from django import db
from django.contrib import auth
from django.utils.encoding import force_bytes
+UserModel = auth.get_user_model()
+
def check_password(environ, username, password):
"""
@@ -11,7 +13,6 @@ def check_password(environ, username, password):
on whether the user exists and authenticates.
"""
- UserModel = auth.get_user_model()
# db connection state is managed similarly to the wsgi handler
# as mod_wsgi may call these functions outside of a request/response cycle
db.reset_queries()
@@ -33,7 +34,6 @@ def groups_for_user(environ, username):
Authorizes a user based on groups
"""
- UserModel = auth.get_user_model()
db.reset_queries()
try:
diff --git a/django/contrib/auth/management/commands/changepassword.py b/django/contrib/auth/management/commands/changepassword.py
index 173ea2ae27..1c4eee3b91 100644
--- a/django/contrib/auth/management/commands/changepassword.py
+++ b/django/contrib/auth/management/commands/changepassword.py
@@ -9,6 +9,8 @@ from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS
from django.utils.encoding import force_str
+UserModel = get_user_model()
+
class Command(BaseCommand):
help = "Change a user's password for django.contrib.auth."
@@ -38,8 +40,6 @@ class Command(BaseCommand):
else:
username = getpass.getuser()
- UserModel = get_user_model()
-
try:
u = UserModel._default_manager.using(options['database']).get(**{
UserModel.USERNAME_FIELD: username
diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
index 08c3510988..8edf02a7d1 100644
--- a/django/contrib/auth/views.py
+++ b/django/contrib/auth/views.py
@@ -31,6 +31,8 @@ from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView
+UserModel = get_user_model()
+
def deprecate_current_app(func):
"""
@@ -320,7 +322,6 @@ def password_reset_confirm(request, uidb64=None, token=None,
warnings.warn("The password_reset_confirm() view is superseded by the "
"class-based PasswordResetConfirmView().",
RemovedInDjango21Warning, stacklevel=2)
- UserModel = get_user_model()
assert uidb64 is not None and token is not None # checked by URLconf
if post_reset_redirect is None:
post_reset_redirect = reverse('password_reset_complete')
@@ -453,7 +454,6 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView):
return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs)
def get_user(self, uidb64):
- UserModel = get_user_model()
try:
# urlsafe_base64_decode() decodes to bytestring on Python 3
uid = force_text(urlsafe_base64_decode(uidb64))
diff --git a/django/test/signals.py b/django/test/signals.py
index 98506609e1..371b90c9ee 100644
--- a/django/test/signals.py
+++ b/django/test/signals.py
@@ -4,6 +4,7 @@ import time
import warnings
from django.apps import apps
+from django.core.exceptions import ImproperlyConfigured
from django.core.signals import setting_changed
from django.db import connections, router
from django.db.utils import ConnectionRouter
@@ -172,3 +173,24 @@ def auth_password_validators_changed(**kwargs):
def user_model_swapped(**kwargs):
if kwargs['setting'] == 'AUTH_USER_MODEL':
apps.clear_cache()
+ try:
+ from django.contrib.auth import get_user_model
+ UserModel = get_user_model()
+ except ImproperlyConfigured:
+ # Some tests set an invalid AUTH_USER_MODEL.
+ pass
+ else:
+ from django.contrib.auth import backends
+ backends.UserModel = UserModel
+
+ from django.contrib.auth import forms
+ forms.UserModel = UserModel
+
+ from django.contrib.auth.handlers import modwsgi
+ modwsgi.UserModel = UserModel
+
+ from django.contrib.auth.management.commands import changepassword
+ changepassword.UserModel = UserModel
+
+ from django.contrib.auth import views
+ views.UserModel = UserModel
diff --git a/docs/ref/applications.txt b/docs/ref/applications.txt
index 149312aafd..c3a06ab855 100644
--- a/docs/ref/applications.txt
+++ b/docs/ref/applications.txt
@@ -473,10 +473,6 @@ Here are some common problems that you may encounter during initialization:
will also trigger this exception. The ORM cannot function properly until all
models are available.
- Another common culprit is :func:`django.contrib.auth.get_user_model()`. Use
- the :setting:`AUTH_USER_MODEL` setting to reference the User model at import
- time.
-
This exception also happens if you forget to call :func:`django.setup()` in
a standalone Python script.
diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt
index 53265272ca..c8c64ab9c9 100644
--- a/docs/releases/1.11.txt
+++ b/docs/releases/1.11.txt
@@ -125,6 +125,9 @@ Minor features
Set :attr:`CustomUser.EMAIL_FIELD
<django.contrib.auth.models.CustomUser.EMAIL_FIELD>` to the name of the field.
+* :func:`~django.contrib.auth.get_user_model` can now be called at import time,
+ even in modules that define models.
+
:mod:`django.contrib.contenttypes`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt
index 1b9dc6539e..79ffa0a88b 100644
--- a/docs/topics/auth/customizing.txt
+++ b/docs/topics/auth/customizing.txt
@@ -487,9 +487,33 @@ different user model.
post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)
- Generally speaking, you should reference the user model with the
- :setting:`AUTH_USER_MODEL` setting in code that is executed at import
- time. ``get_user_model()`` only works once Django has imported all models.
+ Generally speaking, it's easiest to refer to the user model with the
+ :setting:`AUTH_USER_MODEL` setting in code that's executed at import time,
+ however, it's also possible to call ``get_user_model()`` while Django
+ is importing models, so you could use
+ ``models.ForeignKey(get_user_model(), ...)``.
+
+ If your app is tested with multiple user models, using
+ ``@override_settings(AUTH_USER_MODEL=...)`` for example, and you cache the
+ result of ``get_user_model()`` in a module-level variable, you may need to
+ listen to the :data:`~django.test.signals.setting_changed` signal to clear
+ the cache. For example::
+
+ from django.apps import apps
+ from django.contrib.auth import get_user_model
+ from django.core.signals import setting_changed
+ from django.dispatch import receiver
+
+ @receiver(setting_changed)
+ def user_model_swapped(**kwargs):
+ if kwargs['setting'] == 'AUTH_USER_MODEL':
+ apps.clear_cache()
+ from myapp import some_module
+ some_module.UserModel = get_user_model()
+
+ .. versionchanged:: 1.11
+
+ The ability to call ``get_user_model()`` at import time was added.
.. _specifying-custom-user-model: