This commit is contained in:
Alex Gaynor 2013-09-14 12:32:45 -07:00
commit 5c645ec81a
162 changed files with 1298 additions and 589 deletions

View File

@ -397,6 +397,7 @@ answer newbie questions, and generally made Django that much better:
Yann Malet
Frantisek Malina <vizualbod@vizualbod.com>
Mike Malone <mjmalone@gmail.com>
Curtis Maloney (FunkyBob) <curtis@tinbrain.net>
Martin Maney <http://www.chipy.org/Martin_Maney>
Michael Manfre <mmanfre@gmail.com>
Javier Mansilla <javimansilla@gmail.com>

View File

@ -7,35 +7,8 @@ from django.contrib.admin.sites import AdminSite, site
from django.contrib.admin.filters import (ListFilter, SimpleListFilter,
FieldListFilter, BooleanFieldListFilter, RelatedFieldListFilter,
ChoicesFieldListFilter, DateFieldListFilter, AllValuesFieldListFilter)
from django.utils.module_loading import autodiscover_modules
def autodiscover():
"""
Auto-discover INSTALLED_APPS admin.py modules and fail silently when
not present. This forces an import on them to register any admin bits they
may want.
"""
import copy
from importlib import import_module
from django.conf import settings
from django.utils.module_loading import module_has_submodule
for app in settings.INSTALLED_APPS:
mod = import_module(app)
# Attempt to import the app's admin module.
try:
before_import_registry = copy.copy(site._registry)
import_module('%s.admin' % app)
except:
# Reset the model registry to the state before the last import as
# this import will have to reoccur on the next request and this
# could raise NotRegistered and AlreadyRegistered exceptions
# (see #8245).
site._registry = before_import_registry
# Decide whether to bubble up this error. If the app just
# doesn't have an admin module, we can ignore the error
# attempting to import it, otherwise we want it to bubble up.
if module_has_submodule(mod, 'admin'):
raise
autodiscover_modules('admin', register_to=site)

View File

@ -33,26 +33,26 @@ class ListFilter(object):
"""
Returns True if some choices would be output for this filter.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of ListFilter must provide a has_output() method')
def choices(self, cl):
"""
Returns choices ready to be output in the template.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of ListFilter must provide a choices() method')
def queryset(self, request, queryset):
"""
Returns the filtered queryset.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of ListFilter must provide a queryset() method')
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of ListFilter must provide an expected_parameters() method')
class SimpleListFilter(ListFilter):
@ -89,7 +89,9 @@ class SimpleListFilter(ListFilter):
"""
Must be overridden to return a list of tuples (value, verbose value)
"""
raise NotImplementedError
raise NotImplementedError(
'The SimpleListFilter.lookups() method must be overridden to '
'return a list of tuples (value, verbose value)')
def expected_parameters(self):
return [self.parameter_name]

View File

@ -6,7 +6,7 @@ from django.contrib.auth import logout as auth_logout, REDIRECT_FIELD_NAME
from django.contrib.contenttypes import views as contenttype_views
from django.views.decorators.csrf import csrf_protect
from django.db.models.base import ModelBase
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.core.urlresolvers import reverse, NoReverseMatch
from django.template.response import TemplateResponse
from django.utils import six
@ -232,14 +232,25 @@ class AdminSite(object):
url(r'^password_change/done/$', wrap(self.password_change_done, cacheable=True), name='password_change_done'),
url(r'^jsi18n/$', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'),
url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$', wrap(contenttype_views.shortcut), name='view_on_site'),
url(r'^(?P<app_label>\w+)/$', wrap(self.app_index), name='app_list'),
)
# Add in each model's views.
# Add in each model's views, and create a list of valid URLS for the
# app_index
valid_app_labels = []
for model, model_admin in six.iteritems(self._registry):
urlpatterns += patterns('',
url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls))
)
if model._meta.app_label not in valid_app_labels:
valid_app_labels.append(model._meta.app_label)
# If there were ModelAdmins registered, we should have a list of app
# labels for which we need to allow access to the app_index view,
if valid_app_labels:
regex = r'^(?P<app_label>' + '|'.join(valid_app_labels) + ')/$'
urlpatterns += patterns('',
url(regex, wrap(self.app_index), name='app_list'),
)
return urlpatterns
@property
@ -399,44 +410,45 @@ class AdminSite(object):
def app_index(self, request, app_label, extra_context=None):
user = request.user
has_module_perms = user.has_module_perms(app_label)
if not has_module_perms:
raise PermissionDenied
app_dict = {}
for model, model_admin in self._registry.items():
if app_label == model._meta.app_label:
if has_module_perms:
perms = model_admin.get_model_perms(request)
perms = model_admin.get_model_perms(request)
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
info = (app_label, model._meta.model_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
'object_name': model._meta.object_name,
'perms': perms,
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
info = (app_label, model._meta.model_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
'object_name': model._meta.object_name,
'perms': perms,
}
if perms.get('change'):
try:
model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
except NoReverseMatch:
pass
if perms.get('add'):
try:
model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name)
except NoReverseMatch:
pass
if app_dict:
app_dict['models'].append(model_dict),
else:
# First time around, now that we know there's
# something to display, add in the necessary meta
# information.
app_dict = {
'name': app_label.title(),
'app_label': app_label,
'app_url': '',
'has_module_perms': has_module_perms,
'models': [model_dict],
}
if perms.get('change', False):
try:
model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
except NoReverseMatch:
pass
if perms.get('add', False):
try:
model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name)
except NoReverseMatch:
pass
if app_dict:
app_dict['models'].append(model_dict),
else:
# First time around, now that we know there's
# something to display, add in the necessary meta
# information.
app_dict = {
'name': app_label.title(),
'app_label': app_label,
'app_url': '',
'has_module_perms': has_module_perms,
'models': [model_dict],
}
if not app_dict:
raise Http404('The requested admin page does not exist.')
# Sort the models alphabetically within each app.

View File

@ -293,8 +293,9 @@ var DateTimeShortcuts = {
var date_parts = inp.value.split('-');
var year = date_parts[0];
var month = parseFloat(date_parts[1]);
var selected = new Date(inp.value);
if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
DateTimeShortcuts.calendars[num].drawDate(month, year);
DateTimeShortcuts.calendars[num].drawDate(month, year, selected);
}
}

View File

@ -27,13 +27,29 @@ var CalendarNamespace = {
}
return days;
},
draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999
draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999
var today = new Date();
var todayDay = today.getDate();
var todayMonth = today.getMonth()+1;
var todayYear = today.getFullYear();
var todayClass = '';
// Use UTC functions here because the date field does not contain time
// and using the UTC function variants prevent the local time offset
// from altering the date, specifically the day field. For example:
//
// ```
// var x = new Date('2013-10-02');
// var day = x.getDate();
// ```
//
// The day variable above will be 1 instead of 2 in, say, US Pacific time
// zone.
var isSelectedMonth = false;
if (typeof selected != 'undefined') {
isSelectedMonth = (selected.getUTCFullYear() == year && (selected.getUTCMonth()+1) == month);
}
month = parseInt(month);
year = parseInt(year);
var calDiv = document.getElementById(div_id);
@ -55,7 +71,7 @@ var CalendarNamespace = {
tableRow = quickElement('tr', tableBody);
for (var i = 0; i < startingPos; i++) {
var _cell = quickElement('td', tableRow, ' ');
_cell.style.backgroundColor = '#f3f3f3';
_cell.className = "nonday";
}
// Draw days of month
@ -69,6 +85,13 @@ var CalendarNamespace = {
} else {
todayClass='';
}
// use UTC function; see above for explanation.
if (isSelectedMonth && currentDay == selected.getUTCDate()) {
if (todayClass != '') todayClass += " ";
todayClass += "selected";
}
var cell = quickElement('td', tableRow, '', 'class', todayClass);
quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));');
@ -78,7 +101,7 @@ var CalendarNamespace = {
// Draw blanks after end of month (optional, but makes for valid code)
while (tableRow.childNodes.length < 7) {
var _cell = quickElement('td', tableRow, ' ');
_cell.style.backgroundColor = '#f3f3f3';
_cell.className = "nonday";
}
calDiv.appendChild(calTable);
@ -86,7 +109,7 @@ var CalendarNamespace = {
}
// Calendar -- A calendar instance
function Calendar(div_id, callback) {
function Calendar(div_id, callback, selected) {
// div_id (string) is the ID of the element in which the calendar will
// be displayed
// callback (string) is the name of a JavaScript function that will be
@ -97,14 +120,22 @@ function Calendar(div_id, callback) {
this.today = new Date();
this.currentMonth = this.today.getMonth() + 1;
this.currentYear = this.today.getFullYear();
if (typeof selected != 'undefined') {
this.selected = selected;
}
}
Calendar.prototype = {
drawCurrent: function() {
CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback);
CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected);
},
drawDate: function(month, year) {
drawDate: function(month, year, selected) {
this.currentMonth = month;
this.currentYear = year;
if(selected) {
this.selected = selected;
}
this.drawCurrent();
},
drawPreviousMonth: function() {

View File

@ -26,8 +26,10 @@
</div>
{% if user.is_active and user.is_staff %}
<div id="user-tools">
{% trans 'Welcome,' %}
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
{% block welcome-msg %}
{% trans 'Welcome,' %}
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
{% endblock %}
{% block userlinks %}
{% url 'django-admindocs-docroot' as docsroot %}
{% if docsroot %}

View File

@ -192,7 +192,7 @@ class BasePasswordHasher(object):
"""
Checks if the given password is correct
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BasePasswordHasher must provide a verify() method')
def encode(self, password, salt):
"""
@ -201,7 +201,7 @@ class BasePasswordHasher(object):
The result is normally formatted as "algorithm$salt$hash" and
must be fewer than 128 characters.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BasePasswordHasher must provide an encode() method')
def safe_summary(self, encoded):
"""
@ -210,7 +210,7 @@ class BasePasswordHasher(object):
The result is a dictionary and will be used where the password field
must be displayed to construct a safe representation of the password.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BasePasswordHasher must provide a safe_summary() method')
class PBKDF2PasswordHasher(BasePasswordHasher):

View File

@ -245,10 +245,10 @@ class AbstractBaseUser(models.Model):
return is_password_usable(self.password)
def get_full_name(self):
raise NotImplementedError()
raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_full_name() method')
def get_short_name(self):
raise NotImplementedError()
raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_short_name() method.')
# A few helper functions for common logic between User and AnonymousUser.
@ -441,16 +441,16 @@ class AnonymousUser(object):
return 1 # instances always return the same hash value
def save(self):
raise NotImplementedError
raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
def delete(self):
raise NotImplementedError
raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
def set_password(self, raw_password):
raise NotImplementedError
raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
def check_password(self, raw_password):
raise NotImplementedError
raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
def _get_groups(self):
return self._groups

View File

