Merge branch 'master' of github.com:django/django
This commit is contained in:
commit
dc8814bf7d
1
AUTHORS
1
AUTHORS
|
@ -505,6 +505,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Bernd Schlapsi
|
Bernd Schlapsi
|
||||||
schwank@gmail.com
|
schwank@gmail.com
|
||||||
scott@staplefish.com
|
scott@staplefish.com
|
||||||
|
Olivier Sels <olivier.sels@gmail.com>
|
||||||
Ilya Semenov <semenov@inetss.com>
|
Ilya Semenov <semenov@inetss.com>
|
||||||
Aleksandra Sendecka <asendecka@hauru.eu>
|
Aleksandra Sendecka <asendecka@hauru.eu>
|
||||||
serbaut@gmail.com
|
serbaut@gmail.com
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.flatpages.models import FlatPage
|
from django.contrib.flatpages.models import FlatPage
|
||||||
from django.contrib.sites.models import get_current_site
|
from django.contrib.sites.models import get_current_site
|
||||||
from django.core.xheaders import populate_xheaders
|
|
||||||
from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect
|
from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.template import loader, RequestContext
|
from django.template import loader, RequestContext
|
||||||
|
@ -70,5 +69,4 @@ def render_flatpage(request, f):
|
||||||
'flatpage': f,
|
'flatpage': f,
|
||||||
})
|
})
|
||||||
response = HttpResponse(t.render(c))
|
response = HttpResponse(t.render(c))
|
||||||
populate_xheaders(request, response, FlatPage, f.id)
|
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -118,8 +118,8 @@ def get_validation_errors(outfile, app=None):
|
||||||
e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
|
e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
|
||||||
else:
|
else:
|
||||||
for c in f.choices:
|
for c in f.choices:
|
||||||
if not isinstance(c, (list, tuple)) or len(c) != 2:
|
if isinstance(c, six.string_types) or not is_iterable(c) or len(c) != 2:
|
||||||
e.add(opts, '"%s": "choices" should be a sequence of two-tuples.' % f.name)
|
e.add(opts, '"%s": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).' % f.name)
|
||||||
if f.db_index not in (None, True, False):
|
if f.db_index not in (None, True, False):
|
||||||
e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)
|
e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)
|
||||||
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
"""
|
|
||||||
Pages in Django can are served up with custom HTTP headers containing useful
|
|
||||||
information about those pages -- namely, the content type and object ID.
|
|
||||||
|
|
||||||
This module contains utility functions for retrieving and doing interesting
|
|
||||||
things with these special "X-Headers" (so called because the HTTP spec demands
|
|
||||||
that custom headers are prefixed with "X-").
|
|
||||||
|
|
||||||
Next time you're at slashdot.org, watch out for X-Fry and X-Bender. :)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def populate_xheaders(request, response, model, object_id):
|
|
||||||
"""
|
|
||||||
Adds the "X-Object-Type" and "X-Object-Id" headers to the given
|
|
||||||
HttpResponse according to the given model and object_id -- but only if the
|
|
||||||
given HttpRequest object has an IP address within the INTERNAL_IPS setting
|
|
||||||
or if the request is from a logged in staff member.
|
|
||||||
"""
|
|
||||||
from django.conf import settings
|
|
||||||
if (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS
|
|
||||||
or (hasattr(request, 'user') and request.user.is_active
|
|
||||||
and request.user.is_staff)):
|
|
||||||
response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.model_name)
|
|
||||||
response['X-Object-Id'] = str(object_id)
|
|
|
@ -670,6 +670,10 @@ class URLField(CharField):
|
||||||
value = urlunsplit(url_fields)
|
value = urlunsplit(url_fields)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
value = self.to_python(value).strip()
|
||||||
|
return super(URLField, self).clean(value)
|
||||||
|
|
||||||
|
|
||||||
class BooleanField(Field):
|
class BooleanField(Field):
|
||||||
widget = CheckboxInput
|
widget = CheckboxInput
|
||||||
|
@ -1105,3 +1109,7 @@ class GenericIPAddressField(CharField):
|
||||||
|
|
||||||
class SlugField(CharField):
|
class SlugField(CharField):
|
||||||
default_validators = [validators.validate_slug]
|
default_validators = [validators.validate_slug]
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
value = self.to_python(value).strip()
|
||||||
|
return super(SlugField, self).clean(value)
|
||||||
|
|
|
@ -29,11 +29,6 @@ More details about how the caching works:
|
||||||
of the response's "Cache-Control" header, falling back to the
|
of the response's "Cache-Control" header, falling back to the
|
||||||
CACHE_MIDDLEWARE_SECONDS setting if the section was not found.
|
CACHE_MIDDLEWARE_SECONDS setting if the section was not found.
|
||||||
|
|
||||||
* If CACHE_MIDDLEWARE_ANONYMOUS_ONLY is set to True, only anonymous requests
|
|
||||||
(i.e., those not made by a logged-in user) will be cached. This is a simple
|
|
||||||
and effective way of avoiding the caching of the Django admin (and any other
|
|
||||||
user-specific content).
|
|
||||||
|
|
||||||
* This middleware expects that a HEAD request is answered with the same response
|
* This middleware expects that a HEAD request is answered with the same response
|
||||||
headers exactly like the corresponding GET request.
|
headers exactly like the corresponding GET request.
|
||||||
|
|
||||||
|
@ -48,6 +43,8 @@ More details about how the caching works:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
|
from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
|
||||||
from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
|
from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
|
||||||
|
@ -200,5 +197,9 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware):
|
||||||
else:
|
else:
|
||||||
self.cache_anonymous_only = cache_anonymous_only
|
self.cache_anonymous_only = cache_anonymous_only
|
||||||
|
|
||||||
|
if self.cache_anonymous_only:
|
||||||
|
msg = "CACHE_MIDDLEWARE_ANONYMOUS_ONLY has been deprecated and will be removed in Django 1.8."
|
||||||
|
warnings.warn(msg, PendingDeprecationWarning, stacklevel=1)
|
||||||
|
|
||||||
self.cache = get_cache(self.cache_alias, **cache_kwargs)
|
self.cache = get_cache(self.cache_alias, **cache_kwargs)
|
||||||
self.cache_timeout = self.cache.default_timeout
|
self.cache_timeout = self.cache.default_timeout
|
||||||
|
|
|
@ -83,6 +83,13 @@ class CsrfViewMiddleware(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _reject(self, request, reason):
|
def _reject(self, request, reason):
|
||||||
|
logger.warning('Forbidden (%s): %s',
|
||||||
|
reason, request.path,
|
||||||
|
extra={
|
||||||
|
'status_code': 403,
|
||||||
|
'request': request,
|
||||||
|
}
|
||||||
|
)
|
||||||
return _get_failure_view()(request, reason=reason)
|
return _get_failure_view()(request, reason=reason)
|
||||||
|
|
||||||
def process_view(self, request, callback, callback_args, callback_kwargs):
|
def process_view(self, request, callback, callback_args, callback_kwargs):
|
||||||
|
@ -134,38 +141,18 @@ class CsrfViewMiddleware(object):
|
||||||
# we can use strict Referer checking.
|
# we can use strict Referer checking.
|
||||||
referer = request.META.get('HTTP_REFERER')
|
referer = request.META.get('HTTP_REFERER')
|
||||||
if referer is None:
|
if referer is None:
|
||||||
logger.warning('Forbidden (%s): %s',
|
|
||||||
REASON_NO_REFERER, request.path,
|
|
||||||
extra={
|
|
||||||
'status_code': 403,
|
|
||||||
'request': request,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return self._reject(request, REASON_NO_REFERER)
|
return self._reject(request, REASON_NO_REFERER)
|
||||||
|
|
||||||
# Note that request.get_host() includes the port.
|
# Note that request.get_host() includes the port.
|
||||||
good_referer = 'https://%s/' % request.get_host()
|
good_referer = 'https://%s/' % request.get_host()
|
||||||
if not same_origin(referer, good_referer):
|
if not same_origin(referer, good_referer):
|
||||||
reason = REASON_BAD_REFERER % (referer, good_referer)
|
reason = REASON_BAD_REFERER % (referer, good_referer)
|
||||||
logger.warning('Forbidden (%s): %s', reason, request.path,
|
|
||||||
extra={
|
|
||||||
'status_code': 403,
|
|
||||||
'request': request,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return self._reject(request, reason)
|
return self._reject(request, reason)
|
||||||
|
|
||||||
if csrf_token is None:
|
if csrf_token is None:
|
||||||
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
|
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
|
||||||
# and in this way we can avoid all CSRF attacks, including login
|
# and in this way we can avoid all CSRF attacks, including login
|
||||||
# CSRF.
|
# CSRF.
|
||||||
logger.warning('Forbidden (%s): %s',
|
|
||||||
REASON_NO_CSRF_COOKIE, request.path,
|
|
||||||
extra={
|
|
||||||
'status_code': 403,
|
|
||||||
'request': request,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return self._reject(request, REASON_NO_CSRF_COOKIE)
|
return self._reject(request, REASON_NO_CSRF_COOKIE)
|
||||||
|
|
||||||
# Check non-cookie token for match.
|
# Check non-cookie token for match.
|
||||||
|
@ -179,13 +166,6 @@ class CsrfViewMiddleware(object):
|
||||||
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
|
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
|
||||||
|
|
||||||
if not constant_time_compare(request_csrf_token, csrf_token):
|
if not constant_time_compare(request_csrf_token, csrf_token):
|
||||||
logger.warning('Forbidden (%s): %s',
|
|
||||||
REASON_BAD_TOKEN, request.path,
|
|
||||||
extra={
|
|
||||||
'status_code': 403,
|
|
||||||
'request': request,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return self._reject(request, REASON_BAD_TOKEN)
|
return self._reject(request, REASON_BAD_TOKEN)
|
||||||
|
|
||||||
return self._accept(request)
|
return self._accept(request)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from xml.dom.minidom import parseString, Node
|
from xml.dom.minidom import parseString, Node
|
||||||
|
@ -380,3 +381,23 @@ class CaptureQueriesContext(object):
|
||||||
if exc_type is not None:
|
if exc_type is not None:
|
||||||
return
|
return
|
||||||
self.final_queries = len(self.connection.queries)
|
self.final_queries = len(self.connection.queries)
|
||||||
|
|
||||||
|
|
||||||
|
class IgnoreDeprecationWarningsMixin(object):
|
||||||
|
|
||||||
|
warning_class = DeprecationWarning
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(IgnoreDeprecationWarningsMixin, self).setUp()
|
||||||
|
self.catch_warnings = warnings.catch_warnings()
|
||||||
|
self.catch_warnings.__enter__()
|
||||||
|
warnings.filterwarnings("ignore", category=self.warning_class)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.catch_warnings.__exit__(*sys.exc_info())
|
||||||
|
super(IgnoreDeprecationWarningsMixin, self).tearDown()
|
||||||
|
|
||||||
|
|
||||||
|
class IgnorePendingDeprecationWarningsMixin(IgnoreDeprecationWarningsMixin):
|
||||||
|
|
||||||
|
warning_class = PendingDeprecationWarning
|
||||||
|
|
|
@ -15,7 +15,7 @@ using the decorator multiple times, is harmless and efficient.
|
||||||
|
|
||||||
class _EnsureCsrfToken(CsrfViewMiddleware):
|
class _EnsureCsrfToken(CsrfViewMiddleware):
|
||||||
# We need this to behave just like the CsrfViewMiddleware, but not reject
|
# We need this to behave just like the CsrfViewMiddleware, but not reject
|
||||||
# requests.
|
# requests or log warnings.
|
||||||
def _reject(self, request, reason):
|
def _reject(self, request, reason):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,6 @@ account has :attr:`~django.contrib.auth.models.User.is_active` and
|
||||||
:attr:`~django.contrib.auth.models.User.is_staff` set to True. The admin site
|
:attr:`~django.contrib.auth.models.User.is_staff` set to True. The admin site
|
||||||
only allows access to users with those two fields both set to True.
|
only allows access to users with those two fields both set to True.
|
||||||
|
|
||||||
How can I prevent the cache middleware from caching the admin site?
|
|
||||||
-------------------------------------------------------------------
|
|
||||||
|
|
||||||
Set the :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY` setting to ``True``. See the
|
|
||||||
:doc:`cache documentation </topics/cache>` for more information.
|
|
||||||
|
|
||||||
How do I automatically set a field's value to the user who last edited the object in the admin?
|
How do I automatically set a field's value to the user who last edited the object in the admin?
|
||||||
-----------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -390,6 +390,8 @@ these changes.
|
||||||
``django.test.testcases.OutputChecker`` will be removed. Instead use the
|
``django.test.testcases.OutputChecker`` will be removed. Instead use the
|
||||||
doctest module from the Python standard library.
|
doctest module from the Python standard library.
|
||||||
|
|
||||||
|
* The ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting will be removed.
|
||||||
|
|
||||||
2.0
|
2.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -80,9 +80,10 @@ If a field has ``blank=False``, the field will be required.
|
||||||
|
|
||||||
.. attribute:: Field.choices
|
.. attribute:: Field.choices
|
||||||
|
|
||||||
An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this
|
An iterable (e.g., a list or tuple) consisting itself of iterables of exactly
|
||||||
field. If this is given, the default form widget will be a select box with
|
two items (e.g. ``[(A, B), (A, B) ...]``) to use as choices for this field. If
|
||||||
these choices instead of the standard text field.
|
this is given, the default form widget will be a select box with these choices
|
||||||
|
instead of the standard text field.
|
||||||
|
|
||||||
The first element in each tuple is the actual value to be stored, and the
|
The first element in each tuple is the actual value to be stored, and the
|
||||||
second element is the human-readable name. For example::
|
second element is the human-readable name. For example::
|
||||||
|
@ -889,7 +890,8 @@ The value ``0`` is accepted for backward compatibility reasons.
|
||||||
.. class:: PositiveSmallIntegerField([**options])
|
.. class:: PositiveSmallIntegerField([**options])
|
||||||
|
|
||||||
Like a :class:`PositiveIntegerField`, but only allows values under a certain
|
Like a :class:`PositiveIntegerField`, but only allows values under a certain
|
||||||
(database-dependent) point.
|
(database-dependent) point. Values up to 32767 are safe in all databases
|
||||||
|
supported by Django.
|
||||||
|
|
||||||
``SlugField``
|
``SlugField``
|
||||||
-------------
|
-------------
|
||||||
|
@ -917,7 +919,8 @@ of some other value. You can do this automatically in the admin using
|
||||||
.. class:: SmallIntegerField([**options])
|
.. class:: SmallIntegerField([**options])
|
||||||
|
|
||||||
Like an :class:`IntegerField`, but only allows values under a certain
|
Like an :class:`IntegerField`, but only allows values under a certain
|
||||||
(database-dependent) point.
|
(database-dependent) point. Values from -32768 to 32767 are safe in all databases
|
||||||
|
supported by Django.
|
||||||
|
|
||||||
``TextField``
|
``TextField``
|
||||||
-------------
|
-------------
|
||||||
|
|
|
@ -280,6 +280,12 @@ CACHE_MIDDLEWARE_ANONYMOUS_ONLY
|
||||||
|
|
||||||
Default: ``False``
|
Default: ``False``
|
||||||
|
|
||||||
|
.. deprecated:: 1.6
|
||||||
|
|
||||||
|
This setting was largely ineffective because of using cookies for sessions
|
||||||
|
and CSRF. See the :doc:`Django 1.6 release notes</releases/1.6>` for more
|
||||||
|
information.
|
||||||
|
|
||||||
If the value of this setting is ``True``, only anonymous requests (i.e., not
|
If the value of this setting is ``True``, only anonymous requests (i.e., not
|
||||||
those made by a logged-in user) will be cached. Otherwise, the middleware
|
those made by a logged-in user) will be cached. Otherwise, the middleware
|
||||||
caches every page that doesn't have GET or POST parameters.
|
caches every page that doesn't have GET or POST parameters.
|
||||||
|
@ -287,8 +293,6 @@ caches every page that doesn't have GET or POST parameters.
|
||||||
If you set the value of this setting to ``True``, you should make sure you've
|
If you set the value of this setting to ``True``, you should make sure you've
|
||||||
activated ``AuthenticationMiddleware``.
|
activated ``AuthenticationMiddleware``.
|
||||||
|
|
||||||
See :doc:`/topics/cache`.
|
|
||||||
|
|
||||||
.. setting:: CACHE_MIDDLEWARE_KEY_PREFIX
|
.. setting:: CACHE_MIDDLEWARE_KEY_PREFIX
|
||||||
|
|
||||||
CACHE_MIDDLEWARE_KEY_PREFIX
|
CACHE_MIDDLEWARE_KEY_PREFIX
|
||||||
|
|
|
@ -238,6 +238,9 @@ Minor features
|
||||||
Meta option: ``localized_fields``. Fields included in this list will be localized
|
Meta option: ``localized_fields``. Fields included in this list will be localized
|
||||||
(by setting ``localize`` on the form field).
|
(by setting ``localize`` on the form field).
|
||||||
|
|
||||||
|
* The ``choices`` argument to model fields now accepts an iterable of iterables
|
||||||
|
instead of requiring an iterable of lists or tuples.
|
||||||
|
|
||||||
Backwards incompatible changes in 1.6
|
Backwards incompatible changes in 1.6
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
@ -491,6 +494,11 @@ Miscellaneous
|
||||||
memcache backend no longer uses the default timeout, and now will
|
memcache backend no longer uses the default timeout, and now will
|
||||||
set-and-expire-immediately the value.
|
set-and-expire-immediately the value.
|
||||||
|
|
||||||
|
* The ``django.contrib.flatpages`` app used to set custom HTTP headers for
|
||||||
|
debugging purposes. This functionality was not documented and made caching
|
||||||
|
ineffective so it has been removed, along with its generic implementation,
|
||||||
|
previously available in ``django.core.xheaders``.
|
||||||
|
|
||||||
Features deprecated in 1.6
|
Features deprecated in 1.6
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
@ -561,6 +569,23 @@ If necessary, you can temporarily disable auto-escaping with
|
||||||
:func:`~django.utils.safestring.mark_safe` or :ttag:`{% autoescape off %}
|
:func:`~django.utils.safestring.mark_safe` or :ttag:`{% autoescape off %}
|
||||||
<autoescape>`.
|
<autoescape>`.
|
||||||
|
|
||||||
|
``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``CacheMiddleware`` used to provide a way to cache requests only if they
|
||||||
|
weren't made by a logged-in user. This mechanism was largely ineffective
|
||||||
|
because the middleware correctly takes into account the ``Vary: Cookie`` HTTP
|
||||||
|
header, and this header is being set on a variety of occasions, such as:
|
||||||
|
|
||||||
|
* accessing the session, or
|
||||||
|
* using CSRF protection, which is turned on by default, or
|
||||||
|
* using a client-side library which sets cookies, like `Google Analytics`__.
|
||||||
|
|
||||||
|
This makes the cache effectively work on a per-session basis regardless of the
|
||||||
|
``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting.
|
||||||
|
|
||||||
|
__ http://www.google.com/analytics/
|
||||||
|
|
||||||
``SEND_BROKEN_LINK_EMAILS`` setting
|
``SEND_BROKEN_LINK_EMAILS`` setting
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -443,15 +443,9 @@ Then, add the following required settings to your Django settings file:
|
||||||
The cache middleware caches GET and HEAD responses with status 200, where the request
|
The cache middleware caches GET and HEAD responses with status 200, where the request
|
||||||
and response headers allow. Responses to requests for the same URL with different
|
and response headers allow. Responses to requests for the same URL with different
|
||||||
query parameters are considered to be unique pages and are cached separately.
|
query parameters are considered to be unique pages and are cached separately.
|
||||||
Optionally, if the :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY`
|
The cache middleware expects that a HEAD request is answered with the same
|
||||||
setting is ``True``, only anonymous requests (i.e., not those made by a
|
response headers as the corresponding GET request; in which case it can return
|
||||||
logged-in user) will be cached. This is a simple and effective way of disabling
|
a cached GET response for HEAD request.
|
||||||
caching for any user-specific pages (including Django's admin interface). Note
|
|
||||||
that if you use :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY`, you should make
|
|
||||||
sure you've activated ``AuthenticationMiddleware``. The cache middleware
|
|
||||||
expects that a HEAD request is answered with the same response headers as
|
|
||||||
the corresponding GET request; in which case it can return a cached GET
|
|
||||||
response for HEAD request.
|
|
||||||
|
|
||||||
Additionally, the cache middleware automatically sets a few headers in each
|
Additionally, the cache middleware automatically sets a few headers in each
|
||||||
:class:`~django.http.HttpResponse`:
|
:class:`~django.http.HttpResponse`:
|
||||||
|
|
|
@ -125,6 +125,17 @@ and the :setting:`SECRET_KEY` setting.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
**If the :setting:`SECRET_KEY` is not kept secret, this can lead to
|
||||||
|
arbitrary remote code execution.**
|
||||||
|
|
||||||
|
An attacker in possession of the :setting:`SECRET_KEY` can not only
|
||||||
|
generate falsified session data, which your site will trust, but also
|
||||||
|
remotely execute arbitrary code, as the data is serialized using pickle.
|
||||||
|
|
||||||
|
If you use cookie-based sessions, pay extra care that your secret key is
|
||||||
|
always kept completely secret, for any system which might be remotely
|
||||||
|
accessible.
|
||||||
|
|
||||||
**The session data is signed but not encrypted**
|
**The session data is signed but not encrypted**
|
||||||
|
|
||||||
When using the cookies backend the session data can be read by the client.
|
When using the cookies backend the session data can be read by the client.
|
||||||
|
|
|
@ -722,6 +722,31 @@ or with the ``{#`` ... ``#}`` :ref:`one-line comment constructs <template-commen
|
||||||
msgid "Ambiguous translatable block of text"
|
msgid "Ambiguous translatable block of text"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
.. templatetag:: language
|
||||||
|
|
||||||
|
Switching language in templates
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
If you want to select a language within a template, you can use the
|
||||||
|
``language`` template tag:
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||||
|
<p>{% trans "Welcome to our page" %}</p>
|
||||||
|
|
||||||
|
{% language 'en' %}
|
||||||
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
<!-- Current language: {{ LANGUAGE_CODE }} -->
|
||||||
|
<p>{% trans "Welcome to our page" %}</p>
|
||||||
|
{% endlanguage %}
|
||||||
|
|
||||||
|
While the first occurrence of "Welcome to our page" uses the current language,
|
||||||
|
the second will always be in English.
|
||||||
|
|
||||||
.. _template-translation-vars:
|
.. _template-translation-vars:
|
||||||
|
|
||||||
Other tags
|
Other tags
|
||||||
|
@ -1126,13 +1151,11 @@ active language. Example::
|
||||||
|
|
||||||
.. _reversing_in_templates:
|
.. _reversing_in_templates:
|
||||||
|
|
||||||
.. templatetag:: language
|
|
||||||
|
|
||||||
Reversing in templates
|
Reversing in templates
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
If localized URLs get reversed in templates they always use the current
|
If localized URLs get reversed in templates they always use the current
|
||||||
language. To link to a URL in another language use the ``language``
|
language. To link to a URL in another language use the :ttag:`language`
|
||||||
template tag. It enables the given language in the enclosed template section:
|
template tag. It enables the given language in the enclosed template section:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. code-block:: html+django
|
||||||
|
|
|
@ -28,8 +28,8 @@ from django.middleware.cache import (FetchFromCacheMiddleware,
|
||||||
from django.template import Template
|
from django.template import Template
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.test import TestCase, TransactionTestCase, RequestFactory
|
from django.test import TestCase, TransactionTestCase, RequestFactory
|
||||||
from django.test.utils import override_settings, six
|
from django.test.utils import override_settings, IgnorePendingDeprecationWarningsMixin
|
||||||
from django.utils import timezone, translation, unittest
|
from django.utils import six, timezone, translation, unittest
|
||||||
from django.utils.cache import (patch_vary_headers, get_cache_key,
|
from django.utils.cache import (patch_vary_headers, get_cache_key,
|
||||||
learn_cache_key, patch_cache_control, patch_response_headers)
|
learn_cache_key, patch_cache_control, patch_response_headers)
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
@ -1592,9 +1592,10 @@ def hello_world_view(request, value):
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
class CacheMiddlewareTest(TestCase):
|
class CacheMiddlewareTest(IgnorePendingDeprecationWarningsMixin, TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super(CacheMiddlewareTest, self).setUp()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.default_cache = get_cache('default')
|
self.default_cache = get_cache('default')
|
||||||
self.other_cache = get_cache('other')
|
self.other_cache = get_cache('other')
|
||||||
|
@ -1602,6 +1603,7 @@ class CacheMiddlewareTest(TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.default_cache.clear()
|
self.default_cache.clear()
|
||||||
self.other_cache.clear()
|
self.other_cache.clear()
|
||||||
|
super(CacheMiddlewareTest, self).tearDown()
|
||||||
|
|
||||||
def test_constructor(self):
|
def test_constructor(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.context_processors import csrf
|
from django.core.context_processors import csrf
|
||||||
|
@ -353,3 +354,29 @@ class CsrfViewMiddlewareTest(TestCase):
|
||||||
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||||
self.assertTrue(resp2.cookies.get(settings.CSRF_COOKIE_NAME, False))
|
self.assertTrue(resp2.cookies.get(settings.CSRF_COOKIE_NAME, False))
|
||||||
self.assertTrue('Cookie' in resp2.get('Vary',''))
|
self.assertTrue('Cookie' in resp2.get('Vary',''))
|
||||||
|
|
||||||
|
def test_ensures_csrf_cookie_no_logging(self):
|
||||||
|
"""
|
||||||
|
Tests that ensure_csrf_cookie doesn't log warnings. See #19436.
|
||||||
|
"""
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def view(request):
|
||||||
|
# Doesn't insert a token or anything
|
||||||
|
return HttpResponse(content="")
|
||||||
|
|
||||||
|
class TestHandler(logging.Handler):
|
||||||
|
def emit(self, record):
|
||||||
|
raise Exception("This shouldn't have happened!")
|
||||||
|
|
||||||
|
logger = logging.getLogger('django.request')
|
||||||
|
test_handler = TestHandler()
|
||||||
|
old_log_level = logger.level
|
||||||
|
try:
|
||||||
|
logger.addHandler(test_handler)
|
||||||
|
logger.setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
req = self._get_GET_no_csrf_cookie_request()
|
||||||
|
resp = view(req)
|
||||||
|
finally:
|
||||||
|
logger.removeHandler(test_handler)
|
||||||
|
logger.setLevel(old_log_level)
|
||||||
|
|
|
@ -569,6 +569,14 @@ class FormsExtraTestCase(TestCase, AssertFormErrorsMixin):
|
||||||
f = GenericIPAddressField(unpack_ipv4=True)
|
f = GenericIPAddressField(unpack_ipv4=True)
|
||||||
self.assertEqual(f.clean(' ::ffff:0a0a:0a0a'), '10.10.10.10')
|
self.assertEqual(f.clean(' ::ffff:0a0a:0a0a'), '10.10.10.10')
|
||||||
|
|
||||||
|
def test_slugfield_normalization(self):
|
||||||
|
f = SlugField()
|
||||||
|
self.assertEqual(f.clean(' aa-bb-cc '), 'aa-bb-cc')
|
||||||
|
|
||||||
|
def test_urlfield_normalization(self):
|
||||||
|
f = URLField()
|
||||||
|
self.assertEqual(f.clean('http://example.com/ '), 'http://example.com/')
|
||||||
|
|
||||||
def test_smart_text(self):
|
def test_smart_text(self):
|
||||||
class Test:
|
class Test:
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
|
|
|
@ -375,8 +375,8 @@ invalid_models.fielderrors: "decimalfield3": DecimalFields require a "max_digits
|
||||||
invalid_models.fielderrors: "decimalfield4": DecimalFields require a "max_digits" attribute value that is greater than or equal to the value of the "decimal_places" attribute.
|
invalid_models.fielderrors: "decimalfield4": DecimalFields require a "max_digits" attribute value that is greater than or equal to the value of the "decimal_places" attribute.
|
||||||
invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
|
invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
|
||||||
invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list).
|
invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list).
|
||||||
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
|
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).
|
||||||
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
|
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-item iterables (e.g. list of 2 item tuples).
|
||||||
invalid_models.fielderrors: "index": "db_index" should be either None, True or False.
|
invalid_models.fielderrors: "index": "db_index" should be either None, True or False.
|
||||||
invalid_models.fielderrors: "field_": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.
|
invalid_models.fielderrors: "field_": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.
|
||||||
invalid_models.fielderrors: "nullbool": BooleanFields do not accept null values. Use a NullBooleanField instead.
|
invalid_models.fielderrors: "nullbool": BooleanFields do not accept null values. Use a NullBooleanField instead.
|
||||||
|
|
|
@ -18,14 +18,12 @@ from django.middleware.http import ConditionalGetMiddleware
|
||||||
from django.middleware.gzip import GZipMiddleware
|
from django.middleware.gzip import GZipMiddleware
|
||||||
from django.middleware.transaction import TransactionMiddleware
|
from django.middleware.transaction import TransactionMiddleware
|
||||||
from django.test import TransactionTestCase, TestCase, RequestFactory
|
from django.test import TransactionTestCase, TestCase, RequestFactory
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings, IgnorePendingDeprecationWarningsMixin
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
from django.utils.six.moves import xrange
|
from django.utils.six.moves import xrange
|
||||||
from django.utils.unittest import expectedFailure, skipIf
|
from django.utils.unittest import expectedFailure, skipIf
|
||||||
|
|
||||||
from transactions.tests import IgnorePendingDeprecationWarningsMixin
|
|
||||||
|
|
||||||
from .models import Band
|
from .models import Band
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class ThingItem(object):
|
||||||
|
|
||||||
|
def __init__(self, value, display):
|
||||||
|
self.value = value
|
||||||
|
self.display = display
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return (x for x in [self.value, self.display])
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return 2
|
||||||
|
|
||||||
|
|
||||||
|
class Things(object):
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return (x for x in [ThingItem(1, 2), ThingItem(3, 4)])
|
||||||
|
|
||||||
|
|
||||||
|
class ThingWithIterableChoices(models.Model):
|
||||||
|
|
||||||
|
# Testing choices= Iterable of Iterables
|
||||||
|
# See: https://code.djangoproject.com/ticket/20430
|
||||||
|
thing = models.CharField(max_length=100, blank=True, choices=Things())
|
|
@ -0,0 +1,14 @@
|
||||||
|
import io
|
||||||
|
|
||||||
|
from django.core import management
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class ModelValidationTest(TestCase):
|
||||||
|
|
||||||
|
def test_models_validate(self):
|
||||||
|
# All our models should validate properly
|
||||||
|
# Validation Tests:
|
||||||
|
# * choices= Iterable of Iterables
|
||||||
|
# See: https://code.djangoproject.com/ticket/20430
|
||||||
|
management.call_command("validate", stdout=io.BytesIO())
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<django-objects version="1.0">
|
|
||||||
<object pk="100" model="auth.user">
|
|
||||||
<field type="CharField" name="username">super</field>
|
|
||||||
<field type="CharField" name="first_name">Super</field>
|
|
||||||
<field type="CharField" name="last_name">User</field>
|
|
||||||
<field type="CharField" name="email">super@example.com</field>
|
|
||||||
<field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
|
|
||||||
<field type="BooleanField" name="is_staff">True</field>
|
|
||||||
<field type="BooleanField" name="is_active">True</field>
|
|
||||||
<field type="BooleanField" name="is_superuser">True</field>
|
|
||||||
<field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
|
|
||||||
<field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
|
|
||||||
<field to="auth.group" name="groups" rel="ManyToManyRel"></field>
|
|
||||||
<field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
|
|
||||||
</object>
|
|
||||||
<object pk="1" model="special_headers.article">
|
|
||||||
<field type="TextField" name="text">text</field>
|
|
||||||
</object>
|
|
||||||
</django-objects>
|
|
|
@ -1,5 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class Article(models.Model):
|
|
||||||
text = models.TextField()
|
|
|
@ -1 +0,0 @@
|
||||||
{{ object }}
|
|
|
@ -1,62 +0,0 @@
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.test.utils import override_settings
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
|
||||||
class SpecialHeadersTest(TestCase):
|
|
||||||
fixtures = ['data.xml']
|
|
||||||
urls = 'special_headers.urls'
|
|
||||||
|
|
||||||
def test_xheaders(self):
|
|
||||||
user = User.objects.get(username='super')
|
|
||||||
response = self.client.get('/special_headers/article/1/')
|
|
||||||
self.assertFalse('X-Object-Type' in response)
|
|
||||||
self.client.login(username='super', password='secret')
|
|
||||||
response = self.client.get('/special_headers/article/1/')
|
|
||||||
self.assertTrue('X-Object-Type' in response)
|
|
||||||
user.is_staff = False
|
|
||||||
user.save()
|
|
||||||
response = self.client.get('/special_headers/article/1/')
|
|
||||||
self.assertFalse('X-Object-Type' in response)
|
|
||||||
user.is_staff = True
|
|
||||||
user.is_active = False
|
|
||||||
user.save()
|
|
||||||
response = self.client.get('/special_headers/article/1/')
|
|
||||||
self.assertFalse('X-Object-Type' in response)
|
|
||||||
|
|
||||||
def test_xview_func(self):
|
|
||||||
user = User.objects.get(username='super')
|
|
||||||
response = self.client.head('/special_headers/xview/func/')
|
|
||||||
self.assertFalse('X-View' in response)
|
|
||||||
self.client.login(username='super', password='secret')
|
|
||||||
response = self.client.head('/special_headers/xview/func/')
|
|
||||||
self.assertTrue('X-View' in response)
|
|
||||||
self.assertEqual(response['X-View'], 'special_headers.views.xview')
|
|
||||||
user.is_staff = False
|
|
||||||
user.save()
|
|
||||||
response = self.client.head('/special_headers/xview/func/')
|
|
||||||
self.assertFalse('X-View' in response)
|
|
||||||
user.is_staff = True
|
|
||||||
user.is_active = False
|
|
||||||
user.save()
|
|
||||||
response = self.client.head('/special_headers/xview/func/')
|
|
||||||
self.assertFalse('X-View' in response)
|
|
||||||
|
|
||||||
def test_xview_class(self):
|
|
||||||
user = User.objects.get(username='super')
|
|
||||||
response = self.client.head('/special_headers/xview/class/')
|
|
||||||
self.assertFalse('X-View' in response)
|
|
||||||
self.client.login(username='super', password='secret')
|
|
||||||
response = self.client.head('/special_headers/xview/class/')
|
|
||||||
self.assertTrue('X-View' in response)
|
|
||||||
self.assertEqual(response['X-View'], 'special_headers.views.XViewClass')
|
|
||||||
user.is_staff = False
|
|
||||||
user.save()
|
|
||||||
response = self.client.head('/special_headers/xview/class/')
|
|
||||||
self.assertFalse('X-View' in response)
|
|
||||||
user.is_staff = True
|
|
||||||
user.is_active = False
|
|
||||||
user.save()
|
|
||||||
response = self.client.head('/special_headers/xview/class/')
|
|
||||||
self.assertFalse('X-View' in response)
|
|
|
@ -1,13 +0,0 @@
|
||||||
# coding: utf-8
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django.conf.urls import patterns
|
|
||||||
|
|
||||||
from . import views
|
|
||||||
from .models import Article
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
(r'^special_headers/article/(?P<object_id>\d+)/$', views.xview_xheaders),
|
|
||||||
(r'^special_headers/xview/func/$', views.xview_dec(views.xview)),
|
|
||||||
(r'^special_headers/xview/class/$', views.xview_dec(views.XViewClass.as_view())),
|
|
||||||
)
|
|
|
@ -1,21 +0,0 @@
|
||||||
from django.core.xheaders import populate_xheaders
|
|
||||||
from django.http import HttpResponse
|
|
||||||
from django.utils.decorators import decorator_from_middleware
|
|
||||||
from django.views.generic import View
|
|
||||||
from django.middleware.doc import XViewMiddleware
|
|
||||||
|
|
||||||
from .models import Article
|
|
||||||
|
|
||||||
xview_dec = decorator_from_middleware(XViewMiddleware)
|
|
||||||
|
|
||||||
def xview(request):
|
|
||||||
return HttpResponse()
|
|
||||||
|
|
||||||
def xview_xheaders(request, object_id):
|
|
||||||
response = HttpResponse()
|
|
||||||
populate_xheaders(request, response, Article, 1)
|
|
||||||
return response
|
|
||||||
|
|
||||||
class XViewClass(View):
|
|
||||||
def get(self, request):
|
|
||||||
return HttpResponse()
|
|
|
@ -5,6 +5,7 @@ import warnings
|
||||||
|
|
||||||
from django.db import connection, transaction, IntegrityError
|
from django.db import connection, transaction, IntegrityError
|
||||||
from django.test import TransactionTestCase, skipUnlessDBFeature
|
from django.test import TransactionTestCase, skipUnlessDBFeature
|
||||||
|
from django.test.utils import IgnorePendingDeprecationWarningsMixin
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.unittest import skipIf, skipUnless
|
from django.utils.unittest import skipIf, skipUnless
|
||||||
|
|
||||||
|
@ -319,19 +320,6 @@ class AtomicMiscTests(TransactionTestCase):
|
||||||
transaction.atomic(Callable())
|
transaction.atomic(Callable())
|
||||||
|
|
||||||
|
|
||||||
class IgnorePendingDeprecationWarningsMixin(object):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(IgnorePendingDeprecationWarningsMixin, self).setUp()
|
|
||||||
self.catch_warnings = warnings.catch_warnings()
|
|
||||||
self.catch_warnings.__enter__()
|
|
||||||
warnings.filterwarnings("ignore", category=PendingDeprecationWarning)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.catch_warnings.__exit__(*sys.exc_info())
|
|
||||||
super(IgnorePendingDeprecationWarningsMixin, self).tearDown()
|
|
||||||
|
|
||||||
|
|
||||||
class TransactionTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCase):
|
class TransactionTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCase):
|
||||||
|
|
||||||
def create_a_reporter_then_fail(self, first, last):
|
def create_a_reporter_then_fail(self, first, last):
|
||||||
|
|
|
@ -4,11 +4,9 @@ from django.db import (connection, connections, transaction, DEFAULT_DB_ALIAS, D
|
||||||
IntegrityError)
|
IntegrityError)
|
||||||
from django.db.transaction import commit_on_success, commit_manually, TransactionManagementError
|
from django.db.transaction import commit_on_success, commit_manually, TransactionManagementError
|
||||||
from django.test import TransactionTestCase, skipUnlessDBFeature
|
from django.test import TransactionTestCase, skipUnlessDBFeature
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings, IgnorePendingDeprecationWarningsMixin
|
||||||
from django.utils.unittest import skipIf, skipUnless
|
from django.utils.unittest import skipIf, skipUnless
|
||||||
|
|
||||||
from transactions.tests import IgnorePendingDeprecationWarningsMixin
|
|
||||||
|
|
||||||
from .models import Mod, M2mA, M2mB, SubMod
|
from .models import Mod, M2mA, M2mB, SubMod
|
||||||
|
|
||||||
class ModelInheritanceTests(TransactionTestCase):
|
class ModelInheritanceTests(TransactionTestCase):
|
||||||
|
|
Loading…
Reference in New Issue