mirror of https://github.com/django/django.git
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
|
||||
schwank@gmail.com
|
||||
scott@staplefish.com
|
||||
Olivier Sels <olivier.sels@gmail.com>
|
||||
Ilya Semenov <semenov@inetss.com>
|
||||
Aleksandra Sendecka <asendecka@hauru.eu>
|
||||
serbaut@gmail.com
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from django.conf import settings
|
||||
from django.contrib.flatpages.models import FlatPage
|
||||
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.shortcuts import get_object_or_404
|
||||
from django.template import loader, RequestContext
|
||||
|
@ -70,5 +69,4 @@ def render_flatpage(request, f):
|
|||
'flatpage': f,
|
||||
})
|
||||
response = HttpResponse(t.render(c))
|
||||
populate_xheaders(request, response, FlatPage, f.id)
|
||||
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)
|
||||
else:
|
||||
for c in f.choices:
|
||||
if not isinstance(c, (list, tuple)) or len(c) != 2:
|
||||
e.add(opts, '"%s": "choices" should be a sequence of two-tuples.' % f.name)
|
||||
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-item iterables (e.g. list of 2 item tuples).' % f.name)
|
||||
if f.db_index not in (None, True, False):
|
||||
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)
|
||||
return value
|
||||
|
||||
def clean(self, value):
|
||||
value = self.to_python(value).strip()
|
||||
return super(URLField, self).clean(value)
|
||||
|
||||
|
||||
class BooleanField(Field):
|
||||
widget = CheckboxInput
|
||||
|
@ -1105,3 +1109,7 @@ class GenericIPAddressField(CharField):
|
|||
|
||||
class SlugField(CharField):
|
||||
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
|
||||
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
|
||||
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.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
|
||||
|
@ -200,5 +197,9 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware):
|
|||
else:
|
||||
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_timeout = self.cache.default_timeout
|
||||
|
|
|
@ -83,6 +83,13 @@ class CsrfViewMiddleware(object):
|
|||
return None
|
||||
|
||||
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)
|
||||
|
||||
def process_view(self, request, callback, callback_args, callback_kwargs):
|
||||
|
@ -134,38 +141,18 @@ class CsrfViewMiddleware(object):
|
|||
# we can use strict Referer checking.
|
||||
referer = request.META.get('HTTP_REFERER')
|
||||
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)
|
||||
|
||||
# Note that request.get_host() includes the port.
|
||||
good_referer = 'https://%s/' % request.get_host()
|
||||
if not same_origin(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)
|
||||
|
||||
if csrf_token is None:
|
||||
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
|
||||
# and in this way we can avoid all CSRF attacks, including login
|
||||
# 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)
|
||||
|
||||
# Check non-cookie token for match.
|
||||
|
@ -179,13 +166,6 @@ class CsrfViewMiddleware(object):
|
|||
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
|
||||
|
||||
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._accept(request)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from functools import wraps
|
||||
from xml.dom.minidom import parseString, Node
|
||||
|
@ -380,3 +381,23 @@ class CaptureQueriesContext(object):
|
|||
if exc_type is not None:
|
||||
return
|
||||
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):
|
||||
# We need this to behave just like the CsrfViewMiddleware, but not reject
|
||||
# requests.
|
||||
# requests or log warnings.
|
||||
def _reject(self, request, reason):
|
||||
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
|
||||
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?
|
||||
-----------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -390,6 +390,8 @@ these changes.
|
|||
``django.test.testcases.OutputChecker`` will be removed. Instead use the
|
||||
doctest module from the Python standard library.
|
||||
|
||||
* The ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting will be removed.
|
||||
|
||||
2.0
|
||||
---
|
||||
|
||||
|
|
|
@ -80,9 +80,10 @@ If a field has ``blank=False``, the field will be required.
|
|||
|
||||
.. attribute:: Field.choices
|
||||
|
||||
An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this
|
||||
field. If this is given, the default form widget will be a select box with
|
||||
these choices instead of the standard text field.
|
||||
An iterable (e.g., a list or tuple) consisting itself of iterables of exactly
|
||||
two items (e.g. ``[(A, B), (A, B) ...]``) to use as choices for this field. If
|
||||
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
|
||||
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])
|
||||
|
||||
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``
|
||||
-------------
|
||||
|
@ -917,7 +919,8 @@ of some other value. You can do this automatically in the admin using
|
|||
.. class:: SmallIntegerField([**options])
|
||||
|
||||
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``
|
||||
-------------
|
||||
|
|
|
@ -280,6 +280,12 @@ CACHE_MIDDLEWARE_ANONYMOUS_ONLY
|
|||
|
||||
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
|
||||
those made by a logged-in user) will be cached. Otherwise, the middleware
|
||||
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
|
||||
activated ``AuthenticationMiddleware``.
|
||||
|
||||
See :doc:`/topics/cache`.
|
||||
|
||||
.. setting:: 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
|
||||
(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
|
||||
=====================================
|
||||
|
||||
|
@ -491,6 +494,11 @@ Miscellaneous
|
|||
memcache backend no longer uses the default timeout, and now will
|
||||
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
|
||||
==========================
|
||||
|
||||
|
@ -561,6 +569,23 @@ If necessary, you can temporarily disable auto-escaping with
|
|||
:func:`~django.utils.safestring.mark_safe` or :ttag:`{% autoescape off %}
|
||||
<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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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
|
||||
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.
|
||||
Optionally, if the :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY`
|
||||
setting is ``True``, only anonymous requests (i.e., not those made by a
|
||||
logged-in user) will be cached. This is a simple and effective way of disabling
|
||||
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.
|
||||
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
|
||||
:class:`~django.http.HttpResponse`:
|
||||
|
|
|
@ -125,6 +125,17 @@ and the :setting:`SECRET_KEY` setting.
|
|||
|
||||
.. 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**
|
||||
|
||||
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"
|
||||
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:
|
||||
|
||||
Other tags
|
||||
|
@ -1126,13 +1151,11 @@ active language. Example::
|
|||
|
||||
.. _reversing_in_templates:
|
||||
|
||||
.. templatetag:: language
|
||||
|
||||
Reversing in templates
|
||||
----------------------
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
|
|
@ -28,8 +28,8 @@ from django.middleware.cache import (FetchFromCacheMiddleware,
|
|||
from django.template import Template
|
||||
from django.template.response import TemplateResponse
|
||||
from django.test import TestCase, TransactionTestCase, RequestFactory
|
||||
from django.test.utils import override_settings, six
|
||||
from django.utils import timezone, translation, unittest
|
||||
from django.test.utils import override_settings, IgnorePendingDeprecationWarningsMixin
|
||||
from django.utils import six, timezone, translation, unittest
|
||||
from django.utils.cache import (patch_vary_headers, get_cache_key,
|
||||
learn_cache_key, patch_cache_control, patch_response_headers)
|
||||
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):
|
||||
super(CacheMiddlewareTest, self).setUp()
|
||||
self.factory = RequestFactory()
|
||||
self.default_cache = get_cache('default')
|
||||
self.other_cache = get_cache('other')
|
||||
|
@ -1602,6 +1603,7 @@ class CacheMiddlewareTest(TestCase):
|
|||
def tearDown(self):
|
||||
self.default_cache.clear()
|
||||
self.other_cache.clear()
|
||||
super(CacheMiddlewareTest, self).tearDown()
|
||||
|
||||
def test_constructor(self):
|
||||
"""
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.context_processors import csrf
|
||||
|
@ -78,18 +79,18 @@ class CsrfViewMiddlewareTest(TestCase):
|
|||
def _check_token_present(self, response, csrf_id=None):
|
||||
self.assertContains(response, "name='csrfmiddlewaretoken' value='%s'" % (csrf_id or self._csrf_id))
|
||||
|
||||
def test_process_view_token_too_long(self):
|
||||
"""
|
||||
Check that if the token is longer than expected, it is ignored and
|
||||
a new token is created.
|
||||
"""
|
||||
req = self._get_GET_no_csrf_cookie_request()
|
||||
req.COOKIES[settings.CSRF_COOKIE_NAME] = 'x' * 10000000
|
||||
CsrfViewMiddleware().process_view(req, token_view, (), {})
|
||||
resp = token_view(req)
|
||||
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
|
||||
self.assertEqual(len(csrf_cookie.value), CSRF_KEY_LENGTH)
|
||||
def test_process_view_token_too_long(self):
|
||||
"""
|
||||
Check that if the token is longer than expected, it is ignored and
|
||||
a new token is created.
|
||||
"""
|
||||
req = self._get_GET_no_csrf_cookie_request()
|
||||
req.COOKIES[settings.CSRF_COOKIE_NAME] = 'x' * 10000000
|
||||
CsrfViewMiddleware().process_view(req, token_view, (), {})
|
||||
resp = token_view(req)
|
||||
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False)
|
||||
self.assertEqual(len(csrf_cookie.value), CSRF_KEY_LENGTH)
|
||||
|
||||
def test_process_response_get_token_used(self):
|
||||
"""
|
||||
|
@ -353,3 +354,29 @@ class CsrfViewMiddlewareTest(TestCase):
|
|||
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||
self.assertTrue(resp2.cookies.get(settings.CSRF_COOKIE_NAME, False))
|
||||
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)
|
||||
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):
|
||||
class Test:
|
||||
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: "filefield": FileFields require an "upload_to" attribute.
|
||||
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-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-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: "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.
|
||||
|
|
|
@ -18,14 +18,12 @@ from django.middleware.http import ConditionalGetMiddleware
|
|||
from django.middleware.gzip import GZipMiddleware
|
||||
from django.middleware.transaction import TransactionMiddleware
|
||||
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.encoding import force_str
|
||||
from django.utils.six.moves import xrange
|
||||
from django.utils.unittest import expectedFailure, skipIf
|
||||
|
||||
from transactions.tests import IgnorePendingDeprecationWarningsMixin
|
||||
|
||||
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.test import TransactionTestCase, skipUnlessDBFeature
|
||||
from django.test.utils import IgnorePendingDeprecationWarningsMixin
|
||||
from django.utils import six
|
||||
from django.utils.unittest import skipIf, skipUnless
|
||||
|
||||
|
@ -319,19 +320,6 @@ class AtomicMiscTests(TransactionTestCase):
|
|||
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):
|
||||
|
||||
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)
|
||||
from django.db.transaction import commit_on_success, commit_manually, TransactionManagementError
|
||||
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 transactions.tests import IgnorePendingDeprecationWarningsMixin
|
||||
|
||||
from .models import Mod, M2mA, M2mB, SubMod
|
||||
|
||||
class ModelInheritanceTests(TransactionTestCase):
|
||||
|
|
Loading…
Reference in New Issue