@ -112,7 +112,7 @@ class BaseCommentNode(six.with_metaclass(RenameBaseCommentNodeMethods, template.
def get_context_value_from_queryset(self, context, qs):
"""Subclasses should override this."""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseCommentNode must provide a get_context_value_from_queryset() method')
class CommentListNode(BaseCommentNode):
"""Insert a list of comments into the context."""
@ -338,4 +338,3 @@ def get_comment_permalink(comment, anchor_pattern=None):
if anchor_pattern:
return comment.get_absolute_url(anchor_pattern)
return comment.get_absolute_url()

View File

@ -101,7 +101,7 @@ class BaseSpatialOperations(object):
Returns the database column type for the geometry field on
the spatial backend.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseSpatialOperations must provide a geo_db_type() method')
def get_distance(self, f, value, lookup_type):
"""
@ -117,7 +117,7 @@ class BaseSpatialOperations(object):
stored procedure call to the transformation function of the spatial
backend.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseSpatialOperations must provide a geo_db_placeholder() method')
def get_expression_column(self, evaluator):
"""
@ -134,14 +134,14 @@ class BaseSpatialOperations(object):
raise NotImplementedError('Aggregate support not implemented for this spatial backend.')
def spatial_lookup_sql(self, lvalue, lookup_type, value, field):
raise NotImplementedError
raise NotImplementedError('subclasses of BaseSpatialOperations must a provide spatial_lookup_sql() method')
# Routines for getting the OGC-compliant models.
def geometry_columns(self):
raise NotImplementedError
raise NotImplementedError('subclasses of BaseSpatialOperations must a provide geometry_columns() method')
def spatial_ref_sys(self):
raise NotImplementedError
raise NotImplementedError('subclasses of BaseSpatialOperations must a provide spatial_ref_sys() method')
@python_2_unicode_compatible
class SpatialRefSysMixin(object):

View File

@ -25,9 +25,10 @@ class SpatiaLiteIntrospection(DatabaseIntrospection):
cursor = self.connection.cursor()
try:
# Querying the `geometry_columns` table to get additional metadata.
cursor.execute('SELECT "coord_dimension", "srid", "type" '
'FROM "geometry_columns" '
'WHERE "f_table_name"=%s AND "f_geometry_column"=%s',
type_col = 'type' if self.connection.ops.spatial_version < (4, 0, 0) else 'geometry_type'
cursor.execute('SELECT coord_dimension, srid, %s '
'FROM geometry_columns '
'WHERE f_table_name=%%s AND f_geometry_column=%%s' % type_col,
(table_name, geo_col))
row = cursor.fetchone()
if not row:

View File

@ -9,5 +9,6 @@ class AllOGRFields(models.Model):
f_datetime = models.DateTimeField()
f_time = models.TimeField()
geom = models.PolygonField()
point = models.PointField()
objects = models.GeoManager()

View File

@ -3,11 +3,13 @@ from __future__ import unicode_literals
import os
from unittest import skipUnless
from django.core.management import call_command
from django.db import connections
from django.test import TestCase
from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.geometry.test_data import TEST_DATA
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
from django.utils.six import StringIO
if HAS_GDAL:
from django.contrib.gis.gdal import Driver
@ -16,6 +18,22 @@ if HAS_GDAL:
from .models import AllOGRFields
@skipUnless(HAS_GDAL and HAS_SPATIAL_DB, "GDAL and spatial db are required.")
class InspectDbTests(TestCase):
def test_geom_columns(self):
"""
Test the geo-enabled inspectdb command.
"""
out = StringIO()
call_command('inspectdb',
table_name_filter=lambda tn:tn.startswith('inspectapp_'),
stdout=out)
output = out.getvalue()
self.assertIn('geom = models.PolygonField()', output)
self.assertIn('point = models.PointField()', output)
self.assertIn('objects = models.GeoManager()', output)
@skipUnless(HAS_GDAL and HAS_SPATIAL_DB, "GDAL and spatial db are required.")
class OGRInspectTest(TestCase):
maxDiff = 1024

View File

@ -14,10 +14,9 @@ from django.template import Template, Context, defaultfilters
from django.test import TestCase
from django.test.utils import override_settings
from django.utils.html import escape
from django.utils.timezone import utc
from django.utils.timezone import utc, get_fixed_timezone
from django.utils import translation
from django.utils.translation import ugettext as _
from django.utils import tzinfo
from i18n import TransRealMixin
@ -153,8 +152,8 @@ class HumanizeTests(TransRealMixin, TestCase):
def test_naturalday_tz(self):
today = datetime.date.today()
tz_one = tzinfo.FixedOffset(datetime.timedelta(hours=-12))
tz_two = tzinfo.FixedOffset(datetime.timedelta(hours=12))
tz_one = get_fixed_timezone(-720)
tz_two = get_fixed_timezone(720)
# Can be today or yesterday
date_one = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_one)

View File

@ -105,7 +105,7 @@ class BaseStorage(object):
just containing no messages) then ``None`` should be returned in
place of ``messages``.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseStorage must provide a _get() method')
def _store(self, messages, response, *args, **kwargs):
"""
@ -116,7 +116,7 @@ class BaseStorage(object):
**This method must be implemented by a subclass.**
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseStorage must provide a _store() method')
def _prepare_messages(self, messages):
"""

View File

@ -284,7 +284,7 @@ class SessionBase(object):
"""
Returns True if the given session_key already exists.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of SessionBase must provide an exists() method')
def create(self):
"""
@ -292,7 +292,7 @@ class SessionBase(object):
a unique key and will have saved the result once (with empty data)
before the method returns.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of SessionBase must provide a create() method')
def save(self, must_create=False):
"""
@ -300,20 +300,20 @@ class SessionBase(object):
is created (otherwise a CreateError exception is raised). Otherwise,
save() can update an existing object with the same key.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of SessionBase must provide a save() method')
def delete(self, session_key=None):
"""
Deletes the session data under this key. If the key is None, the
current session key value is used.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of SessionBase must provide a delete() method')
def load(self):
"""
Loads the session data and returns a dictionary.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of SessionBase must provide a load() method')
@classmethod
def clear_expired(cls):
@ -324,4 +324,4 @@ class SessionBase(object):
NotImplementedError. If it isn't necessary, because the backend has
a built-in expiration mechanism, it should be a no-op.
"""
raise NotImplementedError
raise NotImplementedError('This backend does not support clear_expired().')

View File

@ -28,7 +28,7 @@ class BaseFinder(object):
the first found file path will be returned; if set
to ``True`` a list of all found files paths is returned.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseFinder must provide a find() method')
def list(self, ignore_patterns):
"""
@ -36,7 +36,7 @@ class BaseFinder(object):
a two item iterable consisting of the relative path and storage
instance.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseFinder must provide a list() method')
class FileSystemFinder(BaseFinder):

View File

@ -7,12 +7,12 @@ from django.contrib.sites.models import get_current_site
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.http import HttpResponse, Http404
from django.template import loader, TemplateDoesNotExist, RequestContext
from django.utils import feedgenerator, tzinfo
from django.utils import feedgenerator
from django.utils.encoding import force_text, iri_to_uri, smart_text
from django.utils.html import escape
from django.utils.http import http_date
from django.utils import six
from django.utils.timezone import is_naive
from django.utils.timezone import get_default_timezone, is_naive, make_aware
def add_domain(domain, url, secure=False):
@ -186,15 +186,15 @@ class Feed(object):
else:
author_email = author_link = None
tz = get_default_timezone()
pubdate = self.__get_dynamic_attr('item_pubdate', item)
if pubdate and is_naive(pubdate):
ltz = tzinfo.LocalTimezone(pubdate)
pubdate = pubdate.replace(tzinfo=ltz)
pubdate = make_aware(pubdate, tz)
updateddate = self.__get_dynamic_attr('item_updateddate', item)
if updateddate and is_naive(updateddate):
ltz = tzinfo.LocalTimezone(updateddate)
updateddate = updateddate.replace(tzinfo=ltz)
updateddate = make_aware(updateddate, tz)
feed.add_item(
title = title,

View File

@ -96,27 +96,27 @@ class BaseCache(object):
Returns True if the value was stored, False otherwise.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseCache must provide an add() method')
def get(self, key, default=None, version=None):
"""
Fetch a given key from the cache. If the key does not exist, return
default, which itself defaults to None.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseCache must provide a get() method')
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
"""
Set a value in the cache. If timeout is given, that timeout will be
used for the key; otherwise the default cache timeout will be used.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseCache must provide a set() method')
def delete(self, key, version=None):
"""
Delete a key from the cache, failing silently.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseCache must provide a delete() method')
def get_many(self, keys, version=None):
"""
@ -190,7 +190,7 @@ class BaseCache(object):
def clear(self):
"""Remove *all* values from the cache at once."""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseCache must provide a clear() method')
def validate_key(self, key):
"""

View File

@ -92,55 +92,55 @@ class Storage(object):
"""
Deletes the specified file from the storage system.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Storage must provide a delete() method')
def exists(self, name):
"""
Returns True if a file referened by the given name already exists in the
storage system, or False if the name is available for a new file.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Storage must provide a exists() method')
def listdir(self, path):
"""
Lists the contents of the specified path, returning a 2-tuple of lists;
the first item being directories, the second item being files.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Storage must provide a listdir() method')
def size(self, name):
"""
Returns the total size, in bytes, of the file specified by name.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Storage must provide a size() method')
def url(self, name):
"""
Returns an absolute URL where the file's contents can be accessed
directly by a Web browser.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Storage must provide a url() method')
def accessed_time(self, name):
"""
Returns the last accessed time (as datetime object) of the file
specified by name.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Storage must provide an accessed_time() method')
def created_time(self, name):
"""
Returns the creation time (as datetime object) of the file
specified by name.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Storage must provide a created_time() method')
def modified_time(self, name):
"""
Returns the last modified time (as datetime object) of the file
specified by name.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Storage must provide a modified_time() method')
class FileSystemStorage(Storage):
"""
@ -215,6 +215,7 @@ class FileSystemStorage(Storage):
_file = os.fdopen(fd, mode)
_file.write(chunk)
finally:
content.close()
locks.unlock(fd)
if _file is not None:
_file.close()

View File

@ -46,6 +46,7 @@ class UploadedFile(File):
# File names longer than 255 characters can cause problems on older OSes.
if len(name) > 255:
name, ext = os.path.splitext(name)
ext = ext[:255]
name = name[:255 - len(ext)] + ext
self._name = name

View File

@ -104,7 +104,7 @@ class FileUploadHandler(object):
Receive data from the streamed upload parser. ``start`` is the position
in the file of the chunk.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of FileUploadHandler must provide a recieve_data_chunk() method')
def file_complete(self, file_size):
"""
@ -113,7 +113,7 @@ class FileUploadHandler(object):
Subclasses should return a valid ``UploadedFile`` object.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of FileUploadHandler must provide a file_complete() method')
def upload_complete(self):
"""

View File

@ -36,4 +36,4 @@ class BaseEmailBackend(object):
Sends one or more EmailMessage objects and returns the number of email
messages sent.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseEmailBackend must override send_messages() method')

View File

@ -325,7 +325,7 @@ class BaseCommand(object):
this method.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseCommand must provide a handle() method')
class AppCommand(BaseCommand):
@ -361,7 +361,7 @@ class AppCommand(BaseCommand):
the command line.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of AppCommand must provide a handle_app() method')
class LabelCommand(BaseCommand):
@ -397,7 +397,7 @@ class LabelCommand(BaseCommand):
string as given on the command line.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of LabelCommand must provide a handle_label() method')
class NoArgsCommand(BaseCommand):
@ -423,4 +423,4 @@ class NoArgsCommand(BaseCommand):
Perform this command's actions.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of NoArgsCommand must provide a handle_noargs() method')

View File

@ -104,8 +104,11 @@ class Command(NoArgsCommand):
# Don't output 'id = meta.AutoField(primary_key=True)', because
# that's assumed if it doesn't exist.
if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}:
continue
if att_name == 'id' and extra_params == {'primary_key': True}:
if field_type == 'AutoField(':
continue
elif field_type == 'IntegerField(' and not connection.features.can_introspect_autofield:
comment_notes.append('AutoField?')
# Add 'null' and 'blank', if the 'null_ok' flag was present in the
# table description.
@ -117,7 +120,12 @@ class Command(NoArgsCommand):
if not field_type in ('TextField(', 'CharField('):
extra_params['null'] = True
field_desc = '%s = models.%s' % (att_name, field_type)
field_desc = '%s = %s%s' % (
att_name,
# Custom fields will have a dotted path
'' if '.' in field_type else 'models.',
field_type,
)
if extra_params:
if not field_desc.endswith('('):
field_desc += ', '

View File

@ -65,7 +65,7 @@ class Serializer(object):
"""
Called when serializing of the queryset starts.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of Serializer must provide a start_serialization() method')
def end_serialization(self):
"""
@ -77,7 +77,7 @@ class Serializer(object):
"""
Called when serializing of an object starts.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of Serializer must provide a start_object() method')
def end_object(self, obj):
"""
@ -89,19 +89,19 @@ class Serializer(object):
"""
Called to handle each individual (non-relational) field on an object.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of Serializer must provide an handle_field() method')
def handle_fk_field(self, obj, field):
"""
Called to handle a ForeignKey field.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of Serializer must provide an handle_fk_field() method')
def handle_m2m_field(self, obj, field):
"""
Called to handle a ManyToManyField.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of Serializer must provide an handle_m2m_field() method')
def getvalue(self):
"""
@ -135,7 +135,7 @@ class Deserializer(six.Iterator):
def __next__(self):
"""Iteration iterface -- return the next item in the stream"""
raise NotImplementedError
raise NotImplementedError('subclasses of Deserializer must provide a __next__() method')
class DeserializedObject(object):
"""

View File

@ -84,19 +84,19 @@ class BaseDatabaseWrapper(object):
def get_connection_params(self):
"""Returns a dict of parameters suitable for get_new_connection."""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a get_connection_params() method')
def get_new_connection(self, conn_params):
"""Opens a connection to the database."""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a get_new_connection() method')
def init_connection_state(self):
"""Initializes the database connection settings."""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an init_connection_state() method')
def create_cursor(self):
"""Creates a cursor. Assumes that a connection is established."""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a create_cursor() method')
##### Backend-specific methods for creating connections #####
@ -262,7 +262,7 @@ class BaseDatabaseWrapper(object):
"""
Backend-specific implementation to enable or disable autocommit.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a _set_autocommit() method')
##### Generic transaction management methods #####
@ -440,7 +440,7 @@ class BaseDatabaseWrapper(object):
Tests if the database connection is usable.
This function may assume that self.connection is not None.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an is_usable() method')
def close_if_unusable_or_obsolete(self):
"""
@ -519,11 +519,11 @@ class BaseDatabaseWrapper(object):
"""
Only required when autocommits_when_autocommit_is_off = True.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a _start_transaction_under_autocommit() method')
def schema_editor(self, *args, **kwargs):
"Returns a new instance of this backend's SchemaEditor"
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a schema_editor() method')
class BaseDatabaseFeatures(object):
@ -627,6 +627,9 @@ class BaseDatabaseFeatures(object):
# which can't do it for MyISAM tables
can_introspect_foreign_keys = True
# Can the backend introspect an AutoField, instead of an IntegerField?
can_introspect_autofield = False
# Support for the DISTINCT ON clause
can_distinct_on_fields = False
@ -741,13 +744,13 @@ class BaseDatabaseOperations(object):
Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
extracts a value from the given date field field_name.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseOperations may require a date_extract_sql() method')
def date_interval_sql(self, sql, connector, timedelta):
"""
Implements the date interval functionality for expressions
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseOperations may require a date_interval_sql() method')
def date_trunc_sql(self, lookup_type, field_name):
"""
@ -755,7 +758,7 @@ class BaseDatabaseOperations(object):
truncates the given date field field_name to a date object with only
the given specificity.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetrunc_sql() method')
def datetime_cast_sql(self):
"""
@ -772,7 +775,7 @@ class BaseDatabaseOperations(object):
'second', returns the SQL that extracts a value from the given
datetime field field_name, and a tuple of parameters.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_extract_sql() method')
def datetime_trunc_sql(self, lookup_type, field_name, tzname):
"""
@ -781,7 +784,7 @@ class BaseDatabaseOperations(object):
field_name to a datetime object with only the given specificity, and
a tuple of parameters.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_trunk_sql() method')
def deferrable_sql(self):
"""
@ -916,7 +919,7 @@ class BaseDatabaseOperations(object):
Returns the value to use for the LIMIT when we are wanting "LIMIT
infinity". Returns None if the limit clause can be omitted in this case.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseOperations may require a no_limit_value() method')
def pk_default_value(self):
"""
@ -956,7 +959,7 @@ class BaseDatabaseOperations(object):
Returns a quoted version of the given table, index or column name. Does
not quote the given name if it's already been quoted.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseOperations may require a quote_name() method')
def quote_parameter(self, value):
"""
@ -982,7 +985,7 @@ class BaseDatabaseOperations(object):
If the feature is not supported (or part of it is not supported), a
NotImplementedError exception can be raised.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseOperations may require a regex_lookup() method')
def savepoint_create_sql(self, sid):
"""
@ -1028,7 +1031,7 @@ class BaseDatabaseOperations(object):
to tables with foreign keys pointing the tables being truncated.
PostgreSQL requires a cascade even if these tables are empty.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseOperations must provide a sql_flush() method')
def sequence_reset_by_name_sql(self, style, sequences):
"""
@ -1245,7 +1248,7 @@ class BaseDatabaseIntrospection(object):
Returns an unsorted list of names of all tables that exist in the
database.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_table_list() method')
def django_table_names(self, only_existing=False):
"""
@ -1322,7 +1325,7 @@ class BaseDatabaseIntrospection(object):
Backends can override this to return a list of (column_name, referenced_table_name,
referenced_column_name) for all key columns in given table.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_key_columns() method')
def get_primary_key_column(self, cursor, table_name):
"""
@ -1342,7 +1345,7 @@ class BaseDatabaseIntrospection(object):
Only single-column indexes are introspected.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_indexes() method')
def get_constraints(self, cursor, table_name):
"""
@ -1361,7 +1364,7 @@ class BaseDatabaseIntrospection(object):
Some backends may return special constraint names that don't exist
if they don't name constraints of a certain type (e.g. SQLite)
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_constraints() method')
class BaseDatabaseClient(object):
@ -1378,7 +1381,7 @@ class BaseDatabaseClient(object):
self.connection = connection
def runshell(self):
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseClient must provide a runshell() method')
class BaseDatabaseValidation(object):

View File

@ -181,6 +181,7 @@ class DatabaseCreation(BaseDatabaseCreation):
IDENTIFIED BY %(password)s
DEFAULT TABLESPACE %(tblspace)s
TEMPORARY TABLESPACE %(tblspace_temp)s
QUOTA UNLIMITED ON %(tblspace)s
""",
"""GRANT CONNECT, RESOURCE TO %(user)s""",
]

