summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2016-04-02 13:18:26 +0200
committerTim Graham <timograham@gmail.com>2016-04-09 14:54:18 -0400
commitc1aec0feda73ede09503192a66f973598aef901d (patch)
treef1e4c09f3e98177cfe78cc9039b300f8984e7aed
parentc16b9dd8e0ae757616e9accbaffecc521191ee98 (diff)
Fixed #25847 -- Made User.is_(anonymous|authenticated) properties.
-rw-r--r--AUTHORS1
-rw-r--r--django/contrib/auth/__init__.py2
-rw-r--r--django/contrib/auth/backends.py4
-rw-r--r--django/contrib/auth/base_user.py7
-rw-r--r--django/contrib/auth/decorators.py2
-rw-r--r--django/contrib/auth/middleware.py4
-rw-r--r--django/contrib/auth/mixins.py2
-rw-r--r--django/contrib/auth/models.py7
-rw-r--r--django/contrib/auth/views.py2
-rw-r--r--django/contrib/flatpages/templatetags/flatpages.py2
-rw-r--r--django/contrib/flatpages/views.py2
-rw-r--r--django/utils/deprecation.py30
-rw-r--r--docs/howto/error-reporting.txt2
-rw-r--r--docs/internals/deprecation.txt3
-rw-r--r--docs/ref/contrib/auth.txt73
-rw-r--r--docs/ref/request-response.txt4
-rw-r--r--docs/releases/1.10.txt35
-rw-r--r--docs/topics/auth/customizing.txt38
-rw-r--r--docs/topics/auth/default.txt12
-rw-r--r--docs/topics/cache.txt2
-rw-r--r--docs/topics/class-based-views/mixins.txt6
-rw-r--r--tests/auth_tests/test_auth_backends.py19
-rw-r--r--tests/auth_tests/test_basic.py48
-rw-r--r--tests/auth_tests/test_middleware.py4
-rw-r--r--tests/auth_tests/test_mixins.py4
-rw-r--r--tests/auth_tests/test_remote_user.py14
-rw-r--r--tests/urlpatterns_reverse/views.py2
27 files changed, 238 insertions, 93 deletions
diff --git a/AUTHORS b/AUTHORS
index 537d11212d..c7ed5afd6f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -345,6 +345,7 @@ answer newbie questions, and generally made Django that much better:
Jérémie Blaser <blaserje@gmail.com>
Jeremy Carbaugh <jcarbaugh@gmail.com>
Jeremy Dunck <jdunck@gmail.com>
+ Jeremy Lainé <jeremy.laine@m4x.org>
Jesse Young <adunar@gmail.com>
jhenry <jhenry@theonion.com>
Jim Dalton <jim.dalton@gmail.com>
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index 734c03731a..4f4f08987a 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -138,7 +138,7 @@ def logout(request):
# Dispatch the signal before the user is logged out so the receivers have a
# chance to find out *who* logged out.
user = getattr(request, 'user', None)
- if hasattr(user, 'is_authenticated') and not user.is_authenticated():
+ if hasattr(user, 'is_authenticated') and not user.is_authenticated:
user = None
user_logged_out.send(sender=user.__class__, request=request, user=user)
diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py
index 43d8627d0c..f145a1929a 100644
--- a/django/contrib/auth/backends.py
+++ b/django/contrib/auth/backends.py
@@ -45,7 +45,7 @@ class ModelBackend(object):
be either "group" or "user" to return permissions from
`_get_group_permissions` or `_get_user_permissions` respectively.
"""
- if not user_obj.is_active or user_obj.is_anonymous() or obj is not None:
+ if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
perm_cache_name = '_%s_perm_cache' % from_name
@@ -73,7 +73,7 @@ class ModelBackend(object):
return self._get_permissions(user_obj, obj, 'group')
def get_all_permissions(self, user_obj, obj=None):
- if not user_obj.is_active or user_obj.is_anonymous() or obj is not None:
+ if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = self.get_user_permissions(user_obj)
diff --git a/django/contrib/auth/base_user.py b/django/contrib/auth/base_user.py
index 48ab950c43..c8f748cf02 100644
--- a/django/contrib/auth/base_user.py
+++ b/django/contrib/auth/base_user.py
@@ -10,6 +10,7 @@ from django.contrib.auth.hashers import (
)
from django.db import models
from django.utils.crypto import get_random_string, salted_hmac
+from django.utils.deprecation import CallableFalse, CallableTrue
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
@@ -79,19 +80,21 @@ class AbstractBaseUser(models.Model):
def natural_key(self):
return (self.get_username(),)
+ @property
def is_anonymous(self):
"""
Always return False. This is a way of comparing User objects to
anonymous users.
"""
- return False
+ return CallableFalse
+ @property
def is_authenticated(self):
"""
Always return True. This is a way to tell if the user has been
authenticated in templates.
"""
- return True
+ return CallableTrue
def set_password(self, raw_password):
self.password = make_password(raw_password)
diff --git a/django/contrib/auth/decorators.py b/django/contrib/auth/decorators.py
index 6ab428e9cd..9c44108c89 100644
--- a/django/contrib/auth/decorators.py
+++ b/django/contrib/auth/decorators.py
@@ -43,7 +43,7 @@ def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login
to the log-in page if necessary.
"""
actual_decorator = user_passes_test(
- lambda u: u.is_authenticated(),
+ lambda u: u.is_authenticated,
login_url=login_url,
redirect_field_name=redirect_field_name
)
diff --git a/django/contrib/auth/middleware.py b/django/contrib/auth/middleware.py
index c829038f89..476729c147 100644
--- a/django/contrib/auth/middleware.py
+++ b/django/contrib/auth/middleware.py
@@ -70,13 +70,13 @@ class RemoteUserMiddleware(object):
# If specified header doesn't exist then remove any existing
# authenticated remote-user, or return (leaving request.user set to
# AnonymousUser by the AuthenticationMiddleware).
- if self.force_logout_if_no_header and request.user.is_authenticated():
+ if self.force_logout_if_no_header and request.user.is_authenticated:
self._remove_invalid_user(request)
return
# If the user is already authenticated and that user is the user we are
# getting passed in the headers, then the correct user is already
# persisted in the session and we don't need to continue.
- if request.user.is_authenticated():
+ if request.user.is_authenticated:
if request.user.get_username() == self.clean_username(username, request):
return
else:
diff --git a/django/contrib/auth/mixins.py b/django/contrib/auth/mixins.py
index d98c909112..4a7759435b 100644
--- a/django/contrib/auth/mixins.py
+++ b/django/contrib/auth/mixins.py
@@ -51,7 +51,7 @@ class LoginRequiredMixin(AccessMixin):
CBV mixin which verifies that the current user is authenticated.
"""
def dispatch(self, request, *args, **kwargs):
- if not request.user.is_authenticated():
+ if not request.user.is_authenticated:
return self.handle_no_permission()
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index 0600b96774..2f3e297ba6 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -10,6 +10,7 @@ from django.core.mail import send_mail
from django.db import models
from django.db.models.manager import EmptyManager
from django.utils import six, timezone
+from django.utils.deprecation import CallableFalse, CallableTrue
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
@@ -438,11 +439,13 @@ class AnonymousUser(object):
def has_module_perms(self, module):
return _user_has_module_perms(self, module)
+ @property
def is_anonymous(self):
- return True
+ return CallableTrue
+ @property
def is_authenticated(self):
- return False
+ return CallableFalse
def get_username(self):
return self.username
diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py
index ff6643b035..3cf7c36b8b 100644
--- a/django/contrib/auth/views.py
+++ b/django/contrib/auth/views.py
@@ -68,7 +68,7 @@ def login(request, template_name='registration/login.html',
"""
redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, ''))
- if redirect_authenticated_user and request.user.is_authenticated():
+ if redirect_authenticated_user and request.user.is_authenticated:
redirect_to = _get_login_redirect_url(request, redirect_to)
if redirect_to == request.path:
raise ValueError(
diff --git a/django/contrib/flatpages/templatetags/flatpages.py b/django/contrib/flatpages/templatetags/flatpages.py
index db82c4aa2c..3abe6f5433 100644
--- a/django/contrib/flatpages/templatetags/flatpages.py
+++ b/django/contrib/flatpages/templatetags/flatpages.py
@@ -33,7 +33,7 @@ class FlatpageNode(template.Node):
# was provided, filter the list to only public flatpages.
if self.user:
user = self.user.resolve(context)
- if not user.is_authenticated():
+ if not user.is_authenticated:
flatpages = flatpages.filter(registration_required=False)
else:
flatpages = flatpages.filter(registration_required=False)
diff --git a/django/contrib/flatpages/views.py b/django/contrib/flatpages/views.py
index 10e13a73be..6df0791837 100644
--- a/django/contrib/flatpages/views.py
+++ b/django/contrib/flatpages/views.py
@@ -52,7 +52,7 @@ def render_flatpage(request, f):
"""
# If registration is required for accessing this page, and the user isn't
# logged in, redirect to the login page.
- if f.registration_required and not request.user.is_authenticated():
+ if f.registration_required and not request.user.is_authenticated:
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(request.path)
if f.template_name:
diff --git a/django/utils/deprecation.py b/django/utils/deprecation.py
index 905bbc4f6d..70216e8c5b 100644
--- a/django/utils/deprecation.py
+++ b/django/utils/deprecation.py
@@ -79,3 +79,33 @@ class DeprecationInstanceCheck(type):
self.deprecation_warning, 2
)
return super(DeprecationInstanceCheck, self).__instancecheck__(instance)
+
+
+class CallableBool:
+ """
+ An boolean-like object that is also callable for backwards compatibility.
+ """
+ do_not_call_in_templates = True
+
+ def __init__(self, value):
+ self.value = value
+
+ def __bool__(self):
+ return self.value
+
+ def __call__(self):
+ warnings.warn(
+ "Using user.is_authenticated() and user.is_anonymous() as a method "
+ "is deprecated. Remove the parentheses to use it as an attribute.",
+ RemovedInDjango20Warning, stacklevel=2
+ )
+ return self.value
+
+ def __nonzero__(self): # Python 2 compatibility
+ return self.value
+
+ def __repr__(self):
+ return 'CallableBool(%r)' % self.value
+
+CallableFalse = CallableBool(False)
+CallableTrue = CallableBool(True)
diff --git a/docs/howto/error-reporting.txt b/docs/howto/error-reporting.txt
index 9ae194d4e0..d07404a1e9 100644
--- a/docs/howto/error-reporting.txt
+++ b/docs/howto/error-reporting.txt
@@ -256,7 +256,7 @@ given view by setting the ``HttpRequest``’s ``exception_reporter_filter``
attribute::
def my_view(request):
- if request.user.is_authenticated():
+ if request.user.is_authenticated:
request.exception_reporter_filter = CustomExceptionReporterFilter()
...
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 810f7d1a2b..d428f414f7 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -147,6 +147,9 @@ details on these changes.
* The shim for supporting custom related manager classes without a
``_apply_rel_filters()`` method will be removed.
+* Using ``User.is_authenticated()`` and ``User.is_anonymous()`` as methods
+ will no longer be supported.
+
.. _deprecation-removed-in-1.10:
1.10
diff --git a/docs/ref/contrib/auth.txt b/docs/ref/contrib/auth.txt
index 831de3b5a6..6e3b8892cf 100644
--- a/docs/ref/contrib/auth.txt
+++ b/docs/ref/contrib/auth.txt
@@ -111,6 +111,41 @@ Fields
A datetime designating when the account was created. Is set to the
current date/time by default when the account is created.
+Attributes
+----------
+
+.. class:: models.User
+
+ .. attribute:: is_authenticated
+
+ Read-only attribute which is always ``True`` (as opposed to
+ ``AnonymousUser.is_authenticated`` which is always ``False``). This is
+ a way to tell if the user has been authenticated. This does not imply
+ any permissions and doesn't check if the user is active or has a valid
+ session. Even though normally you will check this attribute on
+ ``request.user`` to find out whether it has been populated by the
+ :class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
+ (representing the currently logged-in user), you should know this
+ attribute is ``True`` for any :class:`~models.User` instance.
+
+ .. versionchanged:: 1.10
+
+ In older versions, this was a method. Backwards-compatibility
+ support for using it as a method will be removed in Django 2.0.
+
+ .. attribute:: is_anonymous
+
+ Read-only attribute which is always ``False``. This is a way of
+ differentiating :class:`~models.User` and :class:`~models.AnonymousUser`
+ objects. Generally, you should prefer using
+ :attr:`~django.contrib.auth.models.User.is_authenticated` to this
+ attribute.
+
+ .. versionchanged:: 1.10
+
+ In older versions, this was a method. Backwards-compatibility
+ support for using it as a method will be removed in Django 2.0.
+
Methods
-------
@@ -119,31 +154,9 @@ Methods
.. method:: get_username()
Returns the username for the user. Since the User model can be swapped
- out, you should use this method instead of referencing the username
+ out, you should use this method instead of referencing the username
attribute directly.
- .. method:: is_anonymous()
-
- Always returns ``False``. This is a way of differentiating
- :class:`~django.contrib.auth.models.User` and
- :class:`~django.contrib.auth.models.AnonymousUser` objects.
- Generally, you should prefer using
- :meth:`~django.contrib.auth.models.User.is_authenticated()` to this
- method.
-
- .. method:: is_authenticated()
-
- Always returns ``True`` (as opposed to
- ``AnonymousUser.is_authenticated()`` which always returns ``False``).
- This is a way to tell if the user has been authenticated. This does not
- imply any permissions, and doesn't check if the user is active or has
- a valid session. Even though normally you will call this method on
- ``request.user`` to find out whether it has been populated by the
- :class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
- (representing the currently logged-in user), you should know this method
- returns ``True`` for any :class:`~django.contrib.auth.models.User`
- instance.
-
.. method:: get_full_name()
Returns the :attr:`~django.contrib.auth.models.User.first_name` plus
@@ -287,6 +300,10 @@ Manager methods
string.
* :meth:`~django.contrib.auth.models.User.get_username()` always returns
the empty string.
+ * :attr:`~django.contrib.auth.models.User.is_anonymous` is ``True``
+ instead of ``False``.
+ * :attr:`~django.contrib.auth.models.User.is_authenticated` is
+ ``False`` instead of ``True``.
* :attr:`~django.contrib.auth.models.User.is_staff` and
:attr:`~django.contrib.auth.models.User.is_superuser` are always
``False``.
@@ -294,10 +311,6 @@ Manager methods
* :attr:`~django.contrib.auth.models.User.groups` and
:attr:`~django.contrib.auth.models.User.user_permissions` are always
empty.
- * :meth:`~django.contrib.auth.models.User.is_anonymous()` returns ``True``
- instead of ``False``.
- * :meth:`~django.contrib.auth.models.User.is_authenticated()` returns
- ``False`` instead of ``True``.
* :meth:`~django.contrib.auth.models.User.set_password()`,
:meth:`~django.contrib.auth.models.User.check_password()`,
:meth:`~django.db.models.Model.save` and
@@ -471,21 +484,21 @@ The following backends are available in :mod:`django.contrib.auth.backends`:
Returns the set of permission strings the ``user_obj`` has from their
own user permissions. Returns an empty set if
- :meth:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
+ :attr:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
:attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``.
.. method:: get_group_permissions(user_obj, obj=None)
Returns the set of permission strings the ``user_obj`` has from the
permissions of the groups they belong. Returns an empty set if
- :meth:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
+ :attr:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
:attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``.
.. method:: get_all_permissions(user_obj, obj=None)
Returns the set of permission strings the ``user_obj`` has, including both
user permissions and group permissions. Returns an empty set if
- :meth:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
+ :attr:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
:attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``.
.. method:: has_perm(user_obj, perm, obj=None)
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index acf195803b..a1be4d64b9 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -233,9 +233,9 @@ middleware class is listed in :setting:`MIDDLEWARE_CLASSES`.
logged-in user. If the user isn't currently logged in, ``user`` will be set
to an instance of :class:`~django.contrib.auth.models.AnonymousUser`. You
can tell them apart with
- :meth:`~django.contrib.auth.models.User.is_authenticated`, like so::
+ :attr:`~django.contrib.auth.models.User.is_authenticated`, like so::
- if request.user.is_authenticated():
+ if request.user.is_authenticated:
... # Do something for logged-in users.
else:
... # Do something for anonymous users.
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index 2002905b2d..463cd4706e 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -730,6 +730,10 @@ Miscellaneous
* Middleware classes are now initialized when the server starts rather than
during the first request.
+* If you override ``is_authenticated()`` or ``is_anonymous()`` in a custom user
+ model, you must convert them to attributes or properties as described in
+ :ref:`the deprecation note <user-is-auth-anon-deprecation>`.
+
.. _deprecated-features-1.10:
Features deprecated in 1.10
@@ -857,6 +861,37 @@ features, is deprecated. Replace it with a custom lookup::
models.CharField.register_lookup(Search)
models.TextField.register_lookup(Search)
+.. _user-is-auth-anon-deprecation:
+
+Using ``User.is_authenticated()`` and ``User.is_anonymous()`` as methods
+------------------------------------------------------------------------
+
+The ``is_authenticated()`` and ``is_anonymous()`` methods of
+:class:`~django.contrib.auth.models.AbstractBaseUser` and
+:class:`~django.contrib.auth.models.AnonymousUser` classes are now
+properties. They will still work as methods until Django 2.0, but all usage
+in Django now uses attribute access.
+
+For example, if you use
+:class:`~django.contrib.auth.middleware.AuthenticationMiddleware` and want
+to know whether the user is currently logged-in you would use::
+
+ if request.user.is_authenticated:
+ ... # Do something for logged-in users.
+ else:
+ ... # Do something for anonymous users.
+
+instead of ``request.user.is_authenticated()``.
+
+This change avoids accidental information leakage if you forget to call the
+method, e.g.::
+
+ if request.user.is_authenticated:
+ return sensitive_information
+
+If you override these methods in a custom user model, you must change them to
+properties or attributes.
+
Custom manager classes available through ``prefetch_related`` must define a ``_apply_rel_filters()`` method
-----------------------------------------------------------------------------------------------------------
diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt
index 271d7f52f7..68ca0e7f55 100644
--- a/docs/topics/auth/customizing.txt
+++ b/docs/topics/auth/customizing.txt
@@ -603,7 +603,7 @@ password resets. You must then provide some key implementation details:
raised a deprecation warning in older versions and is no longer
supported in Django 1.9).
-The following methods are available on any subclass of
+The following attributes and methods are available on any subclass of
:class:`~django.contrib.auth.models.AbstractBaseUser`:
.. class:: models.AbstractBaseUser
@@ -612,20 +612,34 @@ The following methods are available on any subclass of
Returns the value of the field nominated by ``USERNAME_FIELD``.
- .. method:: models.AbstractBaseUser.is_anonymous()
+ .. attribute:: models.AbstractBaseUser.is_authenticated
- Always returns ``False``. This is a way of differentiating
- from :class:`~django.contrib.auth.models.AnonymousUser` objects.
- Generally, you should prefer using
- :meth:`~django.contrib.auth.models.AbstractBaseUser.is_authenticated()` to this
- method.
+ Read-only attribute which is always ``True`` (as opposed to
+ ``AnonymousUser.is_authenticated`` which is always ``False``).
+ This is a way to tell if the user has been authenticated. This does not
+ imply any permissions and doesn't check if the user is active or has
+ a valid session. Even though normally you will check this attribute on
+ ``request.user`` to find out whether it has been populated by the
+ :class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
+ (representing the currently logged-in user), you should know this
+ attribute is ``True`` for any :class:`~models.User` instance.
- .. method:: models.AbstractBaseUser.is_authenticated()
+ .. versionchanged:: 1.10
- Always returns ``True``. This is a way to tell if the user has been
- authenticated. This does not imply any permissions, and doesn't check
- if the user is active - it only indicates that the user has provided a
- valid username and password.
+ In older versions, this was a method. Backwards-compatibility
+ support for using it as a method will be removed in Django 2.0.
+
+ .. attribute:: models.AbstractBaseUser.is_anonymous
+
+ Read-only attribute which is always ``False``. This is a way of
+ differentiating :class:`~models.User` and :class:`~models.AnonymousUser`
+ objects. Generally, you should prefer using
+ :attr:`~models.User.is_authenticated` to this attribute.
+
+ .. versionchanged:: 1.10
+
+ In older versions, this was a method. Backwards-compatibility
+ support for using it as a method will be removed in Django 2.0.
.. method:: models.AbstractBaseUser.set_password(raw_password)
diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt
index 72205740d5..4c8d44a0cd 100644
--- a/docs/topics/auth/default.txt
+++ b/docs/topics/auth/default.txt
@@ -306,9 +306,9 @@ of :class:`~django.contrib.auth.models.AnonymousUser`, otherwise it will be an
instance of :class:`~django.contrib.auth.models.User`.
You can tell them apart with
-:meth:`~django.contrib.auth.models.User.is_authenticated()`, like so::
+:attr:`~django.contrib.auth.models.User.is_authenticated`, like so::
- if request.user.is_authenticated():
+ if request.user.is_authenticated:
# Do something for authenticated users.
...
else:
@@ -421,15 +421,15 @@ The raw way
~~~~~~~~~~~
The simple, raw way to limit access to pages is to check
-:meth:`request.user.is_authenticated()
-<django.contrib.auth.models.User.is_authenticated()>` and either redirect to a
+:attr:`request.user.is_authenticated
+<django.contrib.auth.models.User.is_authenticated>` and either redirect to a
login page::
from django.conf import settings
from django.shortcuts import redirect
def my_view(request):
- if not request.user.is_authenticated():
+ if not request.user.is_authenticated:
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
# ...
@@ -438,7 +438,7 @@ login page::
from django.shortcuts import render
def my_view(request):
- if not request.user.is_authenticated():
+ if not request.user.is_authenticated:
return render(request, 'myapp/login_error.html')
# ...
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index 4cfffeeecf..b0bf37c94c 100644
--- a/docs/topics/cache.txt
+++ b/docs/topics/cache.txt
@@ -1170,7 +1170,7 @@ decorator)::
@vary_on_cookie
def list_blog_entries_view(request):
- if request.user.is_anonymous():
+ if request.user.is_anonymous:
response = render_only_public_entries()
patch_cache_control(response, public=True)
else:
diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt
index e53a8599e4..a5bb290d2b 100644
--- a/docs/topics/class-based-views/mixins.txt
+++ b/docs/topics/class-based-views/mixins.txt
@@ -235,7 +235,7 @@ We'll demonstrate this with the ``Author`` model we used in the
model = Author
def post(self, request, *args, **kwargs):
- if not request.user.is_authenticated():
+ if not request.user.is_authenticated:
return HttpResponseForbidden()
# Look up the author we're interested in.
@@ -466,7 +466,7 @@ Our new ``AuthorDetail`` looks like this::
return context
def post(self, request, *args, **kwargs):
- if not request.user.is_authenticated():
+ if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
@@ -552,7 +552,7 @@ template as ``AuthorDisplay`` is using on ``GET``::
model = Author
def post(self, request, *args, **kwargs):
- if not request.user.is_authenticated():
+ if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
return super(AuthorInterest, self).post(request, *args, **kwargs)
diff --git a/tests/auth_tests/test_auth_backends.py b/tests/auth_tests/test_auth_backends.py
index e3c0109c96..91f92397a2 100644
--- a/tests/auth_tests/test_auth_backends.py
+++ b/tests/auth_tests/test_auth_backends.py
@@ -12,7 +12,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.http import HttpRequest
from django.test import (
- SimpleTestCase, TestCase, modify_settings, override_settings,
+ SimpleTestCase, TestCase, mock, modify_settings, override_settings,
)
from .models import (
@@ -142,11 +142,10 @@ class BaseModelBackendTest(object):
self.assertEqual(backend.get_user_permissions(user), {'auth.test_user', 'auth.test_group'})
self.assertEqual(backend.get_group_permissions(user), {'auth.test_group'})
- user.is_anonymous = lambda: True
-
- self.assertEqual(backend.get_all_permissions(user), set())
- self.assertEqual(backend.get_user_permissions(user), set())
- self.assertEqual(backend.get_group_permissions(user), set())
+ with mock.patch.object(self.UserModel, 'is_anonymous', True):
+ self.assertEqual(backend.get_all_permissions(user), set())
+ self.assertEqual(backend.get_user_permissions(user), set())
+ self.assertEqual(backend.get_group_permissions(user), set())
def test_inactive_has_no_permissions(self):
"""
@@ -334,14 +333,14 @@ class SimpleRowlevelBackend(object):
if isinstance(obj, TestObj):
if user.username == 'test2':
return True
- elif user.is_anonymous() and perm == 'anon':
+ elif user.is_anonymous and perm == 'anon':
return True
elif not user.is_active and perm == 'inactive':
return True
return False
def has_module_perms(self, user, app_label):
- if not user.is_anonymous() and not user.is_active:
+ if not user.is_anonymous and not user.is_active:
return False
return app_label == "app1"
@@ -352,7 +351,7 @@ class SimpleRowlevelBackend(object):
if not isinstance(obj, TestObj):
return ['none']
- if user.is_anonymous():
+ if user.is_anonymous:
return ['anon']
if user.username == 'test2':
return ['simple', 'advanced']
@@ -578,7 +577,7 @@ class ChangedBackendSettingsTest(TestCase):
# Assert that the user retrieval is successful and the user is
# anonymous as the backend is not longer available.
self.assertIsNotNone(user)
- self.assertTrue(user.is_anonymous())
+ self.assertTrue(user.is_anonymous)
class TypeErrorBackend(object):
diff --git a/tests/auth_tests/test_basic.py b/tests/auth_tests/test_basic.py
index 81b7ac9c56..818f6a6d53 100644
--- a/tests/auth_tests/test_basic.py
+++ b/tests/auth_tests/test_basic.py
@@ -1,5 +1,7 @@
from __future__ import unicode_literals
+import warnings
+
from django.apps import apps
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
@@ -44,7 +46,8 @@ class BasicTestCase(TestCase):
self.assertEqual(u.get_username(), 'testuser')
# Check authentication/permissions
- self.assertTrue(u.is_authenticated())
+ self.assertFalse(u.is_anonymous)
+ self.assertTrue(u.is_authenticated)
self.assertFalse(u.is_staff)
self.assertTrue(u.is_active)
self.assertFalse(u.is_superuser)
@@ -53,6 +56,26 @@ class BasicTestCase(TestCase):
u2 = User.objects.create_user('testuser2', 'test2@example.com')
self.assertFalse(u2.has_usable_password())
+ def test_is_anonymous_authenticated_method_deprecation(self):
+ deprecation_message = (
+ 'Using user.is_authenticated() and user.is_anonymous() as a '
+ 'method is deprecated. Remove the parentheses to use it as an '
+ 'attribute.'
+ )
+ u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
+ # Backwards-compatibility callables
+ with warnings.catch_warnings(record=True) as warns:
+ warnings.simplefilter('always')
+ self.assertFalse(u.is_anonymous())
+ self.assertEqual(len(warns), 1)
+ self.assertEqual(str(warns[0].message), deprecation_message)
+
+ with warnings.catch_warnings(record=True) as warns:
+ warnings.simplefilter('always')
+ self.assertTrue(u.is_authenticated())
+ self.assertEqual(len(warns), 1)
+ self.assertEqual(str(warns[0].message), deprecation_message)
+
def test_user_no_email(self):
"Check that users can be created without an email"
u = User.objects.create_user('testuser1')
@@ -70,13 +93,34 @@ class BasicTestCase(TestCase):
self.assertEqual(a.pk, None)
self.assertEqual(a.username, '')
self.assertEqual(a.get_username(), '')
- self.assertFalse(a.is_authenticated())
+ self.assertTrue(a.is_anonymous)
+ self.assertFalse(a.is_authenticated)
self.assertFalse(a.is_staff)
self.assertFalse(a.is_active)
self.assertFalse(a.is_superuser)
self.assertEqual(a.groups.all().count(), 0)
self.assertEqual(a.user_permissions.all().count(), 0)
+ def test_anonymous_user_is_anonymous_authenticated_method_deprecation(self):
+ a = AnonymousUser()
+ deprecation_message = (
+ 'Using user.is_authenticated() and user.is_anonymous() as a '
+ 'method is deprecated. Remove the parentheses to use it as an '
+ 'attribute.'
+ )
+ # Backwards-compatibility callables
+ with warnings.catch_warnings(record=True) as warns:
+ warnings.simplefilter('always') # prevent warnings from appearing as errors
+ self.assertTrue(a.is_anonymous())
+ self.assertEqual(len(warns), 1)
+ self.assertEqual(str(warns[0].message), deprecation_message)
+
+ with warnings.catch_warnings(record=True) as warns:
+ warnings.simplefilter('always') # prevent warnings from appearing as errors
+ self.assertFalse(a.is_authenticated())
+ self.assertEqual(len(warns), 1)
+ self.assertEqual(str(warns[0].message), deprecation_message)
+
def test_superuser(self):
"Check the creation and properties of a superuser"
super = User.objects.create_superuser('super', 'super@example.com', 'super')
diff --git a/tests/auth_tests/test_middleware.py b/tests/auth_tests/test_middleware.py
index 9ebb1e46ee..635c43beb0 100644
--- a/tests/auth_tests/test_middleware.py
+++ b/tests/auth_tests/test_middleware.py
@@ -16,7 +16,7 @@ class TestAuthenticationMiddleware(TestCase):
self.request.session = self.client.session
self.middleware.process_request(self.request)
self.assertIsNotNone(self.request.user)
- self.assertFalse(self.request.user.is_anonymous())
+ self.assertFalse(self.request.user.is_anonymous)
def test_changed_password_invalidates_session(self):
# After password change, user should be anonymous
@@ -24,6 +24,6 @@ class TestAuthenticationMiddleware(TestCase):
self.user.save()
self.middleware.process_request(self.request)
self.assertIsNotNone(self.request.user)
- self.assertTrue(self.request.user.is_anonymous())
+ self.assertTrue(self.request.user.is_anonymous)
# session should be flushed
self.assertIsNone(self.request.session.session_key)
diff --git a/tests/auth_tests/test_mixins.py b/tests/auth_tests/test_mixins.py
index 22e5709bc3..5df5ee3c4e 100644
--- a/tests/auth_tests/test_mixins.py
+++ b/tests/auth_tests/test_mixins.py
@@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
-from django.test import RequestFactory, TestCase
+from django.test import RequestFactory, TestCase, mock
from django.views.generic import View
@@ -78,9 +78,9 @@ class AccessMixinTests(TestCase):
with self.assertRaises(PermissionDenied):
view(request)
+ @mock.patch.object(models.User, 'is_authenticated', False)
def test_stacked_mixins_not_logged_in(self):
user = models.User.objects.create(username='joe', password='qwerty')
- user.is_authenticated = lambda: False
perms = models.Permission.objects.filter(codename__in=('add_customuser', 'change_customuser'))
user.user_permissions.add(*perms)
request = self.factory.get('/rand')
diff --git a/tests/auth_tests/test_remote_user.py b/tests/auth_tests/test_remote_user.py
index 4e916d80ec..3d77ea3b89 100644
--- a/tests/auth_tests/test_remote_user.py
+++ b/tests/auth_tests/test_remote_user.py
@@ -38,15 +38,15 @@ class RemoteUserTest(TestCase):
num_users = User.objects.count()
response = self.client.get('/remote_user/')
- self.assertTrue(response.context['user'].is_anonymous())
+ self.assertTrue(response.context['user'].is_anonymous)
self.assertEqual(User.objects.count(), num_users)
response = self.client.get('/remote_user/', **{self.header: None})
- self.assertTrue(response.context['user'].is_anonymous())
+ self.assertTrue(response.context['user'].is_anonymous)
self.assertEqual(User.objects.count(), num_users)
response = self.client.get('/remote_user/', **{self.header: ''})
- self.assertTrue(response.context['user'].is_anonymous())
+ self.assertTrue(response.context['user'].is_anonymous)
self.assertEqual(User.objects.count(), num_users)
def test_unknown_user(self):
@@ -118,7 +118,7 @@ class RemoteUserTest(TestCase):
self.assertEqual(response.context['user'].username, 'knownuser')
# During the session, the REMOTE_USER header disappears. Should trigger logout.
response = self.client.get('/remote_user/')
- self.assertEqual(response.context['user'].is_anonymous(), True)
+ self.assertTrue(response.context['user'].is_anonymous)
# verify the remoteuser middleware will not remove a user
# authenticated via another backend
User.objects.create_user(username='modeluser', password='foo')
@@ -148,7 +148,7 @@ class RemoteUserTest(TestCase):
def test_inactive_user(self):
User.objects.create(username='knownuser', is_active=False)
response = self.client.get('/remote_user/', **{self.header: 'knownuser'})
- self.assertTrue(response.context['user'].is_anonymous())
+ self.assertTrue(response.context['user'].is_anonymous)
class RemoteUserNoCreateBackend(RemoteUserBackend):
@@ -167,7 +167,7 @@ class RemoteUserNoCreateTest(RemoteUserTest):
def test_unknown_user(self):
num_users = User.objects.count()
response = self.client.get('/remote_user/', **{self.header: 'newuser'})
- self.assertTrue(response.context['user'].is_anonymous())
+ self.assertTrue(response.context['user'].is_anonymous)
self.assertEqual(User.objects.count(), num_users)
@@ -268,5 +268,5 @@ class PersistentRemoteUserTest(RemoteUserTest):
self.assertEqual(response.context['user'].username, 'knownuser')
# Should stay logged in if the REMOTE_USER header disappears.
response = self.client.get('/remote_user/')
- self.assertEqual(response.context['user'].is_anonymous(), False)
+ self.assertFalse(response.context['user'].is_anonymous)
self.assertEqual(response.context['user'].username, 'knownuser')
diff --git a/tests/urlpatterns_reverse/views.py b/tests/urlpatterns_reverse/views.py
index acfa530b4c..1d1d3d557a 100644
--- a/tests/urlpatterns_reverse/views.py
+++ b/tests/urlpatterns_reverse/views.py
@@ -45,7 +45,7 @@ class LazyRedirectView(RedirectView):
url = reverse_lazy('named-lazy-url-redirected-to')
-@user_passes_test(lambda u: u.is_authenticated(), login_url=reverse_lazy('some-login-page'))
+@user_passes_test(lambda u: u.is_authenticated, login_url=reverse_lazy('some-login-page'))
def login_required_view(request):
return HttpResponse('Hello you')