Merge branch 'master' of github.com:django/django

This commit is contained in:
Florian Apolloner 2013-05-18 17:39:14 +02:00
commit dc8814bf7d
33 changed files with 225 additions and 244 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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?
----------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------

View File

@ -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
--- ---

View File

@ -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``
------------- -------------

View File

@ -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

View File

@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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`:

View File

@ -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.

View File

@ -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

View File

@ -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):
""" """

View File

@ -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)

View File

@ -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:

View File

@ -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.

View File

@ -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

View File

@ -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())

View File

@ -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())

View File

@ -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>

View File

@ -1,5 +0,0 @@
from django.db import models
class Article(models.Model):
text = models.TextField()

View File

@ -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)

View File

@ -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())),
)

View File

@ -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()

View File

@ -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):

View File

@ -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):