View File

@ -148,7 +148,7 @@ class BaseDatabaseSchemaEditor(object):
"""
Only used for backends which have requires_literal_defaults feature
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of BaseDatabaseSchemaEditor for backends which have requires_literal_defaults must provide a prepare_default() method')
def effective_default(self, field):
"""

View File

@ -38,14 +38,14 @@ class Operation(object):
Takes the state from the previous migration, and mutates it
so that it matches what this migration would perform.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Operation must provide a state_forwards() method')
def database_forwards(self, app_label, schema_editor, from_state, to_state):
"""
Performs the mutation on the database schema in the normal
(forwards) direction.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Operation must provide a database_forwards() method')
def database_backwards(self, app_label, schema_editor, from_state, to_state):
"""
@ -53,7 +53,7 @@ class Operation(object):
direction - e.g. if this were CreateModel, it would in fact
drop the model's table.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Operation must provide a database_backwards() method')
def describe(self):
"""

View File

@ -73,6 +73,26 @@ class MigrationWriter(object):
raise ImportError("Cannot open migrations module %s for app %s" % (migrations_module_name, self.migration.app_label))
return os.path.join(basedir, self.filename)
@classmethod
def serialize_deconstructed(cls, path, args, kwargs):
module, name = path.rsplit(".", 1)
if module == "django.db.models":
imports = set(["from django.db import models"])
name = "models.%s" % name
else:
imports = set(["import %s" % module])
name = path
arg_strings = []
for arg in args:
arg_string, arg_imports = cls.serialize(arg)
arg_strings.append(arg_string)
imports.update(arg_imports)
for kw, arg in kwargs.items():
arg_string, arg_imports = cls.serialize(arg)
imports.update(arg_imports)
arg_strings.append("%s=%s" % (kw, arg_string))
return "%s(%s)" % (name, ", ".join(arg_strings)), imports
@classmethod
def serialize(cls, value):
"""
@ -119,23 +139,7 @@ class MigrationWriter(object):
# Django fields
elif isinstance(value, models.Field):
attr_name, path, args, kwargs = value.deconstruct()
module, name = path.rsplit(".", 1)
if module == "django.db.models":
imports = set(["from django.db import models"])
name = "models.%s" % name
else:
imports = set(["import %s" % module])
name = path
arg_strings = []
for arg in args:
arg_string, arg_imports = cls.serialize(arg)
arg_strings.append(arg_string)
imports.update(arg_imports)
for kw, arg in kwargs.items():
arg_string, arg_imports = cls.serialize(arg)
imports.update(arg_imports)
arg_strings.append("%s=%s" % (kw, arg_string))
return "%s(%s)" % (name, ", ".join(arg_strings)), imports
return cls.serialize_deconstructed(path, args, kwargs)
# Functions
elif isinstance(value, (types.FunctionType, types.BuiltinFunctionType)):
# Special-cases, as these don't have im_class
@ -152,6 +156,8 @@ class MigrationWriter(object):
klass = value.im_class
module = klass.__module__
return "%s.%s.%s" % (module, klass.__name__, value.__name__), set(["import %s" % module])
elif hasattr(value, 'deconstruct'):
return cls.serialize_deconstructed(*value.deconstruct())
elif value.__name__ == '<lambda>':
raise ValueError("Cannot serialize function: lambda")
elif value.__module__ is None:

View File

@ -35,10 +35,12 @@ def SET(value):
else:
def set_on_delete(collector, field, sub_objs, using):
collector.add_field_update(field, value, sub_objs)
set_on_delete.deconstruct = lambda: ('django.db.models.SET', (value,), {})
return set_on_delete
SET_NULL = SET(None)
def SET_NULL(collector, field, sub_objs, using):
collector.add_field_update(field, None, sub_objs)
def SET_DEFAULT(collector, field, sub_objs, using):

View File

@ -391,7 +391,7 @@ class SQLCompiler(object):
if not distinct or elt in select_aliases:
result.append('%s %s' % (elt, order))
group_by.append((elt, []))
elif get_order_dir(field)[0] not in self.query.extra:
elif not self.query._extra or get_order_dir(field)[0] not in self.query._extra:
# 'col' is of the form 'field' or 'field1__field2' or
# '-field1__field2__field', etc.
for table, cols, order in self.find_ordering_name(field,
@ -987,7 +987,7 @@ class SQLUpdateCompiler(SQLCompiler):
# We need to use a sub-select in the where clause to filter on things
# from other tables.
query = self.query.clone(klass=Query)
query.extra = {}
query._extra = {}
query.select = []
query.add_fields([query.get_meta().pk.name])
# Recheck the count - it is possible that fiddling with the select

View File

@ -143,7 +143,10 @@ class Query(object):
self.select_related = False
# SQL aggregate-related attributes
self.aggregates = OrderedDict() # Maps alias -> SQL aggregate function
# The _aggregates will be an OrderedDict when used. Due to the cost
# of creating OrderedDict this attribute is created lazily (in
# self.aggregates property).
self._aggregates = None # Maps alias -> SQL aggregate function
self.aggregate_select_mask = None
self._aggregate_select_cache = None
@ -153,7 +156,9 @@ class Query(object):
# These are for extensions. The contents are more or less appended
# verbatim to the appropriate clause.
self.extra = OrderedDict() # Maps col_alias -> (col_sql, params).
# The _extra attribute is an OrderedDict, lazily created similarly to
# .aggregates
self._extra = None # Maps col_alias -> (col_sql, params).
self.extra_select_mask = None
self._extra_select_cache = None
@ -165,6 +170,18 @@ class Query(object):
# load.
self.deferred_loading = (set(), True)
@property
def extra(self):
if self._extra is None:
self._extra = OrderedDict()
return self._extra
@property
def aggregates(self):
if self._aggregates is None:
self._aggregates = OrderedDict()
return self._aggregates
def __str__(self):
"""
Returns the query as a string of SQL with the parameter values
@ -245,7 +262,7 @@ class Query(object):
obj.select_for_update_nowait = self.select_for_update_nowait
obj.select_related = self.select_related
obj.related_select_cols = []
obj.aggregates = self.aggregates.copy()
obj._aggregates = self._aggregates.copy() if self._aggregates is not None else None
if self.aggregate_select_mask is None:
obj.aggregate_select_mask = None
else:
@ -257,7 +274,7 @@ class Query(object):
# used.
obj._aggregate_select_cache = None
obj.max_depth = self.max_depth
obj.extra = self.extra.copy()
obj._extra = self._extra.copy() if self._extra is not None else None
if self.extra_select_mask is None:
obj.extra_select_mask = None
else:
@ -344,7 +361,7 @@ class Query(object):
# and move them to the outer AggregateQuery.
for alias, aggregate in self.aggregate_select.items():
if aggregate.is_summary:
query.aggregate_select[alias] = aggregate.relabeled_clone(relabels)
query.aggregates[alias] = aggregate.relabeled_clone(relabels)
del obj.aggregate_select[alias]
try:
@ -358,7 +375,7 @@ class Query(object):
query = self
self.select = []
self.default_cols = False
self.extra = {}
self._extra = {}
self.remove_inherited_models()
query.clear_ordering(True)
@ -527,7 +544,7 @@ class Query(object):
# It would be nice to be able to handle this, but the queries don't
# really make sense (or return consistent value sets). Not worth
# the extra complexity when you can write a real query instead.
if self.extra and rhs.extra:
if self._extra and rhs._extra:
raise ValueError("When merging querysets using 'or', you "
"cannot have extra(select=...) on both sides.")
self.extra.update(rhs.extra)
@ -756,8 +773,9 @@ class Query(object):
self.group_by = [relabel_column(col) for col in self.group_by]
self.select = [SelectInfo(relabel_column(s.col), s.field)
for s in self.select]
self.aggregates = OrderedDict(
(key, relabel_column(col)) for key, col in self.aggregates.items())
if self._aggregates:
self._aggregates = OrderedDict(
(key, relabel_column(col)) for key, col in self._aggregates.items())
# 2. Rename the alias in the internal table/alias datastructures.
for ident, aliases in self.join_map.items():
@ -967,7 +985,7 @@ class Query(object):
"""
opts = model._meta
field_list = aggregate.lookup.split(LOOKUP_SEP)
if len(field_list) == 1 and aggregate.lookup in self.aggregates:
if len(field_list) == 1 and self._aggregates and aggregate.lookup in self.aggregates:
# Aggregate is over an annotation
field_name = field_list[0]
col = field_name
@ -1049,7 +1067,7 @@ class Query(object):
lookup_parts = lookup.split(LOOKUP_SEP)
num_parts = len(lookup_parts)
if (len(lookup_parts) > 1 and lookup_parts[-1] in self.query_terms
and lookup not in self.aggregates):
and (not self._aggregates or lookup not in self._aggregates)):
# Traverse the lookup query to distinguish related fields from
# lookup types.
lookup_model = self.model
@ -1108,10 +1126,11 @@ class Query(object):
value, lookup_type = self.prepare_lookup_value(value, lookup_type, can_reuse)
clause = self.where_class()
for alias, aggregate in self.aggregates.items():
if alias in (parts[0], LOOKUP_SEP.join(parts)):
clause.add((aggregate, lookup_type, value), AND)
return clause
if self._aggregates:
for alias, aggregate in self.aggregates.items():
if alias in (parts[0], LOOKUP_SEP.join(parts)):
clause.add((aggregate, lookup_type, value), AND)
return clause
opts = self.get_meta()
alias = self.get_initial_alias()
@ -1170,6 +1189,8 @@ class Query(object):
Returns whether or not all elements of this q_object need to be put
together in the HAVING clause.
"""
if not self._aggregates:
return False
if not isinstance(obj, Node):
return (refs_aggregate(obj[0].split(LOOKUP_SEP), self.aggregates)
or (hasattr(obj[1], 'contains_aggregate')
@ -1632,7 +1653,7 @@ class Query(object):
# Set only aggregate to be the count column.
# Clear out the select cache to reflect the new unmasked aggregates.
self.aggregates = {None: count}
self._aggregates = {None: count}
self.set_aggregate_mask(None)
self.group_by = None
@ -1781,7 +1802,8 @@ class Query(object):
self.extra_select_mask = set(names)
self._extra_select_cache = None
def _aggregate_select(self):
@property
def aggregate_select(self):
"""The OrderedDict of aggregate columns that are not masked, and should
be used in the SELECT clause.
@ -1789,6 +1811,8 @@ class Query(object):
"""
if self._aggregate_select_cache is not None:
return self._aggregate_select_cache
elif not self._aggregates:
return {}
elif self.aggregate_select_mask is not None:
self._aggregate_select_cache = OrderedDict(
(k, v) for k, v in self.aggregates.items()
@ -1797,11 +1821,13 @@ class Query(object):
return self._aggregate_select_cache
else:
return self.aggregates
aggregate_select = property(_aggregate_select)
def _extra_select(self):
@property
def extra_select(self):
if self._extra_select_cache is not None:
return self._extra_select_cache
if not self._extra:
return {}
elif self.extra_select_mask is not None:
self._extra_select_cache = OrderedDict(
(k, v) for k, v in self.extra.items()
@ -1810,7 +1836,6 @@ class Query(object):
return self._extra_select_cache
else:
return self.extra
extra_select = property(_extra_select)
def trim_start(self, names_with_path):
"""

View File

@ -977,6 +977,11 @@ class MultiValueField(Field):
f.required = False
self.fields = fields
def __deepcopy__(self, memo):
result = super(MultiValueField, self).__deepcopy__(memo)
result.fields = tuple([x.__deepcopy__(memo) for x in self.fields])
return result
def validate(self, value):
pass

View File

@ -185,7 +185,8 @@ class BaseForm(object):
'label': force_text(label),
'field': six.text_type(bf),
'help_text': help_text,
'html_class_attr': html_class_attr
'html_class_attr': html_class_attr,
'field_name': bf.html_name,
})
if top_errors:

View File

@ -23,11 +23,18 @@ def flatatt(attrs):
The result is passed through 'mark_safe'.
"""
if [v for v in attrs.values() if v is True or v is False]:
warnings.warn(
'The meaning of boolean values for widget attributes will change in Django 1.8',
DeprecationWarning
)
for attr_name, value in attrs.items():
if type(value) is bool:
warnings.warn(
"In Django 1.8, widget attribute %(attr_name)s=%(bool_value)s "
"will %(action)s. To preserve current behavior, use the "
"string '%(bool_value)s' instead of the boolean value." % {
'attr_name': attr_name,
'action': "be rendered as '%s'" % attr_name if value else "not be rendered",
'bool_value': value,
},
DeprecationWarning
)
return format_html_join('', ' {0}="{1}"', sorted(attrs.items()))
@python_2_unicode_compatible

View File

@ -190,7 +190,7 @@ class Widget(six.with_metaclass(MediaDefiningClass)):
The 'value' given is not guaranteed to be valid input, so subclass
implementations should program defensively.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of Widget must provide a render() method')
def build_attrs(self, extra_attrs=None, **kwargs):
"Helper function for building an attribute dictionary."

View File

@ -64,6 +64,8 @@ else:
M.set(key, real_value, coded_value)
dict.__setitem__(self, key, M)
except http_cookies.CookieError:
if not hasattr(self, 'bad_cookies'):
self.bad_cookies = set()
self.bad_cookies.add(key)
dict.__setitem__(self, key, http_cookies.Morsel())

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
import datetime
import time
import sys
from email.header import Header
try:
from urllib.parse import urlparse
@ -160,7 +161,7 @@ class HttpResponseBase(six.Iterator):
except UnicodeError as e:
if mime_encode:
# Wrapping in str() is a workaround for #12422 under Python 2.
value = str(Header(value, 'utf-8').encode())
value = str(Header(value, 'utf-8', maxlinelen=sys.maxsize).encode())
else:
e.reason += ', HTTP response headers must be in %s format' % charset
raise

View File

@ -99,7 +99,7 @@ class Origin(object):
self.name = name
def reload(self):
raise NotImplementedError
raise NotImplementedError('subclasses of Origin must provide a reload() method')
def __str__(self):
return self.name
@ -385,7 +385,7 @@ class TokenParser(object):
"""
Overload this method to do the actual parsing and return the result.
"""
raise NotImplementedError()
raise NotImplementedError('subclasses of Tokenparser must provide a top() method')
def more(self):
"""
@ -622,34 +622,17 @@ class FilterExpression(object):
def args_check(name, func, provided):
provided = list(provided)
plen = len(provided)
# First argument, filter input, is implied.
plen = len(provided) + 1
# Check to see if a decorator is providing the real function.
func = getattr(func, '_decorated_function', func)
args, varargs, varkw, defaults = getargspec(func)
# First argument is filter input.
args.pop(0)
if defaults:
nondefs = args[:-len(defaults)]
else:
nondefs = args
# Args without defaults must be provided.
try:
for arg in nondefs:
provided.pop(0)
except IndexError:
# Not enough
alen = len(args)
dlen = len(defaults or [])
# Not enough OR Too many
if plen < (alen - dlen) or plen > alen:
raise TemplateSyntaxError("%s requires %d arguments, %d provided" %
(name, len(nondefs), plen))
# Defaults can be overridden.
defaults = list(defaults) if defaults else []
try:
for parg in provided:
defaults.pop(0)
except IndexError:
# Too many.
raise TemplateSyntaxError("%s requires %d arguments, %d provided" %
(name, len(nondefs), plen))
(name, alen - dlen, plen))
return True
args_check = staticmethod(args_check)

View File

@ -1,6 +1,7 @@
"""Default tags used by the template system, available to all templates."""
from __future__ import unicode_literals
import os
import sys
import re
from datetime import datetime
@ -328,6 +329,7 @@ class RegroupNode(Node):
return ''
def include_is_allowed(filepath):
filepath = os.path.abspath(filepath)
for root in settings.ALLOWED_INCLUDE_ROOTS:
if filepath.startswith(root):
return True

View File

@ -61,7 +61,7 @@ class BaseLoader(object):
name.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of BaseLoader must provide a load_template_source() method')
def reset(self):
"""

View File

@ -7,4 +7,4 @@ from django.test.testcases import (TestCase, TransactionTestCase,
SimpleTestCase, LiveServerTestCase, skipIfDBFeature,
skipUnlessDBFeature
)
from django.test.utils import Approximate
from django.test.utils import override_settings

View File

@ -14,6 +14,8 @@ class DiscoverRunner(object):
A Django test runner that uses unittest2 test discovery.
"""
test_suite = TestSuite
test_runner = unittest.TextTestRunner
test_loader = defaultTestLoader
reorder_by = (TestCase, )
option_list = (
@ -42,7 +44,7 @@ class DiscoverRunner(object):
unittest.installHandler()
def build_suite(self, test_labels=None, extra_tests=None, **kwargs):
suite = TestSuite()
suite = self.test_suite()
test_labels = test_labels or ['.']
extra_tests = extra_tests or []
@ -107,7 +109,7 @@ class DiscoverRunner(object):
return setup_databases(self.verbosity, self.interactive, **kwargs)
def run_suite(self, suite, **kwargs):
return unittest.TextTestRunner(
return self.test_runner(
verbosity=self.verbosity,
failfast=self.failfast,
).run(suite)
@ -201,7 +203,8 @@ def reorder_suite(suite, classes):
classes[1], etc. Tests with no match in classes are placed last.
"""
class_count = len(classes)
bins = [unittest.TestSuite() for i in range(class_count+1)]
suite_class = type(suite)
bins = [suite_class() for i in range(class_count+1)]
partition_suite(suite, classes, bins)
for i in range(class_count):
bins[0].addTests(bins[i+1])
@ -218,8 +221,9 @@ def partition_suite(suite, classes, bins):
Tests of type classes[i] are added to bins[i],
tests with no match found in classes are place in bins[-1]
"""
suite_class = type(suite)
for test in suite:
if isinstance(test, unittest.TestSuite):
if isinstance(test, suite_class):
partition_suite(test, classes, bins)
else:
for i in range(len(classes)):

View File

@ -225,12 +225,14 @@ class SimpleTestCase(unittest.TestCase):
return override_settings(**kwargs)
def assertRedirects(self, response, expected_url, status_code=302,
target_status_code=200, host=None, msg_prefix=''):
target_status_code=200, host=None, msg_prefix='',
fetch_redirect_response=True):
"""Asserts that a response redirected to a specific URL, and that the
redirect URL can be loaded.
Note that assertRedirects won't work for external links since it uses
TestClient to do a request.
TestClient to do a request (use fetch_redirect_response=False to check
such links without fetching thtem).
"""
if msg_prefix:
msg_prefix += ": "
@ -264,14 +266,15 @@ class SimpleTestCase(unittest.TestCase):
url = response.url
scheme, netloc, path, query, fragment = urlsplit(url)
redirect_response = response.client.get(path, QueryDict(query))
if fetch_redirect_response:
redirect_response = response.client.get(path, QueryDict(query))
# Get the redirection page, using the same client that was used
# to obtain the original response.
self.assertEqual(redirect_response.status_code, target_status_code,
msg_prefix + "Couldn't retrieve redirection page '%s':"
" response code was %d (expected %d)" %
(path, redirect_response.status_code, target_status_code))
# Get the redirection page, using the same client that was used
# to obtain the original response.
self.assertEqual(redirect_response.status_code, target_status_code,
msg_prefix + "Couldn't retrieve redirection page '%s':"
" response code was %d (expected %d)" %
(path, redirect_response.status_code, target_status_code))
e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(
expected_url)
@ -696,6 +699,9 @@ class TransactionTestCase(SimpleTestCase):
# Subclasses can enable only a subset of apps for faster tests
available_apps = None
# Subclasses can define fixtures which will be automatically installed.
fixtures = None
def _pre_setup(self):
"""Performs any pre-test setup. This includes:
@ -743,7 +749,7 @@ class TransactionTestCase(SimpleTestCase):
if self.reset_sequences:
self._reset_sequences(db_name)
if hasattr(self, 'fixtures'):
if self.fixtures:
# We have to use this slightly awkward syntax due to the fact
# that we're using *args and **kwargs together.
call_command('loaddata', *self.fixtures,
@ -835,7 +841,7 @@ class TestCase(TransactionTestCase):
disable_transaction_methods()
for db_name in self._databases_names(include_mirrors=False):
if hasattr(self, 'fixtures'):
if self.fixtures:
try:
call_command('loaddata', *self.fixtures,
**{

View File

@ -126,10 +126,10 @@ class BaseArchive(object):
return True
def extract(self):
raise NotImplementedError
raise NotImplementedError('subclasses of BaseArchive must provide an extract() method')
def list(self):
raise NotImplementedError
raise NotImplementedError('subclasses of BaseArchive must provide a list() method')
class TarArchive(BaseArchive):

View File

@ -18,11 +18,10 @@ import calendar
import datetime
from django.utils.dates import MONTHS, MONTHS_3, MONTHS_ALT, MONTHS_AP, WEEKDAYS, WEEKDAYS_ABBR
from django.utils.tzinfo import LocalTimezone
from django.utils.translation import ugettext as _
from django.utils.encoding import force_text
from django.utils import six
from django.utils.timezone import is_aware, is_naive
from django.utils.timezone import get_default_timezone, is_aware, is_naive
re_formatchars = re.compile(r'(?<!\\)([aAbBcdDeEfFgGhHiIjlLmMnNoOPrsStTUuwWyYzZ])')
re_escaped = re.compile(r'\\(.)')
@ -48,7 +47,7 @@ class TimeFormat(Formatter):
# or time objects (against established django policy).
if isinstance(obj, datetime.datetime):
if is_naive(obj):
self.timezone = LocalTimezone(obj)
self.timezone = get_default_timezone()
else:
self.timezone = obj.tzinfo
@ -66,7 +65,7 @@ class TimeFormat(Formatter):
def B(self):
"Swatch Internet time"
raise NotImplementedError
raise NotImplementedError('may be implemented in a future release')
def e(self):
"""

View File

@ -8,8 +8,8 @@
import datetime
import re
from django.utils import six
from django.utils.timezone import utc
from django.utils.tzinfo import FixedOffset
from django.utils.timezone import utc, get_fixed_timezone
date_re = re.compile(
r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$'
@ -27,6 +27,7 @@ datetime_re = re.compile(
r'(?P<tzinfo>Z|[+-]\d{2}:?\d{2})?$'
)
def parse_date(value):
"""Parses a string and return a datetime.date.
@ -59,7 +60,7 @@ def parse_datetime(value):
"""Parses a string and return a datetime.datetime.
This function supports time zone offsets. When the input contains one,
the output uses an instance of FixedOffset as tzinfo.
the output uses a timezone with a fixed offset from UTC.
Raises ValueError if the input is well formatted but not a valid datetime.
Returns None if the input isn't well formatted.
@ -76,7 +77,7 @@ def parse_datetime(value):
offset = 60 * int(tzinfo[1:3]) + int(tzinfo[-2:])
if tzinfo[0] == '-':
offset = -offset
tzinfo = FixedOffset(offset)
tzinfo = get_fixed_timezone(offset)
kw = dict((k, int(v)) for k, v in six.iteritems(kw) if v is not None)
kw['tzinfo'] = tzinfo
return datetime.datetime(**kw)

View File

@ -177,7 +177,7 @@ class SyndicationFeed(object):
Outputs the feed in the given encoding to outfile, which is a file-like
object. Subclasses should override this.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of SyndicationFeed must provide a write() method')
def writeString(self, encoding):
"""

View File

@ -257,7 +257,7 @@ class LazyObject(object):
"""
Must be implemented by subclasses to initialise the wrapped object.
"""
raise NotImplementedError
raise NotImplementedError('subclasses of LazyObject must provide a _setup() method')
# Introspection support
__dir__ = new_method_proxy(dir)

View File

@ -84,24 +84,22 @@ class AdminEmailHandler(logging.Handler):
record.getMessage()
)
filter = get_exception_reporter_filter(request)
request_repr = filter.get_request_repr(request)
request_repr = '\n{0}'.format(filter.get_request_repr(request))
except Exception:
subject = '%s: %s' % (
record.levelname,
record.getMessage()
)
request = None
request_repr = "Request repr() unavailable."
request_repr = "unavailable"
subject = self.format_subject(subject)
if record.exc_info:
exc_info = record.exc_info
stack_trace = '\n'.join(traceback.format_exception(*record.exc_info))
else:
exc_info = (None, record.getMessage(), None)
stack_trace = 'No stack trace available'
message = "%s\n\n%s" % (stack_trace, request_repr)
message = "%s\n\nRequest repr(): %s" % (self.format(record), request_repr)
reporter = ExceptionReporter(request, is_email=True, *exc_info)
html_message = reporter.get_traceback_html() if self.include_html else None
mail.mail_admins(subject, message, fail_silently=True,

View File

@ -1,5 +1,6 @@
from __future__ import absolute_import # Avoid importing `importlib` from this package.
import copy
import imp
from importlib import import_module
import os
@ -34,6 +35,43 @@ def import_by_path(dotted_path, error_prefix=''):
return attr
def autodiscover_modules(*args, **kwargs):
"""
Auto-discover INSTALLED_APPS modules and fail silently when
not present. This forces an import on them to register any admin bits they
may want.
You may provide a register_to keyword parameter as a way to access a
registry. This register_to object must have a _registry instance variable
to access it.
"""
from django.conf import settings
register_to = kwargs.get('register_to')
for app in settings.INSTALLED_APPS:
mod = import_module(app)
# Attempt to import the app's module.
try:
if register_to:
before_import_registry = copy.copy(register_to._registry)
for module_to_search in args:
import_module('%s.%s' % (app, module_to_search))
except:
# Reset the model registry to the state before the last import as
# this import will have to reoccur on the next request and this
# could raise NotRegistered and AlreadyRegistered exceptions
# (see #8245).
if register_to:
register_to._registry = before_import_registry
# Decide whether to bubble up this error. If the app just
# doesn't have an admin module, we can ignore the error
# attempting to import it, otherwise we want it to bubble up.
if module_has_submodule(mod, module_to_search):
raise
def module_has_submodule(package, module_name):
"""See if 'module' is in 'package'."""
name = ".".join([package.__name__, module_name])

View File

@ -92,7 +92,7 @@ def normalize(pattern):
result.append(".")
elif ch == '|':
# FIXME: One day we'll should do this, but not in 1.0.
raise NotImplementedError
raise NotImplementedError('Awaiting Implementation')
elif ch == "^":
pass
elif ch == '$':

View File

@ -18,7 +18,8 @@ from django.conf import settings
from django.utils import six
__all__ = [
'utc', 'get_default_timezone', 'get_current_timezone',
'utc', 'get_fixed_timezone',
'get_default_timezone', 'get_current_timezone',
'activate', 'deactivate', 'override',
'is_naive', 'is_aware', 'make_aware', 'make_naive',
]
@ -47,19 +48,45 @@ class UTC(tzinfo):
def dst(self, dt):
return ZERO
class FixedOffset(tzinfo):
"""
Fixed offset in minutes east from UTC. Taken from Python's docs.
Kept as close as possible to the reference version. __init__ was changed
to make its arguments optional, according to Python's requirement that
tzinfo subclasses can be instantiated without arguments.
"""
def __init__(self, offset=None, name=None):
if offset is not None:
self.__offset = timedelta(minutes=offset)
if name is not None:
self.__name = name
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return self.__name
def dst(self, dt):
return ZERO
class ReferenceLocalTimezone(tzinfo):
"""
Local time implementation taken from Python's docs.
Local time. Taken from Python's docs.
Used only when pytz isn't available, and most likely inaccurate. If you're
having trouble with this class, don't waste your time, just install pytz.
Kept identical to the reference version. Subclasses contain improvements.
Kept as close as possible to the reference version. __init__ was added to
delay the computation of STDOFFSET, DSTOFFSET and DSTDIFF which is
performed at import time in the example.
Subclasses contain further improvements.
"""
def __init__(self):
# This code is moved in __init__ to execute it as late as possible
# See get_default_timezone().
self.STDOFFSET = timedelta(seconds=-_time.timezone)
if _time.daylight:
self.DSTOFFSET = timedelta(seconds=-_time.altzone)
@ -68,9 +95,6 @@ class ReferenceLocalTimezone(tzinfo):
self.DSTDIFF = self.DSTOFFSET - self.STDOFFSET
tzinfo.__init__(self)
def __repr__(self):
return "<LocalTimezone>"
def utcoffset(self, dt):
if self._isdst(dt):
return self.DSTOFFSET
@ -84,8 +108,7 @@ class ReferenceLocalTimezone(tzinfo):
return ZERO
def tzname(self, dt):
is_dst = False if dt is None else self._isdst(dt)
return _time.tzname[is_dst]
return _time.tzname[self._isdst(dt)]
def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day,
@ -103,6 +126,10 @@ class LocalTimezone(ReferenceLocalTimezone):
error message is helpful.
"""
def tzname(self, dt):
is_dst = False if dt is None else self._isdst(dt)
return _time.tzname[is_dst]
def _isdst(self, dt):
try:
return super(LocalTimezone, self)._isdst(dt)
@ -116,6 +143,17 @@ class LocalTimezone(ReferenceLocalTimezone):
utc = pytz.utc if pytz else UTC()
"""UTC time zone as a tzinfo instance."""
def get_fixed_timezone(offset):
"""
Returns a tzinfo instance with a fixed offset from UTC.
"""
if isinstance(offset, timedelta):
offset = offset.seconds // 60
sign = '-' if offset < 0 else '+'
hhmm = '%02d%02d' % divmod(abs(offset), 60)
name = sign + hhmm
return FixedOffset(offset, name)
# In order to avoid accessing the settings at compile time,
# wrap the expression in a function and cache the result.
_localtime = None
@ -125,8 +163,6 @@ def get_default_timezone():
Returns the default time zone as a tzinfo instance.
This is the time zone defined by settings.TIME_ZONE.
See also :func:`get_current_timezone`.
"""
global _localtime
if _localtime is None:

View File

@ -668,7 +668,10 @@ def parse_accept_lang_header(lang_string):
if first:
return []
if priority:
priority = float(priority)
try:
priority = float(priority)
except ValueError:
return []
if not priority: # if priority is 0.0 at this point make it 1.0
priority = 1.0
result.append((lang, priority))

View File

@ -2,11 +2,18 @@
from __future__ import unicode_literals
import time
from datetime import timedelta, tzinfo
import time
import warnings
from django.utils.encoding import force_str, force_text, DEFAULT_LOCALE_ENCODING
warnings.warn(
"django.utils.tzinfo will be removed in Django 1.9. "
"Use django.utils.timezone instead.",
PendingDeprecationWarning)
# Python's doc say: "A tzinfo subclass must have an __init__() method that can
# be called with no arguments". FixedOffset and LocalTimezone don't honor this
# requirement. Defining __getinitargs__ is sufficient to fix copy/deepcopy as
@ -15,6 +22,10 @@ from django.utils.encoding import force_str, force_text, DEFAULT_LOCALE_ENCODING
class FixedOffset(tzinfo):
"Fixed offset in minutes east from UTC."
def __init__(self, offset):
warnings.warn(
"django.utils.tzinfo.FixedOffset will be removed in Django 1.9. "
"Use django.utils.timezone.get_fixed_timezone instead.",
PendingDeprecationWarning)
if isinstance(offset, timedelta):
self.__offset = offset
offset = self.__offset.seconds // 60
@ -48,6 +59,10 @@ class FixedOffset(tzinfo):
class LocalTimezone(tzinfo):
"Proxy timezone information from time module."
def __init__(self, dt):
warnings.warn(
"django.utils.tzinfo.LocalTimezone will be removed in Django 1.9. "
"Use django.utils.timezone.get_default_timezone instead.",
PendingDeprecationWarning)
tzinfo.__init__(self)
self.__dt = dt
self._tzname = self.tzname(dt)

View File

@ -106,7 +106,7 @@ this by adding the following snippet to your urls.py::
Also this helper function only serves the actual :setting:`STATIC_ROOT`
folder; it doesn't perform static files discovery like
`:mod:`django.contrib.staticfiles`.
:mod:`django.contrib.staticfiles`.
Serving files uploaded by a user during development.
====================================================

View File

@ -34,6 +34,8 @@ Decisions on new committers will follow the process explained in
existing committer privately. Public requests for commit access are potential
flame-war starters, and will simply be ignored.
.. _handling-pull-requests:
Handling pull requests
----------------------

View File

@ -212,7 +212,7 @@ This way your branch will contain only commits related to its topic, which
makes squashing easier.
After review
------------
~~~~~~~~~~~~
It is unusual to get any non-trivial amount of code into core without changes
requested by reviewers. In this case, it is often a good idea to add the
@ -225,7 +225,8 @@ commits, you would run::
git rebase -i HEAD~2
Squash the second commit into the first. Write a commit message along the lines of::
Squash the second commit into the first. Write a commit message along the lines
of::
Made changes asked in review by <reviewer>
@ -239,8 +240,25 @@ the public commits during the rebase, you should not need to force-push::
Your pull request should now contain the new commit too.
Note that the committer is likely to squash the review commit into the previous commit
when committing the code.
Note that the committer is likely to squash the review commit into the previous
commit when committing the code.
Working on a patch
------------------
One of the ways that developers can contribute to Django is by reviewing
patches. Those patches will typically exist as pull requests on GitHub and
can be easily integrated into your local repository::
git checkout -b pull_xxxxx upstream/master
curl https://github.com/django/django/pull/xxxxx.patch | git am
This will create a new branch and then apply the changes from the pull request
to it. At this point you can run the tests or do anything else you need to
do to investigate the quality of the patch.
For more detail on working with pull requests see the
:ref:`guidelines for committers <handling-pull-requests>`.
Summary
-------

View File

@ -117,9 +117,9 @@ these changes.
* The ``mod_python`` request handler will be removed. The ``mod_wsgi``
handler should be used instead.
* The ``template`` attribute on :class:`~django.test.client.Response`
* The ``template`` attribute on :class:`~django.test.Response`
objects returned by the :ref:`test client <test-client>` will be removed.
The :attr:`~django.test.client.Response.templates` attribute should be
The :attr:`~django.test.Response.templates` attribute should be
used instead.
* The ``django.test.simple.DjangoTestRunner`` will be removed.
@ -417,6 +417,8 @@ these changes.
* ``django.utils.importlib`` will be removed.
* ``django.utils.tzinfo`` will be removed.
* ``django.utils.unittest`` will be removed.
* The ``syncdb`` command will be removed.

View File

@ -102,7 +102,7 @@ Installing some prerequisites
The current state of Python packaging is a bit muddled with various tools. For
this tutorial, we're going to use distribute_ to build our package. It's a
community-maintained fork of the older ``setuptools`` project. We'll also be
using `pip`_ to uninstall it after we're finished. You should install these
using `pip`_ to install and uninstall it. You should install these
two packages now. If you need help, you can refer to :ref:`how to install
Django with pip<installing-official-release>`. You can install ``distribute``
the same way.
@ -262,28 +262,18 @@ working. We'll now fix this by installing our new ``django-polls`` package.
tools that run as that user, so ``virtualenv`` is a more robust solution
(see below).
1. Inside ``django-polls/dist``, untar the new package
``django-polls-0.1.tar.gz`` (e.g. ``tar xzvf django-polls-0.1.tar.gz``). If
you're using Windows, you can download the command-line tool bsdtar_ to do
this, or you can use a GUI-based tool such as 7-zip_.
1. To install the package, use pip (you already :ref:`installed it
<installing-reusable-apps-prerequisites>`, right?)::
2. Change into the directory created in step 1 (e.g. ``cd django-polls-0.1``).
pip install --user django-polls/dist/django-polls-0.1.tar.gz
3. If you're using GNU/Linux, Mac OS X or some other flavor of Unix, enter the
command ``python setup.py install --user`` at the shell prompt. If you're
using Windows, start up a command shell and run the command
``setup.py install --user``.
With luck, your Django project should now work correctly again. Run the
2. With luck, your Django project should now work correctly again. Run the
server again to confirm this.
4. To uninstall the package, use pip (you already :ref:`installed it
<installing-reusable-apps-prerequisites>`, right?)::
3. To uninstall the package, use pip::
pip uninstall django-polls
.. _bsdtar: http://gnuwin32.sourceforge.net/packages/bsdtar.htm
.. _7-zip: http://www.7-zip.org/
.. _pip: http://pypi.python.org/pypi/pip
Publishing your app

View File

@ -319,7 +319,7 @@ Before we try to fix anything, let's have a look at the tools at our disposal.
The Django test client
----------------------
Django provides a test :class:`~django.test.client.Client` to simulate a user
Django provides a test :class:`~django.test.Client` to simulate a user
interacting with the code at the view level. We can use it in ``tests.py``
or even in the shell.
@ -341,7 +341,7 @@ Next we need to import the test client class (later in ``tests.py`` we will use
the :class:`django.test.TestCase` class, which comes with its own client, so
this won't be required)::
>>> from django.test.client import Client
>>> from django.test import Client
>>> # create an instance of the client for our use
>>> client = Client()
@ -494,7 +494,7 @@ class::
"""
The questions index page may display multiple questions.
"""
create_question(question_text="Past quesiton 1.", days=-30)
create_question(question_text="Past question 1.", days=-30)
create_question(question_text="Past question 2.", days=-5)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(

View File

@ -142,7 +142,7 @@ Methods
.. versionchanged:: 1.6
In Django 1.4 and 1.5, a blank string was unintentionally stored
as an unsable password.
as an unusable password.
.. method:: check_password(raw_password)

View File

@ -56,7 +56,7 @@ needs to be customized so that SQLite knows to build the R*Tree module::
__ http://www.sqlite.org/rtree.html
__ http://www.sqlite.org/download.html
.. _spatialitebuild :
.. _spatialitebuild:
SpatiaLite library (``libspatialite``) and tools (``spatialite``)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -74,15 +74,17 @@ customization of the ``configure`` command is necessary. If not, then run the
``configure`` script, make, and install for the SpatiaLite library::
$ cd libspatialite-amalgamation-2.3.1
$ ./configure # May need to modified, see notes below.
$ ./configure # May need to be modified, see notes below.
$ make
$ sudo make install
$ cd .... _spatialite
$ cd ..
.. _spatialite_tools:
Finally, do the same for the SpatiaLite tools::
$ cd spatialite-tools-2.3.1
$ ./configure # May need to modified, see notes below.
$ ./configure # May need to be modified, see notes below.
$ make
$ sudo make install
$ cd ..

View File

@ -1638,6 +1638,15 @@ Examples::
management.call_command('flush', verbosity=0, interactive=False)
management.call_command('loaddata', 'test_data', verbosity=0)
Note that command options that take no arguments are passed as keywords
with ``True`` or ``False``::
management.call_command('dumpdata', use_natural_keys=True)
Command options which take multiple options are passed a list::
management.call_command('dumpdata', exclude=['contenttypes', 'auth'])
Output redirection
==================

View File

@ -79,6 +79,20 @@ GZip middleware
.. class:: GZipMiddleware
.. warning::
Security researchers recently revealed that when compression techniques
(including ``GZipMiddleware``) are used on a website, the site becomes
exposed to a number of possible attacks. These approaches can be used to
compromise, amongst other things, Django's CSRF protection. Before using
``GZipMiddleware`` on your site, you should consider very carefully whether
you are subject to these attacks. If you're in *any* doubt about whether
you're affected, you should avoid using ``GZipMiddleware``. For more
details, see the `the BREACH paper (PDF)`_ and `breachattack.com`_.
.. _the BREACH paper (PDF): http://breachattack.com/resources/BREACH%20-%20SSL,%20gone%20in%2030%20seconds.pdf
.. _breachattack.com: http://breachattack.com
Compresses content for browsers that understand GZip compression (all modern
browsers).

View File

@ -221,6 +221,12 @@ Django quotes column and table names behind the scenes.
ordering = ['-pub_date', 'author']
.. warning::
Ordering is not a free operation. Each field you add to the ordering
incurs a cost to your database. Each foreign key you add will
implicitly include all of its default orderings as well.
``permissions``
---------------

View File

@ -334,6 +334,12 @@ You can tell if a query is ordered or not by checking the
:attr:`.QuerySet.ordered` attribute, which will be ``True`` if the
``QuerySet`` has been ordered in any way.
.. warning::
Ordering is not a free operation. Each field you add to the ordering
incurs a cost to your database. Each foreign key you add will
implicitly include all of its default orderings as well.
reverse
~~~~~~~
@ -2268,7 +2274,8 @@ SQL equivalent::
(The exact SQL syntax varies for each database engine.)
When :setting:`USE_TZ` is ``True``, datetime fields are converted to the
current time zone before filtering.
current time zone before filtering. This requires :ref:`time zone definitions
in the database <database-time-zone-definitions>`.
.. fieldlookup:: day
@ -2291,7 +2298,8 @@ Note this will match any record with a pub_date on the third day of the month,
such as January 3, July 3, etc.
When :setting:`USE_TZ` is ``True``, datetime fields are converted to the
current time zone before filtering.
current time zone before filtering. This requires :ref:`time zone definitions
in the database <database-time-zone-definitions>`.
.. fieldlookup:: week_day
@ -2315,7 +2323,8 @@ Note this will match any record with a ``pub_date`` that falls on a Monday (day
are indexed with day 1 being Sunday and day 7 being Saturday.
When :setting:`USE_TZ` is ``True``, datetime fields are converted to the
current time zone before filtering.
current time zone before filtering. This requires :ref:`time zone definitions
in the database <database-time-zone-definitions>`.
.. fieldlookup:: hour

View File

@ -565,7 +565,7 @@ setting_changed
This signal is sent when the value of a setting is changed through the
``django.test.TestCase.settings()`` context manager or the
:func:`django.test.utils.override_settings` decorator/context manager.
:func:`django.test.override_settings` decorator/context manager.
It's actually sent twice: when the new value is applied ("setup") and when the
original value is restored ("teardown"). Use the ``enter`` argument to

View File

@ -2226,7 +2226,7 @@ If ``value`` is ``"http://www.example.org/"``, the output will be
urlize
^^^^^^
Converts URLs in text into clickable links.
Converts URLs and email addresses in text into clickable links.
This template tag works on links prefixed with ``http://``, ``https://``, or
``www.``. For example, ``http://goo.gl/aia1t`` will get converted but
@ -2250,6 +2250,11 @@ If ``value`` is ``"Check out www.djangoproject.com"``, the output will be
``"Check out <a href="http://www.djangoproject.com"
rel="nofollow">www.djangoproject.com</a>"``.
In addition to web links, ``urlize`` also converts email addresses into
``mailto:`` links. If ``value`` is
``"Send questions to foo@example.com"``, the output will be
``"Send questions to <a href="mailto:foo@example.com">foo@example</a>"``.
The ``urlize`` filter also takes an optional parameter ``autoescape``. If
``autoescape`` is ``True``, the link text and URLs will be escaped using
Django's built-in :tfilter:`escape` filter. The default value for
@ -2265,7 +2270,7 @@ Django's built-in :tfilter:`escape` filter. The default value for
urlizetrunc
^^^^^^^^^^^
Converts URLs into clickable links just like urlize_, but truncates URLs
Converts URLs and email addresses into clickable links just like urlize_, but truncates URLs
longer than the given character limit.
**Argument:** Number of characters that link text should be truncated to,

View File

@ -927,6 +927,17 @@ For a complete discussion on the usage of the following see the
:class:`~datetime.tzinfo` instance that represents UTC.
.. function:: get_fixed_timezone(offset)
.. versionadded:: 1.7
Returns a :class:`~datetime.tzinfo` instance that represents a time zone
with a fixed offset from UTC.
``offset`` is a :class:`datetime.timedelta` or an integer number of
minutes. Use positive values for time zones east of UTC and negative
values for west of UTC.
.. function:: get_default_timezone()
Returns a :class:`~datetime.tzinfo` instance that represents the
@ -1021,6 +1032,9 @@ For a complete discussion on the usage of the following see the
``django.utils.tzinfo``
=======================
.. deprecated:: 1.7
Use :mod:`~django.utils.timezone` instead.
.. module:: django.utils.tzinfo
:synopsis: Implementation of ``tzinfo`` classes for use with ``datetime.datetime``.
@ -1028,6 +1042,12 @@ For a complete discussion on the usage of the following see the
Fixed offset in minutes east from UTC.
.. deprecated:: 1.7
Use :func:`~django.utils.timezone.get_fixed_timezone` instead.
.. class:: LocalTimezone
Proxy timezone information from time module.
.. deprecated:: 1.7
Use :func:`~django.utils.timezone.get_default_timezone` instead.

View File

@ -643,7 +643,7 @@ The generic relation classes -- ``GenericForeignKey`` and ``GenericRelation``
Testing
-------
:meth:`django.test.client.Client.login` has changed
:meth:`django.test.Client.login` has changed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Old (0.96)::

View File

@ -99,7 +99,7 @@ one fell swoop.
Testing improvements
--------------------
.. currentmodule:: django.test.client
.. currentmodule:: django.test
A couple of small but very useful improvements have been made to the
:doc:`testing framework </topics/testing/index>`:

View File

@ -285,8 +285,6 @@ full description, and some important notes on database support.
Test client improvements
------------------------
.. currentmodule:: django.test.client
A couple of small -- but highly useful -- improvements have been made to the
test client:

View File

@ -23,7 +23,7 @@ case of Django 1.2.2, we have made an exception to this rule.
In order to test a bug fix that forms part of the 1.2.2 release, it
was necessary to add a feature -- the ``enforce_csrf_checks`` flag --
to the :mod:`test client <django.test.client>`. This flag forces
to the :ref:`test client <test-client>`. This flag forces
the test client to perform full CSRF checks on forms. The default
behavior of the test client hasn't changed, but if you want to do
CSRF checks with the test client, it is now possible to do so.

View File

@ -150,7 +150,7 @@ requests. These include:
* Improved tools for accessing and manipulating the current Site via
``django.contrib.sites.models.get_current_site()``.
* A :class:`~django.test.client.RequestFactory` for mocking
* A :class:`~django.test.RequestFactory` for mocking
requests in tests.
* A new test assertion --
@ -318,7 +318,7 @@ Test client response ``template`` attribute
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django's :ref:`test client <test-client>` returns
:class:`~django.test.client.Response` objects annotated with extra testing
:class:`~django.test.Response` objects annotated with extra testing
information. In Django versions prior to 1.3, this included a ``template``
attribute containing information about templates rendered in generating the
response: either None, a single :class:`~django.template.Template` object, or a
@ -327,7 +327,7 @@ return values (sometimes a list, sometimes not) made the attribute difficult
to work with.
In Django 1.3 the ``template`` attribute is deprecated in favor of a new
:attr:`~django.test.client.Response.templates` attribute, which is always a
:attr:`~django.test.Response.templates` attribute, which is always a
list, even if it has only a single element or no elements.
``DjangoTestRunner``

View File

@ -295,7 +295,7 @@ requests. These include:
:class:`~django.contrib.sites.models.Site` object in
:doc:`the sites framework </ref/contrib/sites>`.
* A :class:`~django.test.client.RequestFactory` for mocking requests
* A :class:`~django.test.RequestFactory` for mocking requests
in tests.
* A new test assertion --
@ -715,7 +715,7 @@ Test client response ``template`` attribute
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django's :ref:`test client <test-client>` returns
:class:`~django.test.client.Response` objects annotated with extra testing
:class:`~django.test.Response` objects annotated with extra testing
information. In Django versions prior to 1.3, this included a ``template``
attribute containing information about templates rendered in generating the
response: either None, a single :class:`~django.template.Template` object, or a
@ -724,7 +724,7 @@ return values (sometimes a list, sometimes not) made the attribute difficult
to work with.
In Django 1.3 the ``template`` attribute is deprecated in favor of a new
:attr:`~django.test.client.Response.templates` attribute, which is always a
:attr:`~django.test.Response.templates` attribute, which is always a
list, even if it has only a single element or no elements.
``DjangoTestRunner``

View File

@ -26,6 +26,6 @@ header and browsers seem to ignore JavaScript there.
Bugfixes
========
* Fixed an obscure bug with the :func:`~django.test.utils.override_settings`
* Fixed an obscure bug with the :func:`~django.test.override_settings`
decorator. If you hit an ``AttributeError: 'Settings' object has no attribute
'_original_allowed_hosts'`` exception, it's probably fixed (#20636).

25
docs/releases/1.4.7.txt Normal file
View File

@ -0,0 +1,25 @@
==========================
Django 1.4.7 release notes
==========================
*September 10, 2013*
Django 1.4.7 fixes one security issue present in previous Django releases in
the 1.4 series.
Directory traversal vulnerability in :ttag:`ssi` template tag
-------------------------------------------------------------
In previous versions of Django it was possible to bypass the
:setting:`ALLOWED_INCLUDE_ROOTS` setting used for security with the :ttag:`ssi`
template tag by specifying a relative path that starts with one of the allowed
roots. For example, if ``ALLOWED_INCLUDE_ROOTS = ("/var/www",)`` the following
would be possible:
.. code-block:: html+django
{% ssi "/var/www/../../etc/passwd" %}
In practice this is not a very common problem, as it would require the template
author to put the :ttag:`ssi` file in a user-controlled variable, but it's
possible in principle.

View File

@ -57,6 +57,6 @@ Bugfixes
* Ensured that the WSGI request's path is correctly based on the
``SCRIPT_NAME`` environment variable or the :setting:`FORCE_SCRIPT_NAME`
setting, regardless of whether or not either has a trailing slash (#20169).
* Fixed an obscure bug with the :func:`~django.test.utils.override_settings`
* Fixed an obscure bug with the :func:`~django.test.override_settings`
decorator. If you hit an ``AttributeError: 'Settings' object has no attribute
'_original_allowed_hosts'`` exception, it's probably fixed (#20636).

50
docs/releases/1.5.3.txt Normal file
View File

@ -0,0 +1,50 @@
==========================
Django 1.5.3 release notes
==========================
*September 10, 2013*
This is Django 1.5.3, the third release in the Django 1.5 series. It addresses
one security issue and also contains an opt-in feature to enhance the security
of :mod:`django.contrib.sessions`.
Directory traversal vulnerability in :ttag:`ssi` template tag
-------------------------------------------------------------
In previous versions of Django it was possible to bypass the
:setting:`ALLOWED_INCLUDE_ROOTS` setting used for security with the :ttag:`ssi`
template tag by specifying a relative path that starts with one of the allowed
roots. For example, if ``ALLOWED_INCLUDE_ROOTS = ("/var/www",)`` the following
would be possible:
.. code-block:: html+django
{% ssi "/var/www/../../etc/passwd" %}
In practice this is not a very common problem, as it would require the template
author to put the :ttag:`ssi` file in a user-controlled variable, but it's
possible in principle.
Mitigating a remote-code execution vulnerability in :mod:`django.contrib.sessions`
----------------------------------------------------------------------------------
:mod:`django.contrib.sessions` currently uses :mod:`pickle` to serialize
session data before storing it in the backend. If you're using the :ref:`signed
cookie session backend<cookie-session-backend>` and :setting:`SECRET_KEY` is
known by an attacker (there isn't an inherent vulnerability in Django that
would cause it to leak), the attacker could insert a string into his session
which, when unpickled, executes arbitrary code on the server. The technique for
doing so is simple and easily available on the internet. Although the cookie
session storage signs the cookie-stored data to prevent tampering, a
:setting:`SECRET_KEY` leak immediately escalates to a remote code execution
vulnerability.
This attack can be mitigated by serializing session data using JSON rather
than :mod:`pickle`. To facilitate this, Django 1.5.3 introduces a new setting,
:setting:`SESSION_SERIALIZER`, to customize the session serialization format.
For backwards compatibility, this setting defaults to using :mod:`pickle`.
While JSON serialization does not support all Python objects like :mod:`pickle`
does, we highly recommend switching to JSON-serialized values. Also,
as JSON requires string keys, you will likely run into problems if you are
using non-string keys in ``request.session``. See the
:ref:`session_serialization` documentation for more details.

View File

@ -435,6 +435,21 @@ but will not be removed from Django until version 1.8.
.. _recommendations in the Python documentation: http://docs.python.org/2/library/doctest.html#unittest-api
Time zone-aware ``day``, ``month``, and ``week_day`` lookups
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django 1.6 introduces time zone support for :lookup:`day`, :lookup:`month`,
and :lookup:`week_day` lookups when :setting:`USE_TZ` is ``True``. These
lookups were previously performed in UTC regardless of the current time zone.
This requires :ref:`time zone definitions in the database
<database-time-zone-definitions>`. If you're using SQLite, you must install
pytz_. If you're using MySQL, you must install pytz_ and load the time zone
tables with `mysql_tzinfo_to_sql`_.
.. _pytz: http://pytz.sourceforge.net/
.. _mysql_tzinfo_to_sql: http://dev.mysql.com/doc/refman/5.5/en/mysql-tzinfo-to-sql.html
Addition of ``QuerySet.datetimes()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -814,7 +829,7 @@ Miscellaneous
``{% url %}`` tag, it causes template rendering to fail like always when
``NoReverseMatch`` is raised.
* :meth:`django.test.client.Client.logout` now calls
* :meth:`django.test.Client.logout` now calls
:meth:`django.contrib.auth.logout` which will send the
:func:`~django.contrib.auth.signals.user_logged_out` signal.
@ -1046,12 +1061,12 @@ security problem described in the section above, because they can automatically
create a ``ModelForm`` that uses all fields for a model.
For this reason, if you use these views for editing models, you must also supply
the ``fields`` attribute, which is a list of model fields and works in the same
way as the :class:`~django.forms.ModelForm` ``Meta.fields`` attribute. Alternatively,
you can set set the ``form_class`` attribute to a ``ModelForm`` that explicitly
defines the fields to be used. Defining an ``UpdateView`` or ``CreateView``
subclass to be used with a model but without an explicit list of fields is
deprecated.
the ``fields`` attribute (new in Django 1.6), which is a list of model fields
and works in the same way as the :class:`~django.forms.ModelForm`
``Meta.fields`` attribute. Alternatively, you can set set the ``form_class``
attribute to a ``ModelForm`` that explicitly defines the fields to be used.
Defining an ``UpdateView`` or ``CreateView`` subclass to be used with a model
but without an explicit list of fields is deprecated.
.. _m2m-help_text-deprecation:

View File

@ -47,7 +47,7 @@ but a few of the key features are:
* A new ``makemigrations`` command provides an easy way to autodetect changes
to your models and make migrations for them.
* :data:`~django.db.models.signals.post_syncdb` and
* :data:`~django.db.models.signals.pre_syncdb` and
:data:`~django.db.models.signals.post_syncdb` have been renamed to
:data:`~django.db.models.signals.pre_migrate` and
:data:`~django.db.models.signals.post_migrate` respectively. The
@ -285,6 +285,19 @@ Templates
* ``TypeError`` exceptions are not longer silenced when raised during the
rendering of a template.
Tests
^^^^^
* :class:`~django.test.runner.DiscoverRunner` has two new attributes,
:attr:`~django.test.runner.DiscoverRunner.test_suite` and
:attr:`~django.test.runner.DiscoverRunner.test_runner`, which facilitate
overriding the way tests are collected and run.
* The ``fetch_redirect_response`` argument was added to
:meth:`~django.test.SimpleTestCase.assertRedirects`. Since the test
client can't fetch externals URLs, this allows you to use ``assertRedirects``
with redirects that aren't part of your Django app.
Backwards incompatible changes in 1.7
=====================================
@ -308,6 +321,16 @@ For apps with migrations, ``allow_migrate`` will now get passed
without custom attributes, methods or managers. Make sure your ``allow_migrate``
methods are only referring to fields or other items in ``model._meta``.
pytz may be required
~~~~~~~~~~~~~~~~~~~~
If your project handles datetimes before 1970 or after 2037 and Django raises
a :exc:`~exceptions.ValueError` when encountering them, you will have to
install pytz_. You may be affected by this problem if you use Django's time
zone-related date formats or :mod:`django.contrib.syndication`.
.. _pytz: https://pypi.python.org/pypi/pytz/
Miscellaneous
~~~~~~~~~~~~~
@ -345,13 +368,18 @@ Miscellaneous
when called on an instance without a primary key value. This is done to
avoid mutable ``__hash__`` values in containers.
* The :meth:`django.db.backends.sqlite3.DatabaseCreation.sql_create_model`
will now create :class:`~django.db.models.AutoField` columns in SQLite
databases using the ``AUTOINCREMENT`` option, which guarantees monotonic
* :class:`~django.db.models.AutoField` columns in SQLite databases will now be
created using the ``AUTOINCREMENT`` option, which guarantees monotonic
increments. This will cause primary key numbering behavior to change on
SQLite, becoming consistent with most other SQL databases. If you have a
database created with an older version of Django, you will need to migrate
it to take advantage of this feature. See ticket #10164 for details.
SQLite, becoming consistent with most other SQL databases. This will only
apply to newly created tables. If you have a database created with an older
version of Django, you will need to migrate it to take advantage of this
feature. For example, you could do the following:
#) Use :djadmin:`dumpdata` to save your data.
#) Rename the existing database file (keep it as a backup).
#) Run :djadmin:`migrate` to create the updated schema.
#) Use :djadmin:`loaddata` to import the fixtures you exported in (1).
* ``django.contrib.auth.models.AbstractUser`` no longer defines a
:meth:`~django.db.models.Model.get_absolute_url()` method. The old definition
@ -384,6 +412,15 @@ Features deprecated in 1.7
respectively :mod:`logging.config` and :mod:`importlib` provided for Python
versions prior to 2.7. They have been deprecated.
``django.utils.tzinfo``
~~~~~~~~~~~~~~~~~~~~~~~
``django.utils.tzinfo`` provided two :class:`~datetime.tzinfo` subclasses,
``LocalTimezone`` and ``FixedOffset``. They've been deprecated in favor of
more correct alternatives provided by :mod:`django.utils.timezone`,
:func:`django.utils.timezone.get_default_timezone` and
:func:`django.utils.timezone.get_fixed_timezone`.
``django.utils.unittest``
~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -36,6 +36,7 @@ Final releases
.. toctree::
:maxdepth: 1
1.5.3
1.5.2
1.5.1
1.5
@ -45,6 +46,7 @@ Final releases
.. toctree::
:maxdepth: 1
1.4.7
1.4.6
1.4.5
1.4.4

View File

@ -869,8 +869,7 @@ would test three possible User models -- the default, plus the two User
models provided by ``auth`` app::
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TestCase
from django.test.utils import override_settings
from django.test import TestCase, override_settings
class ApplicationTestCase(TestCase):

View File

@ -341,40 +341,37 @@ been well-tested and are easy to use.
Cache arguments
---------------
In addition to the defining the engine and name of the each cache
backend, each cache backend can be given additional arguments to
control caching behavior. These arguments are provided as additional
keys in the :setting:`CACHES` setting. Valid arguments are as follows:
Each cache backend can be given additional arguments to control caching
behavior. These arguments are provided as additional keys in the
:setting:`CACHES` setting. Valid arguments are as follows:
* :setting:`TIMEOUT <CACHES-TIMEOUT>`: The default timeout, in
seconds, to use for the cache. This argument defaults to 300
seconds, to use for the cache. This argument defaults to ``300``
seconds (5 minutes).
* :setting:`OPTIONS <CACHES-OPTIONS>`: Any options that should be
passed to cache backend. The list options understood by each
backend vary with each backend.
passed to the cache backend. The list of valid options will vary
with each backend, and cache backends backed by a third-party library
will pass their options directly to the underlying cache library.
Cache backends that implement their own culling strategy (i.e.,
the ``locmem``, ``filesystem`` and ``database`` backends) will
honor the following options:
* ``MAX_ENTRIES``: the maximum number of entries allowed in
* ``MAX_ENTRIES``: The maximum number of entries allowed in
the cache before old values are deleted. This argument
defaults to ``300``.
* ``CULL_FREQUENCY``: The fraction of entries that are culled
when ``MAX_ENTRIES`` is reached. The actual ratio is
``1/CULL_FREQUENCY``, so set ``CULL_FREQUENCY``: to ``2`` to
cull half of the entries when ``MAX_ENTRIES`` is reached.
``1 / CULL_FREQUENCY``, so set ``CULL_FREQUENCY`` to ``2`` to
cull half the entries when ``MAX_ENTRIES`` is reached. This argument
should be an integer and defaults to ``3``.
A value of ``0`` for ``CULL_FREQUENCY`` means that the
entire cache will be dumped when ``MAX_ENTRIES`` is reached.
This makes culling *much* faster at the expense of more
cache misses.
Cache backends backed by a third-party library will pass their
options directly to the underlying cache library. As a result,
the list of valid options depends on the library in use.
On some backends (``database`` in particular) this makes culling *much*
faster at the expense of more cache misses.
* :setting:`KEY_PREFIX <CACHES-KEY_PREFIX>`: A string that will be
automatically included (prepended by default) to all cache keys
@ -1176,7 +1173,10 @@ site's performance:
and ``Last-Modified`` headers.
* :class:`django.middleware.gzip.GZipMiddleware` compresses responses for all
modern browsers, saving bandwidth and transfer time.
modern browsers, saving bandwidth and transfer time. Be warned, however,
that compression techniques like ``GZipMiddleware`` are subject to attacks.
See the warning in :class:`~django.middleware.gzip.GZipMiddleware` for
details.
Order of MIDDLEWARE_CLASSES
===========================

View File

@ -306,6 +306,17 @@ instead of::
entry.blog.id
Don't order results if you don't care
-------------------------------------
Ordering is not free; each field to order by is an operation the database must
perform. If a model has a default ordering (:attr:`Meta.ordering
<django.db.models.Options.ordering>`) and you don't need it, remove
it on a ``QuerySet`` by calling
:meth:`~django.db.models.query.QuerySet.order_by()` with no parameters.
Adding an index to your database may help to improve ordering performance.
Insert in bulk
==============

View File

@ -5,18 +5,18 @@ Advanced testing topics
The request factory
===================
.. module:: django.test.client
.. currentmodule:: django.test
.. class:: RequestFactory
The :class:`~django.test.client.RequestFactory` shares the same API as
The :class:`~django.test.RequestFactory` shares the same API as
the test client. However, instead of behaving like a browser, the
RequestFactory provides a way to generate a request instance that can
be used as the first argument to any view. This means you can test a
view function the same way as you would test any other function -- as
a black box, with exactly known inputs, testing for specific outputs.
The API for the :class:`~django.test.client.RequestFactory` is a slightly
The API for the :class:`~django.test.RequestFactory` is a slightly
restricted subset of the test client API:
* It only has access to the HTTP methods :meth:`~Client.get()`,
@ -38,8 +38,7 @@ Example
The following is a simple unit test using the request factory::
from django.contrib.auth.models import User
from django.test import TestCase
from django.test.client import RequestFactory
from django.test import TestCase, RequestFactory
class SimpleTest(TestCase):
def setUp(self):
@ -165,8 +164,6 @@ exception will be raised.
Advanced features of ``TransactionTestCase``
============================================
.. currentmodule:: django.test
.. attribute:: TransactionTestCase.available_apps
.. versionadded:: 1.6
@ -341,6 +338,25 @@ execute and tear down the test suite.
Attributes
~~~~~~~~~~
.. attribute:: DiscoverRunner.test_suite
.. versionadded:: 1.7
The class used to build the test suite. By default it is set to
``unittest.TestSuite``. This can be overridden if you wish to implement
different logic for collecting tests.
.. attribute:: DiscoverRunner.test_runner
.. versionadded:: 1.7
This is the class of the low-level test runner which is used to execute
the individual tests and format the results. By default it is set to
``unittest.TextTestRunner``. Despite the unfortunate similarity in
naming conventions, this is not the same type of class as
``DiscoverRunner``, which covers a broader set of responsibilites. You
can override this attribute to modify the way tests are run and reported.
.. attribute:: DiscoverRunner.test_loader
This is the class that loads tests, whether from TestCases or modules or

View File

@ -313,9 +313,6 @@ Django provides a small set of tools that come in handy when writing tests.
The test client
---------------
.. module:: django.test.client
:synopsis: Django's test client.
The test client is a Python class that acts as a dummy Web browser, allowing
you to test your views and interact with your Django-powered application
programmatically.
@ -349,10 +346,10 @@ A comprehensive test suite should use a combination of both test types.
Overview and a quick example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To use the test client, instantiate ``django.test.client.Client`` and retrieve
To use the test client, instantiate ``django.test.Client`` and retrieve
Web pages::
>>> from django.test.client import Client
>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
@ -413,7 +410,7 @@ Note a few important things about how the test client works:
Making requests
~~~~~~~~~~~~~~~
Use the ``django.test.client.Client`` class to make requests.
Use the ``django.test.Client`` class to make requests.
.. class:: Client(enforce_csrf_checks=False, **defaults)
@ -424,8 +421,8 @@ Use the ``django.test.client.Client`` class to make requests.
>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
The values from the ``extra`` keywords arguments passed to
:meth:`~django.test.client.Client.get()`,
:meth:`~django.test.client.Client.post()`, etc. have precedence over
:meth:`~django.test.Client.get()`,
:meth:`~django.test.Client.post()`, etc. have precedence over
the defaults passed to the class constructor.
The ``enforce_csrf_checks`` argument can be used to test CSRF
@ -778,7 +775,7 @@ Example
The following is a simple unit test using the test client::
import unittest
from django.test.client import Client
from django.test import Client
class SimpleTest(unittest.TestCase):
def setUp(self):
@ -797,15 +794,13 @@ The following is a simple unit test using the test client::
.. seealso::
:class:`django.test.client.RequestFactory`
:class:`django.test.RequestFactory`
.. _django-testcase-subclasses:
Provided test case classes
--------------------------
.. currentmodule:: django.test
Normal Python unit test classes extend a base class of
:class:`unittest.TestCase`. Django provides a few extensions of this base class:
@ -847,7 +842,7 @@ functionality like:
for equality.
* The ability to run tests with :ref:`modified settings <overriding-settings>`.
* Using the :attr:`~SimpleTestCase.client` :class:`~django.test.client.Client`.
* Using the :attr:`~SimpleTestCase.client` :class:`~django.test.Client`.
* Custom test-time :attr:`URL maps <SimpleTestCase.urls>`.
.. versionchanged:: 1.6
@ -1111,7 +1106,7 @@ worry about state (such as cookies) carrying over from one test to another.
This means, instead of instantiating a ``Client`` in each test::
import unittest
from django.test.client import Client
from django.test import Client
class SimpleTest(unittest.TestCase):
def test_details(self):
@ -1146,8 +1141,7 @@ If you want to use a different ``Client`` class (for example, a subclass
with customized behavior), use the :attr:`~SimpleTestCase.client_class` class
attribute::
from django.test import TestCase
from django.test.client import Client
from django.test import TestCase, Client
class MyTestClient(Client):
# Specialized methods for your environment...
@ -1330,17 +1324,14 @@ Django provides a standard Python context manager (see :pep:`343`)
This example will override the :setting:`LOGIN_URL` setting for the code
in the ``with`` block and reset its value to the previous state afterwards.
.. currentmodule:: django.test.utils
.. function:: override_settings
In case you want to override a setting for just one test method or even the
whole :class:`~django.test.TestCase` class, Django provides the
:func:`~django.test.utils.override_settings` decorator (see :pep:`318`). It's
:func:`~django.test.override_settings` decorator (see :pep:`318`). It's
used like this::
from django.test import TestCase
from django.test.utils import override_settings
from django.test import TestCase, override_settings
class LoginTestCase(TestCase):
@ -1351,8 +1342,7 @@ used like this::
The decorator can also be applied to test case classes::
from django.test import TestCase
from django.test.utils import override_settings
from django.test import TestCase, override_settings
@override_settings(LOGIN_URL='/other/login/')
class LoginTestCase(TestCase):
@ -1361,6 +1351,11 @@ The decorator can also be applied to test case classes::
response = self.client.get('/sekrit/')
self.assertRedirects(response, '/other/login/?next=/sekrit/')
.. versionchanged:: 1.7
Previously, ``override_settings`` was imported from
``django.test.utils``.
.. note::
When given a class, the decorator modifies the class directly and
@ -1427,8 +1422,6 @@ For more detail on email services during tests, see `Email services`_ below.
Assertions
~~~~~~~~~~
.. currentmodule:: django.test
As Python's normal :class:`unittest.TestCase` class implements assertion methods
such as :meth:`~unittest.TestCase.assertTrue` and
:meth:`~unittest.TestCase.assertEqual`, Django's custom :class:`TestCase` class
@ -1549,7 +1542,7 @@ your test suite.
You can use this as a context manager in the same way as
:meth:`~SimpleTestCase.assertTemplateUsed`.
.. method:: SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='')
.. method:: SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True)
Asserts that the response return a ``status_code`` redirect status, it
redirected to ``expected_url`` (including any GET data), and the final
@ -1559,6 +1552,12 @@ your test suite.
``target_status_code`` will be the url and status code for the final
point of the redirect chain.
.. versionadded:: 1.7
If ``fetch_redirect_response`` is ``False``, the final page won't be
loaded. Since the test client can't fetch externals URLs, this is
particularly useful if ``expected_url`` isn't part of your Django app.
.. method:: SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)
Asserts that the strings ``html1`` and ``html2`` are equal. The comparison

View File

@ -1 +0,0 @@
#

View File

@ -9,7 +9,7 @@ import unittest
from django.conf import settings, global_settings
from django.core import mail
from django.core.files import temp as tempfile
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse, NoReverseMatch
# Register auth models with the admin.
from django.contrib.auth import get_permission_codename
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
@ -640,6 +640,20 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
# Check the format of the shown object -- shouldn't contain a change link
self.assertContains(response, '<th class="field-__str__">UnchangeableObject object</th>', html=True)
def test_invalid_appindex_url(self):
"""
#21056 -- URL reversing shouldn't work for nonexistent apps.
"""
good_url = '/test_admin/admin/admin_views/'
confirm_good_url = reverse('admin:app_list',
kwargs={'app_label': 'admin_views'})
self.assertEqual(good_url, confirm_good_url)
with self.assertRaises(NoReverseMatch):
reverse('admin:app_list', kwargs={'app_label': 'this_should_fail'})
with self.assertRaises(NoReverseMatch):
reverse('admin:app_list', args=('admin_views2',))
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class AdminViewFormUrlTest(TestCase):
@ -1296,6 +1310,40 @@ class AdminViewPermissionsTest(TestCase):
response = self.client.get('/test_admin/admin/secure-view/')
self.assertContains(response, 'id="login-form"')
def testDisabledStaffPermissionsWhenLoggedIn(self):
self.client.login(username='super', password='secret')
superuser = User.objects.get(username='super')
superuser.is_staff = False
superuser.save()
response = self.client.get('/test_admin/admin/')
self.assertContains(response, 'id="login-form"')
self.assertNotContains(response, 'Log out')
response = self.client.get('/test_admin/admin/secure-view/')
self.assertContains(response, 'id="login-form"')
def testAppIndexFailEarly(self):
"""
If a user has no module perms, avoid iterating over all the modeladmins
in the registry.
"""
opts = Article._meta
change_user = User.objects.get(username='changeuser')
permission = get_perm(Article, get_permission_codename('change', opts))
self.client.post('/test_admin/admin/', self.changeuser_login)
# the user has no module permissions, because this module doesn't exist
change_user.user_permissions.remove(permission)
response = self.client.get('/test_admin/admin/admin_views/')
self.assertEqual(response.status_code, 403)
# the user now has module permissions
change_user.user_permissions.add(permission)
response = self.client.get('/test_admin/admin/admin_views/')
self.assertEqual(response.status_code, 200)
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class AdminViewsNoUrlTest(TestCase):

View File

@ -549,6 +549,79 @@ class DateTimePickerSeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
self.assertEqual(
self.get_css_value('#clockbox0', 'display'), 'none')
def test_calendar_nonday_class(self):
"""
Ensure cells that are not days of the month have the `nonday` CSS class.
Refs #4574.
"""
self.admin_login(username='super', password='secret', login_url='/')
# Open a page that has a date and time picker widgets
self.selenium.get('%s%s' % (self.live_server_url,
'/admin_widgets/member/add/'))
# fill in the birth date.
self.selenium.find_element_by_id('id_birthdate_0').send_keys('2013-06-01')
# Click the calendar icon
self.selenium.find_element_by_id('calendarlink0').click()
# get all the tds within the calendar
calendar0 = self.selenium.find_element_by_id('calendarin0')
tds = calendar0.find_elements_by_tag_name('td')
# make sure the first and last 6 cells have class nonday
for td in tds[:6] + tds[-6:]:
self.assertEqual(td.get_attribute('class'), 'nonday')
def test_calendar_selected_class(self):
"""
Ensure cell for the day in the input has the `selected` CSS class.
Refs #4574.
"""
self.admin_login(username='super', password='secret', login_url='/')
# Open a page that has a date and time picker widgets
self.selenium.get('%s%s' % (self.live_server_url,
'/admin_widgets/member/add/'))
# fill in the birth date.
self.selenium.find_element_by_id('id_birthdate_0').send_keys('2013-06-01')
# Click the calendar icon
self.selenium.find_element_by_id('calendarlink0').click()
# get all the tds within the calendar
calendar0 = self.selenium.find_element_by_id('calendarin0')
tds = calendar0.find_elements_by_tag_name('td')
# verify the selected cell
selected = tds[6]
self.assertEqual(selected.get_attribute('class'), 'selected')
self.assertEqual(selected.text, '1')
def test_calendar_no_selected_class(self):
"""
Ensure no cells are given the selected class when the field is empty.
Refs #4574.
"""
self.admin_login(username='super', password='secret', login_url='/')
# Open a page that has a date and time picker widgets
self.selenium.get('%s%s' % (self.live_server_url,
'/admin_widgets/member/add/'))
# Click the calendar icon
self.selenium.find_element_by_id('calendarlink0').click()
# get all the tds within the calendar
calendar0 = self.selenium.find_element_by_id('calendarin0')
tds = calendar0.find_elements_by_tag_name('td')
# verify there are no cells with the selected class
selected = [td for td in tds if td.get_attribute('class') == 'selected']
self.assertEqual(len(selected), 0)
class DateTimePickerSeleniumChromeTests(DateTimePickerSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'

View File

@ -6,7 +6,8 @@ import re
from django.db import connection
from django.db.models import Avg, Sum, Count, Max, Min
from django.test import TestCase, Approximate
from django.test import TestCase
from django.test.utils import Approximate
from django.test.utils import CaptureQueriesContext
from .models import Author, Publisher, Book, Store

View File

@ -8,7 +8,8 @@ from operator import attrgetter
from django.core.exceptions import FieldError
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count, Max, Avg, Sum, StdDev, Variance, F, Q
from django.test import TestCase, Approximate, skipUnlessDBFeature
from django.test import TestCase, skipUnlessDBFeature
from django.test.utils import Approximate
from django.utils import six
from .models import (Author, Book, Publisher, Clues, Entries, HardbackBook,

View File

@ -1 +0,0 @@

View File

@ -83,11 +83,3 @@ class CommentTestCase(TestCase):
d.update(f.initial)
return d
from comment_tests.tests.test_app_api import *
from comment_tests.tests.test_feeds import *
from comment_tests.tests.test_models import *
from comment_tests.tests.test_comment_form import *
from comment_tests.tests.test_templatetags import *
from comment_tests.tests.test_comment_view import *
from comment_tests.tests.test_comment_utils_moderators import *
from comment_tests.tests.test_moderation_views import *

Some files were not shown because too many files have changed in this diff Show More