Merge remote-tracking branch 'core/master' into schema-alteration

Conflicts:
	django/db/backends/mysql/base.py
	django/db/backends/postgresql_psycopg2/base.py
This commit is contained in:
Andrew Godwin 2012-09-17 20:00:14 +01:00
commit 9313dea700
136 changed files with 2043 additions and 1254 deletions

View File

@ -31,6 +31,8 @@ The PRIMARY AUTHORS are (and/or have been):
* Claude Paroz
* Anssi Kääriäinen
* Florian Apolloner
* Jeremy Dunck
* Bryan Veloso
More information on the main contributors to Django can be found in
docs/internals/committers.txt.
@ -167,7 +169,6 @@ answer newbie questions, and generally made Django that much better:
dready <wil@mojipage.com>
Maximillian Dornseif <md@hudora.de>
Daniel Duan <DaNmarner@gmail.com>
Jeremy Dunck <http://dunck.us/>
Andrew Durdin <adurdin@gmail.com>
dusk@woofle.net
Andy Dustman <farcepest@gmail.com>
@ -506,6 +507,7 @@ answer newbie questions, and generally made Django that much better:
Johan C. Stöver <johan@nilling.nl>
Nowell Strite <http://nowell.strite.org/>
Thomas Stromberg <tstromberg@google.com>
Travis Swicegood <travis@domain51.com>
Pascal Varet
SuperJared
Radek Švarz <http://www.svarz.cz/translate/>

View File

@ -7,7 +7,6 @@ a list of all possible variables.
"""
import os
import re
import time # Needed for Windows
import warnings
@ -26,7 +25,7 @@ class LazySettings(LazyObject):
The user can manually configure settings prior to using them. Otherwise,
Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE.
"""
def _setup(self):
def _setup(self, name):
"""
Load the settings module pointed to by the environment variable. This
is used the first time we need any settings at all, if the user has not
@ -37,12 +36,21 @@ class LazySettings(LazyObject):
if not settings_module: # If it's set but is an empty string.
raise KeyError
except KeyError:
# NOTE: This is arguably an EnvironmentError, but that causes
# problems with Python's interactive help.
raise ImportError("Settings cannot be imported, because environment variable %s is undefined." % ENVIRONMENT_VARIABLE)
raise ImproperlyConfigured(
"Requested setting %s, but settings are not configured. "
"You must either define the environment variable %s "
"or call settings.configure() before accessing settings."
% (name, ENVIRONMENT_VARIABLE))
self._wrapped = Settings(settings_module)
def __getattr__(self, name):
if self._wrapped is empty:
self._setup(name)
return getattr(self._wrapped, name)
def configure(self, default_settings=global_settings, **options):
"""
Called to manually configure the settings. The 'default_settings'

View File

@ -8,13 +8,13 @@ certain test -- e.g. being a DateField or ForeignKey.
import datetime
from django.db import models
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.utils.encoding import smart_text
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.contrib.admin.util import (get_model_from_relation,
reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
from django.contrib.admin.options import IncorrectLookupParameters
class ListFilter(object):
title = None # Human-readable title to appear in the right sidebar.
@ -129,7 +129,10 @@ class FieldListFilter(ListFilter):
return True
def queryset(self, request, queryset):
return queryset.filter(**self.used_parameters)
try:
return queryset.filter(**self.used_parameters)
except ValidationError as e:
raise IncorrectLookupParameters(e)
@classmethod
def register(cls, test, list_filter_class, take_priority=False):

View File

@ -14,9 +14,10 @@ from django.core.exceptions import PermissionDenied, ValidationError
from django.core.paginator import Paginator
from django.core.urlresolvers import reverse
from django.db import models, transaction, router
from django.db.models.constants import LOOKUP_SEP
from django.db.models.related import RelatedObject
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
from django.db.models.sql.constants import LOOKUP_SEP, QUERY_TERMS
from django.db.models.sql.constants import QUERY_TERMS
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template.response import SimpleTemplateResponse, TemplateResponse
@ -1456,8 +1457,10 @@ class InlineModelAdmin(BaseModelAdmin):
return request.user.has_perm(
self.opts.app_label + '.' + self.opts.get_delete_permission())
class StackedInline(InlineModelAdmin):
template = 'admin/edit_inline/stacked.html'
class TabularInline(InlineModelAdmin):
template = 'admin/edit_inline/tabular.html'

View File

@ -9,128 +9,264 @@
* All rights reserved.
*
* Spiced up with Code from Zain Memon's GSoC project 2009
* and modified for Django by Jannis Leidel
* and modified for Django by Jannis Leidel, Travis Swicegood and Julien Phalip.
*
* Licensed under the New BSD License
* See: http://www.opensource.org/licenses/bsd-license.php
*/
(function($) {
$.fn.formset = function(opts) {
var options = $.extend({}, $.fn.formset.defaults, opts);
var updateElementIndex = function(el, prefix, ndx) {
var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
var replacement = prefix + "-" + ndx;
if ($(el).attr("for")) {
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
}
if (el.id) {
el.id = el.id.replace(id_regex, replacement);
}
if (el.name) {
el.name = el.name.replace(id_regex, replacement);
}
};
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
var nextIndex = parseInt(totalForms.val(), 10);
var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
// only show the add button if we are allowed to add more items,
$.fn.formset = function(opts) {
var options = $.extend({}, $.fn.formset.defaults, opts);
var $this = $(this);
var $parent = $this.parent();
var updateElementIndex = function(el, prefix, ndx) {
var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
var replacement = prefix + "-" + ndx;
if ($(el).attr("for")) {
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
}
if (el.id) {
el.id = el.id.replace(id_regex, replacement);
}
if (el.name) {
el.name = el.name.replace(id_regex, replacement);
}
};
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
var nextIndex = parseInt(totalForms.val(), 10);
var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").attr("autocomplete", "off");
// only show the add button if we are allowed to add more items,
// note that max_num = None translates to a blank string.
var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
$(this).each(function(i) {
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
});
if ($(this).length && showAddButton) {
var addButton;
if ($(this).attr("tagName") == "TR") {
// If forms are laid out as table rows, insert the
// "add" button in a new table row:
var numCols = this.eq(-1).children().length;
$(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
addButton = $(this).parent().find("tr:last a");
} else {
// Otherwise, insert it immediately after the last form:
$(this).filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
addButton = $(this).filter(":last").next().find("a");
}
addButton.click(function(e) {
e.preventDefault();
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
var template = $("#" + options.prefix + "-empty");
var row = template.clone(true);
row.removeClass(options.emptyCssClass)
.addClass(options.formCssClass)
.attr("id", options.prefix + "-" + nextIndex);
if (row.is("tr")) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
} else if (row.is("ul") || row.is("ol")) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
}
row.find("*").each(function() {
updateElementIndex(this, options.prefix, totalForms.val());
});
// Insert the new form when it has been fully edited
row.insertBefore($(template));
// Update number of total forms
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
nextIndex += 1;
// Hide add button in case we've hit the max, except we want to add infinitely
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
addButton.parent().hide();
}
// The delete button of each row triggers a bunch of other things
row.find("a." + options.deleteCssClass).click(function(e) {
e.preventDefault();
// Remove the parent form containing this button:
var row = $(this).parents("." + options.formCssClass);
row.remove();
nextIndex -= 1;
// If a post-delete callback was provided, call it with the deleted form:
if (options.removed) {
options.removed(row);
}
// Update the TOTAL_FORMS form count.
var forms = $("." + options.formCssClass);
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
// Show add button again once we drop below max
if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
addButton.parent().show();
}
// Also, update names and ids for all remaining form controls
// so they remain in sequence:
for (var i=0, formCount=forms.length; i<formCount; i++)
{
updateElementIndex($(forms).get(i), options.prefix, i);
$(forms.get(i)).find("*").each(function() {
updateElementIndex(this, options.prefix, i);
});
}
});
// If a post-add callback was supplied, call it with the added form:
if (options.added) {
options.added(row);
}
});
}
return this;
};
/* Setup plugin defaults */
$.fn.formset.defaults = {
prefix: "form", // The form prefix for your django formset
addText: "add another", // Text for the add link
deleteText: "remove", // Text for the delete link
addCssClass: "add-row", // CSS class applied to the add link
deleteCssClass: "delete-row", // CSS class applied to the delete link
emptyCssClass: "empty-row", // CSS class applied to the empty row
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
added: null, // Function called each time a new form is added
removed: null // Function called each time a form is deleted
};
var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
$this.each(function(i) {
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
});
if ($this.length && showAddButton) {
var addButton;
if ($this.attr("tagName") == "TR") {
// If forms are laid out as table rows, insert the
// "add" button in a new table row:
var numCols = this.eq(-1).children().length;
$parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
addButton = $parent.find("tr:last a");
} else {
// Otherwise, insert it immediately after the last form:
$this.filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
addButton = $this.filter(":last").next().find("a");
}
addButton.click(function(e) {
e.preventDefault();
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
var template = $("#" + options.prefix + "-empty");
var row = template.clone(true);
row.removeClass(options.emptyCssClass)
.addClass(options.formCssClass)
.attr("id", options.prefix + "-" + nextIndex);
if (row.is("tr")) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
} else if (row.is("ul") || row.is("ol")) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
}
row.find("*").each(function() {
updateElementIndex(this, options.prefix, totalForms.val());
});
// Insert the new form when it has been fully edited
row.insertBefore($(template));
// Update number of total forms
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
nextIndex += 1;
// Hide add button in case we've hit the max, except we want to add infinitely
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
addButton.parent().hide();
}
// The delete button of each row triggers a bunch of other things
row.find("a." + options.deleteCssClass).click(function(e) {
e.preventDefault();
// Remove the parent form containing this button:
var row = $(this).parents("." + options.formCssClass);
row.remove();
nextIndex -= 1;
// If a post-delete callback was provided, call it with the deleted form:
if (options.removed) {
options.removed(row);
}
// Update the TOTAL_FORMS form count.
var forms = $("." + options.formCssClass);
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
// Show add button again once we drop below max
if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
addButton.parent().show();
}
// Also, update names and ids for all remaining form controls
// so they remain in sequence:
for (var i=0, formCount=forms.length; i<formCount; i++)
{
updateElementIndex($(forms).get(i), options.prefix, i);
$(forms.get(i)).find("*").each(function() {
updateElementIndex(this, options.prefix, i);
});
}
});
// If a post-add callback was supplied, call it with the added form:
if (options.added) {
options.added(row);
}
});
}
return this;
};
/* Setup plugin defaults */
$.fn.formset.defaults = {
prefix: "form", // The form prefix for your django formset
addText: "add another", // Text for the add link
deleteText: "remove", // Text for the delete link
addCssClass: "add-row", // CSS class applied to the add link
deleteCssClass: "delete-row", // CSS class applied to the delete link
emptyCssClass: "empty-row", // CSS class applied to the empty row
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
added: null, // Function called each time a new form is added
removed: null // Function called each time a form is deleted
};
// Tabular inlines ---------------------------------------------------------
$.fn.tabularFormset = function(options) {
var $rows = $(this);
var alternatingRows = function(row) {
$($rows.selector).not(".add-row").removeClass("row1 row2")
.filter(":even").addClass("row1").end()
.filter(":odd").addClass("row2");
};
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
};
var updateSelectFilter = function() {
// If any SelectFilter widgets are a part of the new form,
// instantiate a new SelectFilter instance for it.
if (typeof SelectFilter != 'undefined'){
$('.selectfilter').each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix );
});
$('.selectfilterstacked').each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix );
});
}
};
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this),
input = field.find('input, select, textarea'),
dependency_list = input.data('dependency_list') || [],
dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
};
$rows.formset({
prefix: options.prefix,
addText: options.addText,
formCssClass: "dynamic-" + options.prefix,
deleteCssClass: "inline-deletelink",
deleteText: options.deleteText,
emptyCssClass: "empty-form",
removed: alternatingRows,
added: function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
alternatingRows(row);
}
});
return $rows;
};
// Stacked inlines ---------------------------------------------------------
$.fn.stackedFormset = function(options) {
var $rows = $(this);
var updateInlineLabel = function(row) {
$($rows.selector).find(".inline_label").each(function(i) {
var count = i + 1;
$(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
});
};
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force, yuck.
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
};
var updateSelectFilter = function() {
// If any SelectFilter widgets were added, instantiate a new instance.
if (typeof SelectFilter != "undefined"){
$(".selectfilter").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, options.adminStaticPrefix);
});
$(".selectfilterstacked").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, options.adminStaticPrefix);
});
}
};
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this),
input = field.find('input, select, textarea'),
dependency_list = input.data('dependency_list') || [],
dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
};
$rows.formset({
prefix: options.prefix,
addText: options.addText,
formCssClass: "dynamic-" + options.prefix,
deleteCssClass: "inline-deletelink",
deleteText: options.deleteText,
emptyCssClass: "empty-form",
removed: updateInlineLabel,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
updateInlineLabel(row);
})
});
return $rows;
};
})(django.jQuery);

View File

@ -1,5 +1,9 @@
(function(b){b.fn.formset=function(g){var a=b.extend({},b.fn.formset.defaults,g),k=function(c,f,e){var d=RegExp("("+f+"-(\\d+|__prefix__))");f=f+"-"+e;b(c).attr("for")&&b(c).attr("for",b(c).attr("for").replace(d,f));if(c.id)c.id=c.id.replace(d,f);if(c.name)c.name=c.name.replace(d,f)};g=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off");var l=parseInt(g.val(),10),h=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off");g=h.val()===""||h.val()-g.val()>0;b(this).each(function(){b(this).not("."+
a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&g){var j;if(b(this).attr("tagName")=="TR"){g=this.eq(-1).children().length;b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+g+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>");j=b(this).parent().find("tr:last a")}else{b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>");j=b(this).filter(":last").next().find("a")}j.click(function(c){c.preventDefault();
var f=b("#id_"+a.prefix+"-TOTAL_FORMS");c=b("#"+a.prefix+"-empty");var e=c.clone(true);e.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);if(e.is("tr"))e.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>");else e.is("ul")||e.is("ol")?e.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):e.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+
a.deleteText+"</a></span>");e.find("*").each(function(){k(this,a.prefix,f.val())});e.insertBefore(b(c));b(f).val(parseInt(f.val(),10)+1);l+=1;h.val()!==""&&h.val()-f.val()<=0&&j.parent().hide();e.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();l-=1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);if(h.val()===""||h.val()-d.length>0)j.parent().show();for(var i=0,m=d.length;i<m;i++){k(b(d).get(i),
a.prefix,i);b(d.get(i)).find("*").each(function(){k(this,a.prefix,i)})}});a.added&&a.added(e)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);
(function(b){b.fn.formset=function(d){var a=b.extend({},b.fn.formset.defaults,d),c=b(this),d=c.parent(),i=function(a,e,g){var d=RegExp("("+e+"-(\\d+|__prefix__))"),e=e+"-"+g;b(a).attr("for")&&b(a).attr("for",b(a).attr("for").replace(d,e));a.id&&(a.id=a.id.replace(d,e));a.name&&(a.name=a.name.replace(d,e))},f=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off"),g=parseInt(f.val(),10),e=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off"),f=""===e.val()||0<e.val()-f.val();c.each(function(){b(this).not("."+
a.emptyCssClass).addClass(a.formCssClass)});if(c.length&&f){var h;"TR"==c.attr("tagName")?(c=this.eq(-1).children().length,d.append('<tr class="'+a.addCssClass+'"><td colspan="'+c+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),h=d.find("tr:last a")):(c.filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),h=c.filter(":last").next().find("a"));h.click(function(d){d.preventDefault();var f=b("#id_"+a.prefix+"-TOTAL_FORMS"),d=b("#"+a.prefix+
"-empty"),c=d.clone(true);c.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+g);c.is("tr")?c.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):c.is("ul")||c.is("ol")?c.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):c.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></span>");c.find("*").each(function(){i(this,
a.prefix,f.val())});c.insertBefore(b(d));b(f).val(parseInt(f.val(),10)+1);g=g+1;e.val()!==""&&e.val()-f.val()<=0&&h.parent().hide();c.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();g=g-1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);(e.val()===""||e.val()-d.length>0)&&h.parent().show();for(var c=0,f=d.length;c<f;c++){i(b(d).get(c),a.prefix,c);b(d.get(c)).find("*").each(function(){i(this,
a.prefix,c)})}});a.added&&a.added(c)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null};b.fn.tabularFormset=function(d){var a=b(this),c=function(){b(a.selector).not(".add-row").removeClass("row1 row2").filter(":even").addClass("row1").end().filter(":odd").addClass("row2")};a.formset({prefix:d.prefix,addText:d.addText,formCssClass:"dynamic-"+
d.prefix,deleteCssClass:"inline-deletelink",deleteText:d.deleteText,emptyCssClass:"empty-form",removed:c,added:function(a){a.find(".prepopulated_field").each(function(){var d=b(this).find("input, select, textarea"),c=d.data("dependency_list")||[],e=[];b.each(c,function(d,b){e.push("#"+a.find(".field-"+b).find("input, select, textarea").attr("id"))});e.length&&d.prepopulate(e,d.attr("maxlength"))});"undefined"!=typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),DateTimeShortcuts.init());"undefined"!=
typeof SelectFilter&&(b(".selectfilter").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],false,d.adminStaticPrefix)}),b(".selectfilterstacked").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],true,d.adminStaticPrefix)}));c(a)}});return a};b.fn.stackedFormset=function(d){var a=b(this),c=function(){b(a.selector).find(".inline_label").each(function(a){a+=1;b(this).html(b(this).html().replace(/(#\d+)/g,"#"+a))})};a.formset({prefix:d.prefix,
addText:d.addText,formCssClass:"dynamic-"+d.prefix,deleteCssClass:"inline-deletelink",deleteText:d.deleteText,emptyCssClass:"empty-form",removed:c,added:function(a){a.find(".prepopulated_field").each(function(){var d=b(this).find("input, select, textarea"),c=d.data("dependency_list")||[],e=[];b.each(c,function(d,b){e.push("#"+a.find(".form-row .field-"+b).find("input, select, textarea").attr("id"))});e.length&&d.prepopulate(e,d.attr("maxlength"))});"undefined"!=typeof DateTimeShortcuts&&(b(".datetimeshortcuts").remove(),
DateTimeShortcuts.init());"undefined"!=typeof SelectFilter&&(b(".selectfilter").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],false,d.adminStaticPrefix)}),b(".selectfilterstacked").each(function(a,b){var c=b.name.split("-");SelectFilter.init(b.id,c[c.length-1],true,d.adminStaticPrefix)}));c(a)}});return a}})(django.jQuery);

View File

@ -20,63 +20,11 @@
<script type="text/javascript">
(function($) {
$(document).ready(function() {
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .inline-related";
var updateInlineLabel = function(row) {
$(rows).find(".inline_label").each(function(i) {
var count = i + 1;
$(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
});
};
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force, yuck.
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
};
var updateSelectFilter = function() {
// If any SelectFilter widgets were added, instantiate a new instance.
if (typeof SelectFilter != "undefined"){
$(".selectfilter").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static "admin/" %}");
});
$(".selectfilterstacked").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static "admin/" %}");
});
}
};
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this);
var input = field.find('input, select, textarea');
var dependency_list = input.data('dependency_list') || [];
var dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
};
$(rows).formset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}",
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
deleteCssClass: "inline-deletelink",
deleteText: "{% trans "Remove" %}",
emptyCssClass: "empty-form",
removed: updateInlineLabel,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
updateInlineLabel(row);
})
});
});
$("#{{ inline_admin_formset.formset.prefix }}-group .inline-related").stackedFormset({
prefix: '{{ inline_admin_formset.formset.prefix }}',
adminStaticPrefix: '{% static "admin/" %}',
deleteText: "{% trans "Remove" %}",
addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}"
});
})(django.jQuery);
</script>

View File

@ -67,64 +67,13 @@
</div>
<script type="text/javascript">
(function($) {
$(document).ready(function($) {
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr";
var alternatingRows = function(row) {
$(rows).not(".add-row").removeClass("row1 row2")
.filter(":even").addClass("row1").end()
.filter(rows + ":odd").addClass("row2");
}
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
}
var updateSelectFilter = function() {
// If any SelectFilter widgets are a part of the new form,
// instantiate a new SelectFilter instance for it.
if (typeof SelectFilter != "undefined"){
$(".selectfilter").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static "admin/" %}");
});
$(".selectfilterstacked").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static "admin/" %}");
});
}
}
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this);
var input = field.find('input, select, textarea');
var dependency_list = input.data('dependency_list') || [];
var dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
}
$(rows).formset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}",
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
deleteCssClass: "inline-deletelink",
deleteText: "{% trans "Remove" %}",
emptyCssClass: "empty-form",
removed: alternatingRows,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
alternatingRows(row);
})
});
});
$("#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr").tabularFormset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
adminStaticPrefix: '{% static "admin/" %}',
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
deleteText: "{% trans 'Remove' %}"
});
})(django.jQuery);
</script>

View File

@ -182,7 +182,7 @@ def items_for_result(cl, result, form):
row_class = ''
try:
f, attr, value = lookup_field(field_name, result, cl.model_admin)
except (AttributeError, ObjectDoesNotExist):
except ObjectDoesNotExist:
result_repr = EMPTY_CHANGELIST_VALUE
else:
if f is None:

View File

@ -4,7 +4,7 @@ import datetime
import decimal
from django.db import models
from django.db.models.sql.constants import LOOKUP_SEP
from django.db.models.constants import LOOKUP_SEP
from django.db.models.deletion import Collector
from django.db.models.related import RelatedObject
from django.forms.forms import pretty_name

View File

@ -8,6 +8,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import PermissionDenied
from django.utils.decorators import available_attrs
from django.utils.encoding import force_str
from django.shortcuts import resolve_url
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
@ -23,17 +24,19 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
if test_func(request.user):
return view_func(request, *args, **kwargs)
path = request.build_absolute_uri()
# urlparse chokes on lazy objects in Python 3
login_url_as_str = force_str(login_url or settings.LOGIN_URL)
# urlparse chokes on lazy objects in Python 3, force to str
resolved_login_url = force_str(
resolve_url(login_url or settings.LOGIN_URL))
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(login_url_as_str)[:2]
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(path, login_url, redirect_field_name)
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
return _wrapped_view
return decorator

View File

@ -11,7 +11,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, identify_hasher
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, identify_hasher
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import get_current_site
@ -24,22 +24,22 @@ mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p
class ReadOnlyPasswordHashWidget(forms.Widget):
def render(self, name, value, attrs):
encoded = value
if not is_password_usable(encoded):
return "None"
final_attrs = self.build_attrs(attrs)
try:
hasher = identify_hasher(encoded)
except ValueError:
summary = mark_safe("<strong>Invalid password format or unknown hashing algorithm.</strong>")
if encoded == '' or encoded == UNUSABLE_PASSWORD:
summary = mark_safe("<strong>%s</strong>" % ugettext("No password set."))
else:
summary = format_html_join('',
"<strong>{0}</strong>: {1} ",
((ugettext(key), value)
for key, value in hasher.safe_summary(encoded).items())
)
try:
hasher = identify_hasher(encoded)
except ValueError:
summary = mark_safe("<strong>%s</strong>" % ugettext(
"Invalid password format or unknown hashing algorithm."))
else:
summary = format_html_join('',
"<strong>{0}</strong>: {1} ",
((ugettext(key), value)
for key, value in hasher.safe_summary(encoded).items())
)
return format_html("<div{0}>{1}</div>", flatatt(final_attrs), summary)

View File

@ -28,7 +28,13 @@ def reset_hashers(**kwargs):
def is_password_usable(encoded):
return (encoded is not None and encoded != UNUSABLE_PASSWORD)
if encoded is None or encoded == UNUSABLE_PASSWORD:
return False
try:
hasher = identify_hasher(encoded)
except ValueError:
return False
return True
def check_password(password, encoded, setter=None, preferred='default'):

View File

@ -25,7 +25,7 @@ def update_last_login(sender, user, **kwargs):
the user logging in.
"""
user.last_login = timezone.now()
user.save()
user.save(update_fields=['last_login'])
user_logged_in.connect(update_last_login)

View File

@ -25,7 +25,7 @@ class LoginRequiredTestCase(AuthViewsTestCase):
pass
login_required(normal_view)
def testLoginRequired(self, view_url='/login_required/', login_url=settings.LOGIN_URL):
def testLoginRequired(self, view_url='/login_required/', login_url='/login/'):
"""
Check that login_required works on a simple view wrapped in a
login_required decorator.

View File

@ -236,23 +236,29 @@ class UserChangeFormTest(TestCase):
# Just check we can create it
form = MyUserForm({})
def test_unsuable_password(self):
user = User.objects.get(username='empty_password')
user.set_unusable_password()
user.save()
form = UserChangeForm(instance=user)
self.assertIn(_("No password set."), form.as_table())
def test_bug_17944_empty_password(self):
user = User.objects.get(username='empty_password')
form = UserChangeForm(instance=user)
# Just check that no error is raised.
form.as_table()
self.assertIn(_("No password set."), form.as_table())
def test_bug_17944_unmanageable_password(self):
user = User.objects.get(username='unmanageable_password')
form = UserChangeForm(instance=user)
# Just check that no error is raised.
form.as_table()
self.assertIn(_("Invalid password format or unknown hashing algorithm."),
form.as_table())
def test_bug_17944_unknown_password_algorithm(self):
user = User.objects.get(username='unknown_password')
form = UserChangeForm(instance=user)
# Just check that no error is raised.
form.as_table()
self.assertIn(_("Invalid password format or unknown hashing algorithm."),
form.as_table())
@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))

View File

@ -100,6 +100,10 @@ class TestUtilsHashPass(unittest.TestCase):
self.assertRaises(ValueError, doit)
self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")
def test_bad_encoded(self):
self.assertFalse(is_password_usable('letmein_badencoded'))
self.assertFalse(is_password_usable(''))
def test_low_level_pkbdf2(self):
hasher = PBKDF2PasswordHasher()
encoded = hasher.encode('letmein', 'seasalt')

View File

@ -1,7 +1,7 @@
import os
import re
from django.conf import settings
from django.conf import global_settings, settings
from django.contrib.sites.models import Site, RequestSite
from django.contrib.auth.models import User
from django.core import mail
@ -23,7 +23,8 @@ from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm,
('en', 'English'),
),
LANGUAGE_CODE='en',
TEMPLATE_DIRS = (
TEMPLATE_LOADERS=global_settings.TEMPLATE_LOADERS,
TEMPLATE_DIRS=(
os.path.join(os.path.dirname(__file__), 'templates'),
),
USE_TZ=False,

View File

@ -7,9 +7,9 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, QueryDict
from django.template.response import TemplateResponse
from django.utils.encoding import force_str
from django.utils.http import base36_to_int
from django.utils.translation import ugettext as _
from django.shortcuts import resolve_url
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
@ -38,16 +38,16 @@ def login(request, template_name='registration/login.html',
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
netloc = urlparse(redirect_to)[1]
# Use default setting if redirect_to is empty
if not redirect_to:
redirect_to = settings.LOGIN_REDIRECT_URL
redirect_to = resolve_url(redirect_to)
netloc = urlparse(redirect_to)[1]
# Heavier security check -- don't allow redirection to a different
# host.
elif netloc and netloc != request.get_host():
redirect_to = settings.LOGIN_REDIRECT_URL
if netloc and netloc != request.get_host():
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
# Okay, security checks complete. Log the user in.
auth_login(request, form.get_user())
@ -110,6 +110,7 @@ def logout_then_login(request, login_url=None, current_app=None, extra_context=N
"""
if not login_url:
login_url = settings.LOGIN_URL
login_url = resolve_url(login_url)
return logout(request, login_url, current_app=current_app, extra_context=extra_context)
def redirect_to_login(next, login_url=None,
@ -117,10 +118,9 @@ def redirect_to_login(next, login_url=None,
"""
Redirects the user to the login page, passing the given 'next' page
"""
# urlparse chokes on lazy objects in Python 3
login_url_as_str = force_str(login_url or settings.LOGIN_URL)
resolved_url = resolve_url(login_url or settings.LOGIN_URL)
login_url_parts = list(urlparse(login_url_as_str))
login_url_parts = list(urlparse(resolved_url))
if redirect_field_name:
querystring = QueryDict(login_url_parts[4], mutable=True)
querystring[redirect_field_name] = next
@ -229,7 +229,7 @@ def password_reset_complete(request,
template_name='registration/password_reset_complete.html',
current_app=None, extra_context=None):
context = {
'login_url': settings.LOGIN_URL
'login_url': resolve_url(settings.LOGIN_URL)
}
if extra_context is not None:
context.update(extra_context)

View File

@ -90,8 +90,6 @@ class BaseSpatialOperations(object):
# For quoting column values, rather than columns.
def geo_quote_name(self, name):
if isinstance(name, six.text_type):
name = name.encode('ascii')
return "'%s'" % name
# GeometryField operations

View File

@ -3,20 +3,6 @@ A collection of utility routines and classes used by the spatial
backends.
"""
from django.utils import six
def gqn(val):
"""
The geographic quote name function; used for quoting tables and
geometries (they use single rather than the double quotes of the
backend quotename function).
"""
if isinstance(val, six.string_types):
if isinstance(val, six.text_type): val = val.encode('ascii')
return "'%s'" % val
else:
return str(val)
class SpatialOperation(object):
"""
Base class for generating spatial SQL.

View File

@ -1,5 +1,5 @@
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import FieldDoesNotExist
from django.db.models.sql.constants import LOOKUP_SEP
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.where import Constraint, WhereNode
from django.contrib.gis.db.models.fields import GeometryField

View File

@ -181,7 +181,11 @@ class DataSourceTest(unittest.TestCase):
# Making sure the SpatialReference is as expected.
if hasattr(source, 'srs_wkt'):
self.assertEqual(source.srs_wkt, g.srs.wkt)
self.assertEqual(
source.srs_wkt,
# Depending on lib versions, WGS_84 might be WGS_1984
g.srs.wkt.replace('SPHEROID["WGS_84"', 'SPHEROID["WGS_1984"')
)
def test06_spatial_filter(self):
"Testing the Layer.spatial_filter property."

View File

@ -1,3 +1,4 @@
import json
from binascii import b2a_hex
try:
from django.utils.six.moves import cPickle as pickle
@ -111,8 +112,9 @@ class OGRGeomTest(unittest.TestCase, TestDataMixin):
for g in self.geometries.json_geoms:
geom = OGRGeometry(g.wkt)
if not hasattr(g, 'not_equal'):
self.assertEqual(g.json, geom.json)
self.assertEqual(g.json, geom.geojson)
# Loading jsons to prevent decimal differences
self.assertEqual(json.loads(g.json), json.loads(geom.json))
self.assertEqual(json.loads(g.json), json.loads(geom.geojson))
self.assertEqual(OGRGeometry(g.wkt), OGRGeometry(geom.json))
def test02_points(self):

View File

@ -110,7 +110,7 @@ def geos_version_info():
is a release candidate (and what number release candidate), and the C API
version.
"""
ver = geos_version()
ver = geos_version().decode()
m = version_regex.match(ver)
if not m: raise GEOSException('Could not parse version info string "%s"' % ver)
return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))

View File

@ -215,15 +215,18 @@ class ListMixin(object):
"Standard list reverse method"
self[:] = self[-1::-1]
def sort(self, cmp=cmp, key=None, reverse=False):
def sort(self, cmp=None, key=None, reverse=False):
"Standard list sort method"
if key:
temp = [(key(v),v) for v in self]
temp.sort(cmp=cmp, key=lambda x: x[0], reverse=reverse)
temp.sort(key=lambda x: x[0], reverse=reverse)
self[:] = [v[1] for v in temp]
else:
temp = list(self)
temp.sort(cmp=cmp, reverse=reverse)
if cmp is not None:
temp.sort(cmp=cmp, reverse=reverse)
else:
temp.sort(reverse=reverse)
self[:] = temp
### Private routines ###

View File

@ -16,7 +16,8 @@ test_suites = [
def suite():
"Builds a test suite for the GEOS tests."
s = TestSuite()
map(s.addTest, test_suites)
for suite in test_suites:
s.addTest(suite)
return s
def run(verbosity=1):

View File

@ -1,4 +1,5 @@
import ctypes
import json
import random
from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
@ -204,8 +205,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
for g in self.geometries.json_geoms:
geom = GEOSGeometry(g.wkt)
if not hasattr(g, 'not_equal'):
self.assertEqual(g.json, geom.json)
self.assertEqual(g.json, geom.geojson)
# Loading jsons to prevent decimal differences
self.assertEqual(json.loads(g.json), json.loads(geom.json))
self.assertEqual(json.loads(g.json), json.loads(geom.geojson))
self.assertEqual(GEOSGeometry(g.wkt), GEOSGeometry(geom.json))
def test_fromfile(self):

View File

@ -55,14 +55,14 @@ class ListMixinTest(unittest.TestCase):
def lists_of_len(self, length=None):
if length is None: length = self.limit
pl = range(length)
pl = list(range(length))
return pl, self.listType(pl)
def limits_plus(self, b):
return range(-self.limit - b, self.limit + b)
def step_range(self):
return range(-1 - self.limit, 0) + range(1, 1 + self.limit)
return list(range(-1 - self.limit, 0)) + list(range(1, 1 + self.limit))
def test01_getslice(self):
'Slice retrieval'
@ -160,13 +160,13 @@ class ListMixinTest(unittest.TestCase):
del pl[i:j]
del ul[i:j]
self.assertEqual(pl[:], ul[:], 'del slice [%d:%d]' % (i,j))
for k in range(-Len - 1,0) + range(1,Len):
for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[i:j:k]
del ul[i:j:k]
self.assertEqual(pl[:], ul[:], 'del slice [%d:%d:%d]' % (i,j,k))
for k in range(-Len - 1,0) + range(1,Len):
for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[:i:k]
del ul[:i:k]
@ -177,7 +177,7 @@ class ListMixinTest(unittest.TestCase):
del ul[i::k]
self.assertEqual(pl[:], ul[:], 'del slice [%d::%d]' % (i,k))
for k in range(-Len - 1,0) + range(1,Len):
for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[::k]
del ul[::k]
@ -320,7 +320,7 @@ class ListMixinTest(unittest.TestCase):
pl.sort()
ul.sort()
self.assertEqual(pl[:], ul[:], 'sort')
mid = pl[len(pl) / 2]
mid = pl[len(pl) // 2]
pl.sort(key=lambda x: (mid-x)**2)
ul.sort(key=lambda x: (mid-x)**2)
self.assertEqual(pl[:], ul[:], 'sort w/ key')
@ -330,7 +330,7 @@ class ListMixinTest(unittest.TestCase):
pl.sort(reverse=True)
ul.sort(reverse=True)
self.assertEqual(pl[:], ul[:], 'sort w/ reverse')
mid = pl[len(pl) / 2]
mid = pl[len(pl) // 2]
pl.sort(key=lambda x: (mid-x)**2)
ul.sort(key=lambda x: (mid-x)**2)
self.assertEqual(pl[:], ul[:], 'sort w/ key')
@ -338,7 +338,7 @@ class ListMixinTest(unittest.TestCase):
def test_12_arithmetic(self):
'Arithmetic'
pl, ul = self.lists_of_len()
al = range(10,14)
al = list(range(10,14))
self.assertEqual(list(pl + al), list(ul + al), 'add')
self.assertEqual(type(ul), type(ul + al), 'type of add result')
self.assertEqual(list(al + pl), list(al + ul), 'radd')

View File

@ -191,7 +191,8 @@ class GeoModelTest(TestCase):
cities1 = City.objects.all()
# Only PostGIS would support a 'select *' query because of its recognized
# HEXEWKB format for geometry fields
cities2 = City.objects.raw('select id, name, asText(point) from geoapp_city')
as_text = 'ST_AsText' if postgis else 'asText'
cities2 = City.objects.raw('select id, name, %s(point) from geoapp_city' % as_text)
self.assertEqual(len(cities1), len(list(cities2)))
self.assertTrue(isinstance(cities2[0].point, Point))

View File

@ -8,9 +8,11 @@ from django.utils import unittest
test_srs = ({'srid' : 4326,
'auth_name' : ('EPSG', True),
'auth_srid' : 4326,
'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
'srtext14' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
'proj4' : '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
# Only the beginning, because there are differences depending on installed libs
'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"',
'proj4' : ['+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
# +ellps=WGS84 has been removed in the 4326 proj string in proj-4.8
'+proj=longlat +datum=WGS84 +no_defs '],
'spheroid' : 'WGS 84', 'name' : 'WGS 84',
'geographic' : True, 'projected' : False, 'spatialite' : True,
'ellipsoid' : (6378137.0, 6356752.3, 298.257223563), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
@ -19,9 +21,9 @@ test_srs = ({'srid' : 4326,
{'srid' : 32140,
'auth_name' : ('EPSG', False),
'auth_srid' : 32140,
'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',
'srtext14': 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],AUTHORITY["EPSG","32140"],AXIS["X",EAST],AXIS["Y",NORTH]]',
'proj4' : '+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980"',
'proj4' : ['+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
'+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
'spheroid' : 'GRS 1980', 'name' : 'NAD83 / Texas South Central',
'geographic' : False, 'projected' : True, 'spatialite' : False,
'ellipsoid' : (6378137.0, 6356752.31414, 298.257222101), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
@ -51,17 +53,12 @@ class SpatialRefSysTest(unittest.TestCase):
# No proj.4 and different srtext on oracle backends :(
if postgis:
if connection.ops.spatial_version >= (1, 4, 0):
srtext = sd['srtext14']
else:
srtext = sd['srtext']
self.assertEqual(srtext, srs.wkt)
self.assertEqual(sd['proj4'], srs.proj4text)
self.assertTrue(srs.wkt.startswith(sd['srtext']))
self.assertTrue(srs.proj4text in sd['proj4'])
@no_mysql
def test02_osr(self):
"Testing getting OSR objects from SpatialRefSys model objects."
from django.contrib.gis.gdal import GDAL_VERSION
for sd in test_srs:
sr = SpatialRefSys.objects.get(srid=sd['srid'])
self.assertEqual(True, sr.spheroid.startswith(sd['spheroid']))
@ -76,15 +73,10 @@ class SpatialRefSysTest(unittest.TestCase):
# Testing the SpatialReference object directly.
if postgis or spatialite:
srs = sr.srs
if GDAL_VERSION <= (1, 8):
self.assertEqual(sd['proj4'], srs.proj4)
self.assertTrue(srs.proj4 in sd['proj4'])
# No `srtext` field in the `spatial_ref_sys` table in SpatiaLite
if not spatialite:
if connection.ops.spatial_version >= (1, 4, 0):
srtext = sd['srtext14']
else:
srtext = sd['srtext']
self.assertEqual(srtext, srs.wkt)
self.assertTrue(srs.wkt.startswith(sd['srtext']))
@no_mysql
def test03_ellipsoid(self):

View File

@ -47,6 +47,9 @@ def markdown(value, arg=''):
they will be silently ignored.
"""
import warnings
warnings.warn('The markdown filter has been deprecated',
category=DeprecationWarning)
try:
import markdown
except ImportError:
@ -72,6 +75,9 @@ def markdown(value, arg=''):
@register.filter(is_safe=True)
def restructuredtext(value):
import warnings
warnings.warn('The restructuredtext filter has been deprecated',
category=DeprecationWarning)
try:
from docutils.core import publish_parts
except ImportError:

View File

@ -1,7 +1,9 @@
# Quick tests for the markup templatetags (django.contrib.markup)
import re
import warnings
from django.template import Template, Context
from django import test
from django.utils import unittest
from django.utils.html import escape
@ -21,7 +23,7 @@ try:
except ImportError:
docutils = None
class Templates(unittest.TestCase):
class Templates(test.TestCase):
textile_content = """Paragraph 1
@ -37,6 +39,13 @@ Paragraph 2 with a link_
.. _link: http://www.example.com/"""
def setUp(self):
self.save_warnings_state()
warnings.filterwarnings('ignore', category=DeprecationWarning, module='django.contrib.markup')
def tearDown(self):
self.restore_warnings_state()
@unittest.skipUnless(textile, 'textile not installed')
def test_textile(self):
t = Template("{% load markup %}{{ textile_content|textile }}")

View File

@ -46,10 +46,10 @@ class CookieStorage(BaseStorage):
Stores messages in a cookie.
"""
cookie_name = 'messages'
# We should be able to store 4K in a cookie, but Internet Explorer
# imposes 4K as the *total* limit for a domain. To allow other
# cookies, we go for 3/4 of 4K.
max_cookie_size = 3072
# uwsgi's default configuration enforces a maximum size of 4kb for all the
# HTTP headers. In order to leave some room for other cookies and headers,
# restrict the session cookie to 1/2 of 4kb. See #18781.
max_cookie_size = 2048
not_finished = '__messagesnotfinished__'
def _get(self, *args, **kwargs):

View File

@ -152,7 +152,7 @@ class BaseTest(TestCase):
cycle.
"""
data = {
'messages': ['Test message %d' % x for x in range(10)],
'messages': ['Test message %d' % x for x in range(5)],
}
show_url = reverse('django.contrib.messages.tests.urls.show')
for level in ('debug', 'info', 'success', 'warning', 'error'):
@ -170,7 +170,7 @@ class BaseTest(TestCase):
@override_settings(MESSAGE_LEVEL=constants.DEBUG)
def test_with_template_response(self):
data = {
'messages': ['Test message %d' % x for x in range(10)],
'messages': ['Test message %d' % x for x in range(5)],
}
show_url = reverse('django.contrib.messages.tests.urls.show_template_response')
for level in self.levels.keys():
@ -194,7 +194,7 @@ class BaseTest(TestCase):
before a GET.
"""
data = {
'messages': ['Test message %d' % x for x in range(10)],
'messages': ['Test message %d' % x for x in range(5)],
}
show_url = reverse('django.contrib.messages.tests.urls.show')
messages = []
@ -226,7 +226,7 @@ class BaseTest(TestCase):
when one attempts to store a message.
"""
data = {
'messages': ['Test message %d' % x for x in range(10)],
'messages': ['Test message %d' % x for x in range(5)],
}
show_url = reverse('django.contrib.messages.tests.urls.show')
for level in ('debug', 'info', 'success', 'warning', 'error'):
@ -251,7 +251,7 @@ class BaseTest(TestCase):
raised if 'fail_silently' = True
"""
data = {
'messages': ['Test message %d' % x for x in range(10)],
'messages': ['Test message %d' % x for x in range(5)],
'fail_silently': True,
}
show_url = reverse('django.contrib.messages.tests.urls.show')

View File

@ -1,4 +1,4 @@
from datetime import datetime, timedelta
from datetime import timedelta
import shutil
import string
import tempfile
@ -302,11 +302,11 @@ class CacheDBSessionTests(SessionTestsMixin, TestCase):
self.assertTrue(self.session.exists(self.session.session_key))
def test_load_overlong_key(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
# Some backends might issue a warning
with warnings.catch_warnings():
warnings.simplefilter("ignore")
self.session._session_key = (string.ascii_letters + string.digits) * 20
self.assertEqual(self.session.load(), {})
self.assertEqual(len(w), 1)
@override_settings(USE_TZ=True)
@ -352,11 +352,11 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
backend = CacheSession
def test_load_overlong_key(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
# Some backends might issue a warning
with warnings.catch_warnings():
warnings.simplefilter("ignore")
self.session._session_key = (string.ascii_letters + string.digits) * 20
self.assertEqual(self.session.load(), {})
self.assertEqual(len(w), 1)
class SessionMiddlewareTests(unittest.TestCase):

View File

@ -223,18 +223,17 @@ class WSGIHandler(base.BaseHandler):
set_script_prefix(base.get_script_name(environ))
signals.request_started.send(sender=self.__class__)
try:
try:
request = self.request_class(environ)
except UnicodeDecodeError:
logger.warning('Bad Request (UnicodeDecodeError)',
exc_info=sys.exc_info(),
extra={
'status_code': 400,
}
)
response = http.HttpResponseBadRequest()
else:
response = self.get_response(request)
request = self.request_class(environ)
except UnicodeDecodeError:
logger.warning('Bad Request (UnicodeDecodeError)',
exc_info=sys.exc_info(),
extra={
'status_code': 400,
}
)
response = http.HttpResponseBadRequest()
else:
response = self.get_response(request)
finally:
signals.request_finished.send(sender=self.__class__)

View File

@ -5,6 +5,7 @@ from optparse import OptionParser, NO_DEFAULT
import imp
import warnings
from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError, handle_default_options
from django.core.management.color import color_style
from django.utils.importlib import import_module
@ -105,7 +106,7 @@ def get_commands():
try:
from django.conf import settings
apps = settings.INSTALLED_APPS
except (AttributeError, EnvironmentError, ImportError):
except (AttributeError, ImproperlyConfigured):
apps = []
# Find and load the management module for each installed app.

View File

@ -1,4 +1,5 @@
from optparse import make_option
from datetime import datetime
import os
import re
import sys
@ -90,10 +91,12 @@ class Command(BaseCommand):
self.stdout.write("Validating models...\n\n")
self.validate(display_num_errors=True)
self.stdout.write((
"%(started_at)s\n"
"Django version %(version)s, using settings %(settings)r\n"
"Development server is running at http://%(addr)s:%(port)s/\n"
"Quit the server with %(quit_command)s.\n"
) % {
"started_at": datetime.now().strftime('%B %d, %Y - %X'),
"version": self.get_version(),
"settings": settings.SETTINGS_MODULE,
"addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr,

View File

@ -1054,9 +1054,12 @@ class BaseDatabaseIntrospection(object):
def get_primary_key_column(self, cursor, table_name):
"""
Backends can override this to return the column name of the primary key for the given table.
Returns the name of the primary key column for the given table.
"""
raise NotImplementedError
for column in six.iteritems(self.get_indexes(cursor, table_name)):
if column[1]['primary_key']:
return column[0]
return None
def get_indexes(self, cursor, table_name):
"""

View File

@ -38,6 +38,7 @@ from django.db.backends.mysql.creation import DatabaseCreation
from django.db.backends.mysql.introspection import DatabaseIntrospection
from django.db.backends.mysql.validation import DatabaseValidation
from django.db.backends.mysql.schema import DatabaseSchemaEditor
from django.utils.encoding import force_str
from django.utils.functional import cached_property
from django.utils.safestring import SafeBytes, SafeText
from django.utils import six
@ -392,7 +393,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if settings_dict['NAME']:
kwargs['db'] = settings_dict['NAME']
if settings_dict['PASSWORD']:
kwargs['passwd'] = settings_dict['PASSWORD']
kwargs['passwd'] = force_str(settings_dict['PASSWORD'])
if settings_dict['HOST'].startswith('/'):
kwargs['unix_socket'] = settings_dict['HOST']
elif settings_dict['HOST']:

View File

@ -2,7 +2,6 @@ import re
from .base import FIELD_TYPE
from django.db.backends import BaseDatabaseIntrospection
from django.utils import six
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
@ -88,15 +87,6 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
key_columns.extend(cursor.fetchall())
return key_columns
def get_primary_key_column(self, cursor, table_name):
"""
Returns the name of the primary key column for the given table
"""
for column in six.iteritems(self.get_indexes(cursor, table_name)):
if column[1]['primary_key']:
return column[0]
return None
def get_indexes(self, cursor, table_name):
cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name))
# Do a two-pass search for indexes: on first pass check which indexes

View File

@ -14,6 +14,7 @@ from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation
from django.db.backends.postgresql_psycopg2.version import get_version
from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.utils.encoding import force_str
from django.utils.log import getLogger
from django.utils.safestring import SafeText, SafeBytes
from django.utils import six
@ -175,7 +176,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if settings_dict['USER']:
conn_params['user'] = settings_dict['USER']
if settings_dict['PASSWORD']:
conn_params['password'] = settings_dict['PASSWORD']
conn_params['password'] = force_str(settings_dict['PASSWORD'])
if settings_dict['HOST']:
conn_params['host'] = settings_dict['HOST']
if settings_dict['PORT']:

View File

@ -0,0 +1,7 @@
"""
Constants used across the ORM in general.
"""
# Separator used to split filter strings apart.
LOOKUP_SEP = '__'

View File

@ -8,6 +8,7 @@ import sys
from django.core import exceptions
from django.db import connections, router, transaction, IntegrityError
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import AutoField
from django.db.models.query_utils import (Q, select_related_descend,
deferred_class_factory, InvalidQuery)
@ -1613,8 +1614,6 @@ def prefetch_related_objects(result_cache, related_lookups):
Populates prefetched objects caches for a list of results
from a QuerySet
"""
from django.db.models.sql.constants import LOOKUP_SEP
if len(result_cache) == 0:
return # nothing to do

View File

@ -3,9 +3,10 @@ from django.utils.six.moves import zip
from django.core.exceptions import FieldError
from django.db import transaction
from django.db.backends.util import truncate_name
from django.db.models.constants import LOOKUP_SEP
from django.db.models.query_utils import select_related_descend
from django.db.models.sql.constants import (SINGLE, MULTI, ORDER_DIR,
LOOKUP_SEP, GET_ITERATOR_CHUNK_SIZE)
GET_ITERATOR_CHUNK_SIZE)
from django.db.models.sql.datastructures import EmptyResultSet
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.query import get_order_dir, Query
@ -608,8 +609,12 @@ class SQLCompiler(object):
restricted = False
for f, model in opts.get_fields_with_model():
# The get_fields_with_model() returns None for fields that live
# in the field's local model. So, for those fields we want to use
# the f.model - that is the field's local model.
field_model = model or f.model
if not select_related_descend(f, restricted, requested,
only_load.get(model or self.query.model)):
only_load.get(field_model)):
continue
# The "avoid" set is aliases we want to avoid just for this
# particular branch of the recursion. They aren't permanently

View File

@ -1,7 +1,13 @@
"""
Constants specific to the SQL storage portion of the ORM.
"""
from collections import namedtuple
import re
# Valid query types (a set is used for speedy lookups).
# Valid query types (a set is used for speedy lookups). These are (currently)
# considered SQL-specific; other storage systems may choose to use different
# lookup types.
QUERY_TERMS = set([
'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in',
'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year',
@ -12,9 +18,6 @@ QUERY_TERMS = set([
# Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100
# Separator used to split filter strings apart.
LOOKUP_SEP = '__'
# Constants to make looking up tuple values clearer.
# Join lists (indexes into the tuples that are values in the alias_map
# dictionary in the Query class).

View File

@ -1,6 +1,6 @@
from django.core.exceptions import FieldError
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import FieldDoesNotExist
from django.db.models.sql.constants import LOOKUP_SEP
class SQLEvaluator(object):
def __init__(self, expression, query, allow_joins=True):

View File

@ -15,11 +15,12 @@ from django.utils.tree import Node
from django.utils import six
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.models import signals
from django.db.models.constants import LOOKUP_SEP
from django.db.models.expressions import ExpressionNode
from django.db.models.fields import FieldDoesNotExist
from django.db.models.sql import aggregates as base_aggregates_module
from django.db.models.sql.constants import (QUERY_TERMS, LOOKUP_SEP, ORDER_DIR,
SINGLE, ORDER_PATTERN, JoinInfo)
from django.db.models.sql.constants import (QUERY_TERMS, ORDER_DIR, SINGLE,
ORDER_PATTERN, JoinInfo)
from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode,

View File

@ -3,6 +3,7 @@ Query subclasses which provide extra functionality beyond simple data retrieval.
"""
from django.core.exceptions import FieldError
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models.sql.constants import *
from django.db.models.sql.datastructures import Date

View File

@ -199,7 +199,7 @@ class CharField(Field):
def widget_attrs(self, widget):
attrs = super(CharField, self).widget_attrs(widget)
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
if self.max_length is not None and isinstance(widget, TextInput):
# The HTML attribute is maxlength, not max_length.
attrs.update({'maxlength': str(self.max_length)})
return attrs

View File

@ -260,10 +260,17 @@ class Input(Widget):
final_attrs['value'] = force_text(self._format_value(value))
return format_html('<input{0} />', flatatt(final_attrs))
class TextInput(Input):
input_type = 'text'
class PasswordInput(Input):
def __init__(self, attrs=None):
if attrs is not None:
self.input_type = attrs.pop('type', self.input_type)
super(TextInput, self).__init__(attrs)
class PasswordInput(TextInput):
input_type = 'password'
def __init__(self, attrs=None, render_value=False):
@ -400,9 +407,8 @@ class Textarea(Widget):
flatatt(final_attrs),
force_text(value))
class DateInput(Input):
input_type = 'text'
class DateInput(TextInput):
def __init__(self, attrs=None, format=None):
super(DateInput, self).__init__(attrs)
if format:
@ -431,9 +437,8 @@ class DateInput(Input):
pass
return super(DateInput, self)._has_changed(self._format_value(initial), data)
class DateTimeInput(Input):
input_type = 'text'
class DateTimeInput(TextInput):
def __init__(self, attrs=None, format=None):
super(DateTimeInput, self).__init__(attrs)
if format:
@ -462,9 +467,8 @@ class DateTimeInput(Input):
pass
return super(DateTimeInput, self)._has_changed(self._format_value(initial), data)
class TimeInput(Input):
input_type = 'text'
class TimeInput(TextInput):
def __init__(self, attrs=None, format=None):
super(TimeInput, self).__init__(attrs)
if format:

View File

@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals
import copy
import datetime
from email.header import Header
import os
import re
import sys
@ -560,31 +561,44 @@ class HttpResponse(object):
else:
__str__ = serialize
def _convert_to_ascii(self, *values):
"""Converts all values to ascii strings."""
for value in values:
if not isinstance(value, six.string_types):
value = str(value)
try:
if six.PY3:
# Ensure string only contains ASCII
value.encode('us-ascii')
def _convert_to_charset(self, value, charset, mime_encode=False):
"""Converts headers key/value to ascii/latin1 native strings.
`charset` must be 'ascii' or 'latin-1'. If `mime_encode` is True and
`value` value can't be represented in the given charset, MIME-encoding
is applied.
"""
if not isinstance(value, (bytes, six.text_type)):
value = str(value)
try:
if six.PY3:
if isinstance(value, str):
# Ensure string is valid in given charset
value.encode(charset)
else:
if isinstance(value, str):
# Ensure string only contains ASCII
value.decode('us-ascii')
else:
# Convert unicode to an ASCII string
value = value.encode('us-ascii')
except UnicodeError as e:
e.reason += ', HTTP response headers must be in US-ASCII format'
# Convert bytestring using given charset
value = value.decode(charset)
else:
if isinstance(value, str):
# Ensure string is valid in given charset
value.decode(charset)
else:
# Convert unicode string to given charset
value = value.encode(charset)
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())
else:
e.reason += ', HTTP response headers must be in %s format' % charset
raise
if '\n' in value or '\r' in value:
raise BadHeaderError("Header values can't contain newlines (got %r)" % value)
yield value
if str('\n') in value or str('\r') in value:
raise BadHeaderError("Header values can't contain newlines (got %r)" % value)
return value
def __setitem__(self, header, value):
header, value = self._convert_to_ascii(header, value)
header = self._convert_to_charset(header, 'ascii')
value = self._convert_to_charset(value, 'latin1', mime_encode=True)
self._headers[header.lower()] = (header, value)
def __delitem__(self, header):

View File

@ -105,7 +105,7 @@ class CsrfViewMiddleware(object):
if getattr(callback, 'csrf_exempt', False):
return None
# Assume that anything not defined as 'safe' by RC2616 needs protection
# Assume that anything not defined as 'safe' by RFC2616 needs protection
if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
if getattr(request, '_dont_enforce_csrf_checks', False):
# Mechanism to turn off CSRF checks for test suite.

View File

@ -66,23 +66,7 @@ def redirect(to, *args, **kwargs):
else:
redirect_class = HttpResponseRedirect
# If it's a model, use get_absolute_url()
if hasattr(to, 'get_absolute_url'):
return redirect_class(to.get_absolute_url())
# Next try a reverse URL resolution.
try:
return redirect_class(urlresolvers.reverse(to, args=args, kwargs=kwargs))
except urlresolvers.NoReverseMatch:
# If this is a callable, re-raise.
if callable(to):
raise
# If this doesn't "feel" like a URL, re-raise.
if '/' not in to and '.' not in to:
raise
# Finally, fall back and assume it's a URL
return redirect_class(to)
return redirect_class(resolve_url(to, *args, **kwargs))
def _get_queryset(klass):
"""
@ -128,3 +112,34 @@ def get_list_or_404(klass, *args, **kwargs):
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
return obj_list
def resolve_url(to, *args, **kwargs):
"""
Return a URL appropriate for the arguments passed.
The arguments could be:
* A model: the model's `get_absolute_url()` function will be called.
* A view name, possibly with arguments: `urlresolvers.reverse()` will
be used to reverse-resolve the name.
* A URL, which will be returned as-is.
"""
# If it's a model, use get_absolute_url()
if hasattr(to, 'get_absolute_url'):
return to.get_absolute_url()
# Next try a reverse URL resolution.
try:
return urlresolvers.reverse(to, args=args, kwargs=kwargs)
except urlresolvers.NoReverseMatch:
# If this is a callable, re-raise.
if callable(to):
raise
# If this doesn't "feel" like a URL, re-raise.
if '/' not in to and '.' not in to:
raise
# Finally, fall back and assume it's a URL
return to

View File

@ -531,11 +531,9 @@ def cycle(parser, token):
The optional flag "silent" can be used to prevent the cycle declaration
from returning any value::
{% cycle 'row1' 'row2' as rowcolors silent %}{# no value here #}
{% for o in some_list %}
<tr class="{% cycle rowcolors %}">{# first value will be "row1" #}
...
</tr>
{% cycle 'row1' 'row2' as rowcolors silent %}
<tr class="{{ rowcolors }}">{% include "subtemplate.html " %}</tr>
{% endfor %}
"""

View File

@ -51,6 +51,13 @@ def clear_context_processors_cache(**kwargs):
context._standard_context_processors = None
@receiver(setting_changed)
def clear_template_loaders_cache(**kwargs):
if kwargs['setting'] == 'TEMPLATE_LOADERS':
from django.template import loader
loader.template_source_loaders = None
@receiver(setting_changed)
def clear_serializers_cache(**kwargs):
if kwargs['setting'] == 'SERIALIZATION_MODULES':

View File

@ -1,6 +1,6 @@
import os
import stat
from os.path import join, normcase, normpath, abspath, isabs, sep
from os.path import join, normcase, normpath, abspath, isabs, sep, dirname
from django.utils.encoding import force_text
from django.utils import six
@ -41,13 +41,16 @@ def safe_join(base, *paths):
paths = [force_text(p) for p in paths]
final_path = abspathu(join(base, *paths))
base_path = abspathu(base)
base_path_len = len(base_path)
# Ensure final_path starts with base_path (using normcase to ensure we
# don't false-negative on case insensitive operating systems like Windows)
# and that the next character after the final path is os.sep (or nothing,
# in which case final_path must be equal to base_path).
if not normcase(final_path).startswith(normcase(base_path)) \
or final_path[base_path_len:base_path_len+1] not in ('', sep):
# don't false-negative on case insensitive operating systems like Windows),
# further, one of the following conditions must be true:
# a) The next character is the path separator (to prevent conditions like
# safe_join("/dir", "/../d"))
# b) The final path must be the same as the base path.
# c) The base path must be the most root path (meaning either "/" or "C:\\")
if (not normcase(final_path).startswith(normcase(base_path + sep)) and
normcase(final_path) != normcase(base_path) and
dirname(normcase(base_path)) != normcase(base_path)):
raise ValueError('The joined path (%s) is located outside of the base '
'path component (%s)' % (final_path, base_path))
return final_path

View File

@ -5,8 +5,7 @@ import sys
current_version = sys.version_info
use_workaround = (
(current_version < (2, 6, 8)) or
(current_version >= (2, 7) and current_version < (2, 7, 3)) or
(current_version < (2, 7, 3)) or
(current_version >= (3, 0) and current_version < (3, 2, 3))
)

View File

@ -372,6 +372,9 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
return qs
def get_date_list_period(self):
"""
Get the aggregation period for the list of dates: 'year', 'month', or 'day'.
"""
return self.date_list_period
def get_date_list(self, queryset, date_type=None):

View File

@ -1,9 +1,8 @@
The documentation in this tree is in plain text files and can be viewed using
any text file viewer.
Technically speaking, it uses ReST (reStructuredText) [1], and the Sphinx
documentation system [2]. This allows it to be built into other forms for
easier viewing and browsing.
It uses ReST (reStructuredText) [1], and the Sphinx documentation system [2].
This allows it to be built into other forms for easier viewing and browsing.
To create an HTML version of the docs:

View File

@ -28,14 +28,3 @@ Indices, glossary and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`glossary`
Deprecated/obsolete documentation
=================================
The following documentation covers features that have been deprecated or that
have been replaced in newer versions of Django.
.. toctree::
:maxdepth: 2
obsolete/index

View File

@ -91,8 +91,7 @@ The dynamically-generated admin site is ugly! How can I change it?
We like it, but if you don't agree, you can modify the admin site's
presentation by editing the CSS stylesheet and/or associated image files. The
site is built using semantic HTML and plenty of CSS hooks, so any changes you'd
like to make should be possible by editing the stylesheet. We've got a
:doc:`guide to the CSS used in the admin </obsolete/admin-css>` to get you started.
like to make should be possible by editing the stylesheet.
What browsers are supported for using the admin?
------------------------------------------------

View File

@ -16,8 +16,9 @@ How do I get started?
What are Django's prerequisites?
--------------------------------
Django requires Python_, specifically Python 2.6.5 - 2.7.x. No other Python
libraries are required for basic Django usage.
Django requires Python, specifically Python 2.6.5 - 2.7.x. No other Python
libraries are required for basic Django usage. Django 1.5 also has
experimental support for Python 3.2 and above.
For a development environment -- if you just want to experiment with Django --
you don't need to have a separate Web server installed; Django comes with its
@ -50,15 +51,12 @@ aren't available under older versions of Python.
Third-party applications for use with Django are, of course, free to set their
own version requirements.
Over the next year or two Django will begin dropping support for older Python
versions as part of a migration which will end with Django running on Python 3
(see below for details).
All else being equal, we recommend that you use the latest 2.x release
(currently Python 2.7). This will let you take advantage of the numerous
improvements and optimizations to the Python language since version 2.6, and
will help ease the process of dropping support for older Python versions on
the road to Python 3.
improvements and optimizations to the Python language since version 2.6.
Generally speaking, we don't recommend running Django on Python 3 yet; see
below for more.
What Python version can I use with Django?
------------------------------------------
@ -71,25 +69,21 @@ Django version Python versions
1.2 2.4, 2.5, 2.6, 2.7
1.3 2.4, 2.5, 2.6, 2.7
**1.4** **2.5, 2.6, 2.7**
*1.5 (future)* *2.6, 2.7, 3.x (experimental)*
*1.5 (future)* *2.6, 2.7* and *3.2, 3.3 (experimental)*
============== ===============
Can I use Django with Python 3?
-------------------------------
Not at the moment. Python 3.0 introduced a number of
backwards-incompatible changes to the Python language, and although
these changes are generally a good thing for Python's future, it will
be a while before most Python software catches up and is able to run
on Python 3.0. For larger Python-based software like Django, the
transition is expected to take at least a year or two (since it
involves dropping support for older Python releases and so must be
done gradually).
Django 1.5 introduces experimental support for Python 3.2 and 3.3. However, we
don't yet suggest that you use Django and Python 3 in production.
In the meantime, Python 2.x releases will be supported and provided
with bug fixes and security updates by the Python development team, so
continuing to use a Python 2.x release during the transition should
not present any risk.
Python 3 support should be considered a "preview". It's offered to bootstrap
the transition of the Django ecosystem to Python 3, and to help you start
porting your apps for future Python 3 compatibility. But we're not yet
confident enough to promise stability in production.
Our current plan is to make Django 1.6 suitable for general use with Python 3.
Will Django run under shared hosting (like TextDrive or Dreamhost)?
-------------------------------------------------------------------

View File

@ -11,7 +11,7 @@ Then, just do this::
>>> from django.db import connection
>>> connection.queries
[{'sql': 'SELECT polls_polls.id,polls_polls.question,polls_polls.pub_date FROM polls_polls',
[{'sql': 'SELECT polls_polls.id, polls_polls.question, polls_polls.pub_date FROM polls_polls',
'time': '0.002'}]
``connection.queries`` is only available if :setting:`DEBUG` is ``True``.

View File

@ -181,10 +181,10 @@ card values plus their suits; 104 characters in total.
Many of Django's model fields accept options that they don't do anything
with. For example, you can pass both
:attr:`~django.db.models.Field.editable` and
:attr:`~django.db.models.Field.auto_now` to a
:attr:`~django.db.models.DateField.auto_now` to a
:class:`django.db.models.DateField` and it will simply ignore the
:attr:`~django.db.models.Field.editable` parameter
(:attr:`~django.db.models.Field.auto_now` being set implies
(:attr:`~django.db.models.DateField.auto_now` being set implies
``editable=False``). No error is raised in this case.
This behavior simplifies the field classes, because they don't need to
@ -516,8 +516,8 @@ for the first time, the ``add`` parameter will be ``True``, otherwise it will be
You only need to override this method if you want to preprocess the value
somehow, just before saving. For example, Django's
:class:`~django.db.models.DateTimeField` uses this method to set the attribute
correctly in the case of :attr:`~django.db.models.Field.auto_now` or
:attr:`~django.db.models.Field.auto_now_add`.
correctly in the case of :attr:`~django.db.models.DateField.auto_now` or
:attr:`~django.db.models.DateField.auto_now_add`.
If you do override this method, you must return the value of the attribute at
the end. You should also update the model's attribute if you make any changes

View File

@ -5,7 +5,7 @@
Django documentation
====================
.. rubric:: Everything you need to know about Django (and then some).
.. rubric:: Everything you need to know about Django.
Getting help
============

View File

@ -379,6 +379,34 @@ Florian Apolloner
.. _Graz University of Technology: http://tugraz.at/
.. _Ubuntuusers webteam: http://wiki.ubuntuusers.de/ubuntuusers/Webteam
Jeremy Dunck
Jeremy was rescued from corporate IT drudgery by Free Software and, in part,
Django. Many of Jeremy's interests center around access to information.
Jeremy was the lead developer of Pegasus News, one of the first uses of
Django outside World Online, and has since joined Votizen, a startup intent
on reducing the influence of money in politics.
He serves as DSF Secretary, organizes and helps organize sprints, cares
about the health and equity of the Django community. He has gone an
embarrassingly long time without a working blog.
Jeremy lives in Mountain View, CA, USA.
`Bryan Veloso`_
Bryan found Django 0.96 through a fellow designer who was evangelizing
its use. It was his first foray outside of the land that was PHP-based
templating. Although he has only ever used Django for personal projects,
it is the very reason he considers himself a designer/developer
hybrid and is working to further design within the Django community.
Bryan works as a designer at GitHub by day, and masquerades as a `vlogger`_
and `shoutcaster`_ in the after-hours. Bryan lives in Los Angeles, CA, USA.
.. _bryan veloso: http://avalonstar.com/
.. _vlogger: http://youtube.com/bryanveloso/
.. _shoutcaster: http://twitch.tv/vlogalonstar/
Specialists
-----------
@ -403,16 +431,6 @@ Ian Kelly
Matt Boersma
Matt is also responsible for Django's Oracle support.
Jeremy Dunck
Jeremy is the lead developer of Pegasus News, a personalized local site based
in Dallas, Texas. An early contributor to Greasemonkey and Django, he sees
technology as a tool for communication and access to knowledge.
Jeremy helped kick off GeoDjango development, and is mostly responsible for
the serious speed improvements that signals received in Django 1.0.
Jeremy lives in Dallas, Texas, USA.
`Simon Meers`_
Simon discovered Django 0.96 during his Computer Science PhD research and
has been developing with it full-time ever since. His core code

View File

@ -264,6 +264,9 @@ these changes.
in 1.4. The backward compatibility will be removed --
``HttpRequest.raw_post_data`` will no longer work.
* ``django.contrib.markup`` will be removed following an accelerated
deprecation.
1.7
---

View File

@ -10,11 +10,9 @@ Install Python
--------------
Being a Python Web framework, Django requires Python. It works with any Python
version from 2.6.5 to 2.7 (due to backwards incompatibilities in Python 3.0,
Django does not currently work with Python 3.0; see :doc:`the Django FAQ
</faq/install>` for more information on supported Python versions and the 3.0
transition), these versions of Python include a lightweight database called
SQLite_ so you won't need to set up a database just yet.
version from 2.6.5 to 2.7. It also features experimental support for versions
3.2 and 3.3. All these versions of Python include a lightweight database
called SQLite_ so you won't need to set up a database just yet.
.. _sqlite: http://sqlite.org/

View File

@ -31,7 +31,7 @@ the file ``mysite/news/models.py``::
return self.full_name
class Article(models.Model):
pub_date = models.DateTimeField()
pub_date = models.DateField()
headline = models.CharField(max_length=200)
content = models.TextField()
reporter = models.ForeignKey(Reporter)
@ -96,8 +96,8 @@ access your data. The API is created on the fly, no code generation necessary::
DoesNotExist: Reporter matching query does not exist. Lookup parameters were {'id': 2}
# Create an article.
>>> from datetime import datetime
>>> a = Article(pub_date=datetime.now(), headline='Django is cool',
>>> from datetime import date
>>> a = Article(pub_date=date.today(), headline='Django is cool',
... content='Yeah.', reporter=r)
>>> a.save()
@ -140,7 +140,7 @@ as registering your model in the admin site::
from django.db import models
class Article(models.Model):
pub_date = models.DateTimeField()
pub_date = models.DateField()
headline = models.CharField(max_length=200)
content = models.TextField()
reporter = models.ForeignKey(Reporter)

View File

@ -513,8 +513,9 @@ Here's what happens if a user goes to "/polls/34/" in this system:
further processing.
Now that we've decoupled that, we need to decouple the ``polls.urls``
URLconf by removing the leading "polls/" from each line, and removing the
lines registering the admin site. Your ``polls/urls.py`` file should now look like
URLconf by removing the leading "polls/" from each line, removing the
lines registering the admin site, and removing the ``include`` import which
is no longer used. Your ``polls/urls.py`` file should now look like
this::
from django.conf.urls import patterns, url

View File

@ -218,7 +218,7 @@ Read on for details.
First, open the ``polls/urls.py`` URLconf. It looks like this, according to the
tutorial so far::
from django.conf.urls import patterns, include, url
from django.conf.urls import patterns, url
urlpatterns = patterns('polls.views',
url(r'^$', 'index'),
@ -229,7 +229,7 @@ tutorial so far::
Change it like so::
from django.conf.urls import patterns, include, url
from django.conf.urls import patterns, url
from django.views.generic import DetailView, ListView
from polls.models import Poll

View File

@ -67,8 +67,7 @@ different needs:
whathaveyou.
* Finally, there's some "specialized" documentation not usually relevant to
most developers. This includes the :doc:`release notes </releases/index>`,
:doc:`documentation of obsolete features </obsolete/index>`,
most developers. This includes the :doc:`release notes </releases/index>` and
:doc:`internals documentation </internals/index>` for those who want to add
code to Django itself, and a :doc:`few other things that simply don't fit
elsewhere </misc/index>`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,186 +0,0 @@
======================================
Customizing the Django admin interface
======================================
.. warning::
The design of the admin has changed somewhat since this document was
written, and parts may not apply any more. This document is no longer
maintained since an official API for customizing the Django admin interface
is in development.
Django's dynamic admin interface gives you a fully-functional admin for free
with no hand-coding required. The dynamic admin is designed to be
production-ready, not just a starting point, so you can use it as-is on a real
site. While the underlying format of the admin pages is built in to Django, you
can customize the look and feel by editing the admin stylesheet and images.
Here's a quick and dirty overview some of the main styles and classes used in
the Django admin CSS.
Modules
=======
The ``.module`` class is a basic building block for grouping content in the
admin. It's generally applied to a ``div`` or a ``fieldset``. It wraps the content
group in a box and applies certain styles to the elements within. An ``h2``
within a ``div.module`` will align to the top of the ``div`` as a header for the
whole group.
.. image:: _images/module.png
:alt: Example use of module class on admin homepage
Column Types
============
.. note::
All admin pages (except the dashboard) are fluid-width. All fixed-width
classes from previous Django versions have been removed.
The base template for each admin page has a block that defines the column
structure for the page. This sets a class on the page content area
(``div#content``) so everything on the page knows how wide it should be. There
are three column types available.
colM
This is the default column setting for all pages. The "M" stands for "main".
Assumes that all content on the page is in one main column
(``div#content-main``).
colMS
This is for pages with one main column and a sidebar on the right. The "S"
stands for "sidebar". Assumes that main content is in ``div#content-main``
and sidebar content is in ``div#content-related``. This is used on the main
admin page.
colSM
Same as above, with the sidebar on the left. The source order of the columns
doesn't matter.
For instance, you could stick this in a template to make a two-column page with
the sidebar on the right:
.. code-block:: html+django
{% block coltype %}colMS{% endblock %}
Text Styles
===========
Font Sizes
----------
Most HTML elements (headers, lists, etc.) have base font sizes in the stylesheet
based on context. There are three classes are available for forcing text to a
certain size in any context.
small
11px
tiny
10px
mini
9px (use sparingly)
Font Styles and Alignment
-------------------------
There are also a few styles for styling text.
.quiet
Sets font color to light gray. Good for side notes in instructions. Combine
with ``.small`` or ``.tiny`` for sheer excitement.
.help
This is a custom class for blocks of inline help text explaining the
function of form elements. It makes text smaller and gray, and when applied
to ``p`` elements within ``.form-row`` elements (see Form Styles below),
it will offset the text to align with the form field. Use this for help
text, instead of ``small quiet``. It works on other elements, but try to
put the class on a ``p`` whenever you can.
.align-left
It aligns the text left. Only works on block elements containing inline
elements.
.align-right
Are you paying attention?
.nowrap
Keeps text and inline objects from wrapping. Comes in handy for table
headers you want to stay on one line.
Floats and Clears
-----------------
float-left
floats left
float-right
floats right
clear
clears all
Object Tools
============
Certain actions which apply directly to an object are used in form and
changelist pages. These appear in a "toolbar" row above the form or changelist,
to the right of the page. The tools are wrapped in a ``ul`` with the class
``object-tools``. There are two custom tool types which can be defined with an
additional class on the ``a`` for that tool. These are ``.addlink`` and
``.viewsitelink``.
Example from a changelist page:
.. code-block:: html+django
<ul class="object-tools">
<li><a href="/stories/add/" class="addlink">Add redirect</a></li>
</ul>
.. image:: _images/objecttools_01.png
:alt: Object tools on a changelist page
and from a form page:
.. code-block:: html+django
<ul class="object-tools">
<li><a href="/history/303/152383/">History</a></li>
<li><a href="/r/303/152383/" class="viewsitelink">View on site</a></li>
</ul>
.. image:: _images/objecttools_02.png
:alt: Object tools on a form page
Form Styles
===========
Fieldsets
---------
Admin forms are broken up into groups by ``fieldset`` elements. Each form fieldset
should have a class ``.module``. Each fieldset should have a header ``h2`` within the
fieldset at the top (except the first group in the form, and in some cases where the
group of fields doesn't have a logical label).
Each fieldset can also take extra classes in addition to ``.module`` to apply
appropriate formatting to the group of fields.
.aligned
This will align the labels and inputs side by side on the same line.
.wide
Used in combination with ``.aligned`` to widen the space available for the
labels.
Form Rows
---------
Each row of the form (within the ``fieldset``) should be enclosed in a ``div``
with class ``form-row``. If the field in the row is required, a class of
``required`` should also be added to the ``div.form-row``.
.. image:: _images/formrow.png
:alt: Example use of form-row class
Labels
------
Form labels should always precede the field, except in the case
of checkboxes and radio buttons, where the ``input`` should come first. Any
explanation or help text should follow the ``label`` in a ``p`` with class
``.help``.

View File

@ -1,12 +0,0 @@
Deprecated/obsolete documentation
=================================
These documents cover features that have been deprecated or that have been
replaced in newer versions of Django. They're preserved here for folks using old
versions of Django or those still using deprecated APIs. No new code based on
these APIs should be written.
.. toctree::
:maxdepth: 1
admin-css

View File

@ -8,6 +8,11 @@ themselves or inherited from. They may not provide all the capabilities
required for projects, in which case there are Mixins and Generic class-based
views.
Many of Django's built-in class-based views inherit from other class-based
views or various mixins. Because this inheritence chain is very important, the
ancestor classes are documented under the section title of **Ancestors (MRO)**.
MRO is an acronym for Method Resolution Order.
View
----
@ -20,6 +25,7 @@ View
1. :meth:`dispatch()`
2. :meth:`http_method_not_allowed()`
3. :meth:`options()`
**Example views.py**::
@ -41,8 +47,20 @@ View
url(r'^mine/$', MyView.as_view(), name='my-view'),
)
**Attributes**
.. attribute:: http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace']
The default list of HTTP method names that this view will accept.
**Methods**
.. classmethod:: as_view(**initkwargs)
Returns a callable view that takes a request and returns a response::
response = MyView.as_view()(request)
.. method:: dispatch(request, *args, **kwargs)
The ``view`` part of the view -- the method that accepts a ``request``
@ -53,6 +71,11 @@ View
delegated to :meth:`~View.get()`, a ``POST`` to :meth:`~View.post()`,
and so on.
By default, a ``HEAD`` request will be delegated to :meth:`~View.get()`.
If you need to handle ``HEAD`` requests in a different way than ``GET``,
you can override the :meth:`~View.head()` method. See
:ref:`supporting-other-http-methods` for an example.
The default implementation also sets ``request``, ``args`` and
``kwargs`` as instance variables, so any method on the view can know
the full details of the request that was made to invoke the view.
@ -62,14 +85,13 @@ View
If the view was called with a HTTP method it doesn't support, this
method is called instead.
The default implementation returns ``HttpResponseNotAllowed`` with list
of allowed methods in plain text.
The default implementation returns ``HttpResponseNotAllowed`` with a
list of allowed methods in plain text.
.. note::
.. method:: options(request, *args, **kwargs)
Documentation on class-based views is a work in progress. As yet, only the
methods defined directly on the class are documented here, not methods
defined on superclasses.
Handles responding to requests for the OPTIONS HTTP verb. Returns a
list of the allowed HTTP method names for the view.
TemplateView
------------
@ -81,6 +103,8 @@ TemplateView
**Ancestors (MRO)**
This view inherits methods and attributes from the following views:
* :class:`django.views.generic.base.TemplateView`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.base.View`
@ -116,28 +140,11 @@ TemplateView
url(r'^$', HomePageView.as_view(), name='home'),
)
**Methods and Attributes**
.. attribute:: template_name
The full name of a template to use.
.. method:: get_context_data(**kwargs)
Return a context data dictionary consisting of the contents of
``kwargs`` stored in the context variable ``params``.
**Context**
* ``params``: The dictionary of keyword arguments captured from the URL
pattern that served the view.
.. note::
Documentation on class-based views is a work in progress. As yet, only the
methods defined directly on the class are documented here, not methods
defined on superclasses.
RedirectView
------------
@ -156,6 +163,8 @@ RedirectView
**Ancestors (MRO)**
This view inherits methods and attributes from the following view:
* :class:`django.views.generic.base.View`
**Method Flowchart**
@ -194,7 +203,7 @@ RedirectView
url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
)
**Methods and Attributes**
**Attributes**
.. attribute:: url
@ -215,6 +224,8 @@ RedirectView
then the query string is discarded. By default, ``query_string`` is
``False``.
**Methods**
.. method:: get_redirect_url(**kwargs)
Constructs the target URL for redirection.
@ -225,9 +236,3 @@ RedirectView
:attr:`~RedirectView.query_string`. Subclasses may implement any
behavior they wish, as long as the method returns a redirect-ready URL
string.
.. note::
Documentation on class-based views is a work in progress. As yet, only the
methods defined directly on the class are documented here, not methods
defined on superclasses.

View File

@ -2,13 +2,15 @@
Generic date views
==================
Date-based generic views (in the module :mod:`django.views.generic.dates`)
are views for displaying drilldown pages for date-based data.
.. module:: django.views.generic.dates
Date-based generic views, provided in :mod:`django.views.generic.dates`, are
views for displaying drilldown pages for date-based data.
ArchiveIndexView
----------------
.. class:: django.views.generic.dates.ArchiveIndexView
.. class:: ArchiveIndexView
A top-level index page showing the "latest" objects, by date. Objects with
a date in the *future* are not included unless you set ``allow_future`` to
@ -36,7 +38,7 @@ ArchiveIndexView
YearArchiveView
---------------
.. class:: django.views.generic.dates.YearArchiveView
.. class:: YearArchiveView
A yearly archive page showing all available months in a given year. Objects
with a date in the *future* are not displayed unless you set
@ -58,13 +60,15 @@ YearArchiveView
A boolean specifying whether to retrieve the full list of objects for
this year and pass those to the template. If ``True``, the list of
objects will be made available to the context. By default, this is
objects will be made available to the context. If ``False``, the
``None`` queryset will be used as the object list. By default, this is
``False``.
.. method:: get_make_object_list()
Determine if an object list will be returned as part of the context. If
``False``, the ``None`` queryset will be used as the object list.
Determine if an object list will be returned as part of the context.
Returns :attr:`~YearArchiveView.make_object_list` by default.
**Context**
@ -80,16 +84,18 @@ YearArchiveView
:class:`datetime.datetime<python:datetime.datetime>` objects, in
ascending order.
* ``year``: A :class:`datetime.date<python:datetime.date>` object
* ``year``: A :class:`~datetime.date` object
representing the given year.
* ``next_year``: A :class:`datetime.date<python:datetime.date>` object
representing the first day of the next year. If the next year is in the
future, this will be ``None``.
* ``next_year``: A :class:`~datetime.date` object
representing the first day of the next year, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
* ``previous_year``: A :class:`datetime.date<python:datetime.date>` object
representing the first day of the previous year. Unlike ``next_year``,
this will never be ``None``.
* ``previous_year``: A :class:`~datetime.date` object
representing the first day of the previous year, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
**Notes**
@ -98,7 +104,7 @@ YearArchiveView
MonthArchiveView
----------------
.. class:: django.views.generic.dates.MonthArchiveView
.. class:: MonthArchiveView
A monthly archive page showing all objects in a given month. Objects with a
date in the *future* are not displayed unless you set ``allow_future`` to
@ -131,16 +137,18 @@ MonthArchiveView
:class:`datetime.datetime<python:datetime.datetime>` objects, in
ascending order.
* ``month``: A :class:`datetime.date<python:datetime.date>` object
* ``month``: A :class:`~datetime.date` object
representing the given month.
* ``next_month``: A :class:`datetime.date<python:datetime.date>` object
representing the first day of the next month. If the next month is in the
future, this will be ``None``.
* ``next_month``: A :class:`~datetime.date` object
representing the first day of the next month, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
* ``previous_month``: A :class:`datetime.date<python:datetime.date>` object
representing the first day of the previous month. Unlike ``next_month``,
this will never be ``None``.
* ``previous_month``: A :class:`~datetime.date` object
representing the first day of the previous month, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
**Notes**
@ -149,7 +157,7 @@ MonthArchiveView
WeekArchiveView
---------------
.. class:: django.views.generic.dates.WeekArchiveView
.. class:: WeekArchiveView
A weekly archive page showing all objects in a given week. Objects with a
date in the *future* are not displayed unless you set ``allow_future`` to
@ -175,16 +183,18 @@ WeekArchiveView
:class:`~django.views.generic.dates.BaseDateListView`), the template's
context will be:
* ``week``: A :class:`datetime.date<python:datetime.date>` object
* ``week``: A :class:`~datetime.date` object
representing the first day of the given week.
* ``next_week``: A :class:`datetime.date<python:datetime.date>` object
representing the first day of the next week. If the next week is in the
future, this will be ``None``.
* ``next_week``: A :class:`~datetime.date` object
representing the first day of the next week, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
* ``previous_week``: A :class:`datetime.date<python:datetime.date>` object
representing the first day of the previous week. Unlike ``next_week``,
this will never be ``None``.
* ``previous_week``: A :class:`~datetime.date` object
representing the first day of the previous week, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
**Notes**
@ -193,7 +203,7 @@ WeekArchiveView
DayArchiveView
--------------
.. class:: django.views.generic.dates.DayArchiveView
.. class:: DayArchiveView
A day archive page showing all objects in a given day. Days in the future
throw a 404 error, regardless of whether any objects exist for future days,
@ -220,24 +230,28 @@ DayArchiveView
:class:`~django.views.generic.dates.BaseDateListView`), the template's
context will be:
* ``day``: A :class:`datetime.date<python:datetime.date>` object
* ``day``: A :class:`~datetime.date` object
representing the given day.
* ``next_day``: A :class:`datetime.date<python:datetime.date>` object
representing the next day. If the next day is in the future, this will be
``None``.
* ``next_day``: A :class:`~datetime.date` object
representing the next day, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
* ``previous_day``: A :class:`datetime.date<python:datetime.date>` object
representing the previous day. Unlike ``next_day``, this will never be
``None``.
* ``previous_day``: A :class:`~datetime.date` object
representing the previous day, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
* ``next_month``: A :class:`datetime.date<python:datetime.date>` object
representing the first day of the next month. If the next month is in the
future, this will be ``None``.
* ``next_month``: A :class:`~datetime.date` object
representing the first day of the next month, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
* ``previous_month``: A :class:`datetime.date<python:datetime.date>` object
representing the first day of the previous month. Unlike ``next_month``,
this will never be ``None``.
* ``previous_month``: A :class:`~datetime.date` object
representing the first day of the previous month, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
**Notes**
@ -246,7 +260,7 @@ DayArchiveView
TodayArchiveView
----------------
.. class:: django.views.generic.dates.TodayArchiveView
.. class:: TodayArchiveView
A day archive page showing all objects for *today*. This is exactly the
same as :class:`django.views.generic.dates.DayArchiveView`, except today's
@ -271,7 +285,7 @@ TodayArchiveView
DateDetailView
--------------
.. class:: django.views.generic.dates.DateDetailView
.. class:: DateDetailView
A page representing an individual object. If the object has a date value in
the future, the view will throw a 404 error by default, unless you set
@ -293,6 +307,22 @@ DateDetailView
.. note::
All of the generic views listed above have matching Base* views that only
differ in that the they do not include the
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
All of the generic views listed above have matching ``Base`` views that
only differ in that the they do not include the
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`:
.. class:: BaseArchiveIndexView
.. class:: BaseYearArchiveView
.. class:: BaseMonthArchiveView
.. class:: BaseWeekArchiveView
.. class:: BaseDayArchiveView
.. class:: BaseTodayArchiveView
.. class:: BaseDateDetailView

View File

@ -15,6 +15,8 @@ DetailView
**Ancestors (MRO)**
This view inherits methods and attributes from the following views:
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.detail.BaseDetailView`
@ -71,7 +73,9 @@ ListView
objects (usually, but not necessarily a queryset) that the view is
operating upon.
**Mixins**
**Ancestors (MRO)**
This view inherits methods and attributes from the following views:
* :class:`django.views.generic.list.ListView`
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
@ -90,3 +94,54 @@ ListView
6. :meth:`get_context_data()`
7. :meth:`get()`
8. :meth:`render_to_response()`
**Example views.py**::
from django.views.generic.list import ListView
from django.utils import timezone
from articles.models import Article
class ArticleListView(ListView):
model = Article
def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
context['now'] = timezone.now()
return context
**Example urls.py**::
from django.conf.urls import patterns, url
from article.views import ArticleListView
urlpatterns = patterns('',
url(r'^$', ArticleListView.as_view(), name='article-list'),
)
.. class:: django.views.generic.list.BaseListView
A base view for displaying a list of objects. It is not intended to be used
directly, but rather as a parent class of the
:class:`django.views.generic.list.ListView` or other views representing
lists of objects.
**Ancestors (MRO)**
This view inherits methods and attributes from the following views:
* :class:`django.views.generic.list.MultipleObjectMixin`
* :class:`django.views.generic.base.View`
**Methods**
.. method:: get(request, *args, **kwargs)
Adds :attr:`object_list` to the context. If
:attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty`
is True then display an empty list. If
:attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` is
False then raise a 404 error.

View File

@ -23,7 +23,7 @@ it is safe to store state variables on the instance (i.e., ``self.foo = 3`` is
a thread-safe operation).
A class-based view is deployed into a URL pattern using the
:meth:`~View.as_view()` classmethod::
:meth:`~django.views.generic.base.View.as_view()` classmethod::
urlpatterns = patterns('',
(r'^view/$', MyView.as_view(size=42)),
@ -37,9 +37,10 @@ A class-based view is deployed into a URL pattern using the
is modified, the actions of one user visiting your view could have an
effect on subsequent users visiting the same view.
Any argument passed into :meth:`~View.as_view()` will be assigned onto the
instance that is used to service a request. Using the previous example,
this means that every request on ``MyView`` is able to use ``self.size``.
Any argument passed into :meth:`~django.views.generic.base.View.as_view()` will
be assigned onto the instance that is used to service a request. Using the
previous example, this means that every request on ``MyView`` is able to use
``self.size``.
Base vs Generic views
---------------------

View File

@ -2,11 +2,12 @@
Date-based mixins
=================
.. currentmodule:: django.views.generic.dates
YearMixin
---------
.. class:: django.views.generic.dates.YearMixin
.. class:: YearMixin
A mixin that can be used to retrieve and provide parsing information for a
year component of a date.
@ -20,29 +21,45 @@ YearMixin
.. attribute:: year
**Optional** The value for the year (as a string). By default, set to
**Optional** The value for the year, as a string. By default, set to
``None``, which means the year will be determined using other means.
.. method:: get_year_format()
Returns the :func:`~time.strftime` format to use when parsing the year. Returns
:attr:`YearMixin.year_format` by default.
Returns the :func:`~time.strftime` format to use when parsing the
year. Returns :attr:`~YearMixin.year_format` by default.
.. method:: get_year()
Returns the year for which this view will display data. Tries the
following sources, in order:
Returns the year for which this view will display data, as a string.
Tries the following sources, in order:
* The value of the :attr:`YearMixin.year` attribute.
* The value of the `year` argument captured in the URL pattern
* The value of the `year` argument captured in the URL pattern.
* The value of the `year` GET query argument.
Raises a 404 if no valid year specification can be found.
.. method:: get_next_year(date)
Returns a date object containing the first day of the year after the
date provided. This function can also return ``None`` or raise an
:class:`~django.http.Http404` exception, depending on the values of
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
.. method:: get_previous_year(date)
Returns a date object containing the first day of the year before the
date provided. This function can also return ``None`` or raise an
:class:`~django.http.Http404` exception, depending on the values of
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
MonthMixin
----------
.. class:: django.views.generic.dates.MonthMixin
.. class:: MonthMixin
A mixin that can be used to retrieve and provide parsing information for a
month component of a date.
@ -51,26 +68,26 @@ MonthMixin
.. attribute:: month_format
The :func:`~time.strftime` format to use when parsing the month. By default, this is
``'%b'``.
The :func:`~time.strftime` format to use when parsing the month. By
default, this is ``'%b'``.
.. attribute:: month
**Optional** The value for the month (as a string). By default, set to
**Optional** The value for the month, as a string. By default, set to
``None``, which means the month will be determined using other means.
.. method:: get_month_format()
Returns the :func:`~time.strftime` format to use when parsing the month. Returns
:attr:`MonthMixin.month_format` by default.
Returns the :func:`~time.strftime` format to use when parsing the
month. Returns :attr:`~MonthMixin.month_format` by default.
.. method:: get_month()
Returns the month for which this view will display data. Tries the
following sources, in order:
Returns the month for which this view will display data, as a string.
Tries the following sources, in order:
* The value of the :attr:`MonthMixin.month` attribute.
* The value of the `month` argument captured in the URL pattern
* The value of the `month` argument captured in the URL pattern.
* The value of the `month` GET query argument.
Raises a 404 if no valid month specification can be found.
@ -78,20 +95,23 @@ MonthMixin
.. method:: get_next_month(date)
Returns a date object containing the first day of the month after the
date provided. Returns ``None`` if mixed with a view that sets
``allow_future = False``, and the next month is in the future. If
``allow_empty = False``, returns the next month that contains data.
date provided. This function can also return ``None`` or raise an
:class:`~django.http.Http404` exception, depending on the values of
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
.. method:: get_prev_month(date)
Returns a date object containing the first day of the month before the
date provided. If ``allow_empty = False``, returns the previous month
that contained data.
date provided. This function can also return ``None`` or raise an
:class:`~django.http.Http404` exception, depending on the values of
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
DayMixin
--------
.. class:: django.views.generic.dates.DayMixin
.. class:: DayMixin
A mixin that can be used to retrieve and provide parsing information for a
day component of a date.
@ -100,46 +120,50 @@ DayMixin
.. attribute:: day_format
The :func:`~time.strftime` format to use when parsing the day. By default, this is
``'%d'``.
The :func:`~time.strftime` format to use when parsing the day. By
default, this is ``'%d'``.
.. attribute:: day
**Optional** The value for the day (as a string). By default, set to
**Optional** The value for the day, as a string. By default, set to
``None``, which means the day will be determined using other means.
.. method:: get_day_format()
Returns the :func:`~time.strftime` format to use when parsing the day. Returns
:attr:`DayMixin.day_format` by default.
Returns the :func:`~time.strftime` format to use when parsing the day.
Returns :attr:`~DayMixin.day_format` by default.
.. method:: get_day()
Returns the day for which this view will display data. Tries the
following sources, in order:
Returns the day for which this view will display data, as a string.
Tries the following sources, in order:
* The value of the :attr:`DayMixin.day` attribute.
* The value of the `day` argument captured in the URL pattern
* The value of the `day` argument captured in the URL pattern.
* The value of the `day` GET query argument.
Raises a 404 if no valid day specification can be found.
.. method:: get_next_day(date)
Returns a date object containing the next day after the date provided.
Returns ``None`` if mixed with a view that sets ``allow_future = False``,
and the next day is in the future. If ``allow_empty = False``, returns
the next day that contains data.
Returns a date object containing the next valid day after the date
provided. This function can also return ``None`` or raise an
:class:`~django.http.Http404` exception, depending on the values of
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
.. method:: get_prev_day(date)
Returns a date object containing the previous day. If
``allow_empty = False``, returns the previous day that contained data.
Returns a date object containing the previous valid day. This function
can also return ``None`` or raise an :class:`~django.http.Http404`
exception, depending on the values of
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
WeekMixin
---------
.. class:: django.views.generic.dates.WeekMixin
.. class:: WeekMixin
A mixin that can be used to retrieve and provide parsing information for a
week component of a date.
@ -148,23 +172,24 @@ WeekMixin
.. attribute:: week_format
The :func:`~time.strftime` format to use when parsing the week. By default, this is
``'%U'``.
The :func:`~time.strftime` format to use when parsing the week. By
default, this is ``'%U'``, which means the week starts on Sunday. Set
it to ``'%W'`` if your week starts on Monday.
.. attribute:: week
**Optional** The value for the week (as a string). By default, set to
**Optional** The value for the week, as a string. By default, set to
``None``, which means the week will be determined using other means.
.. method:: get_week_format()
Returns the :func:`~time.strftime` format to use when parsing the week. Returns
:attr:`WeekMixin.week_format` by default.
Returns the :func:`~time.strftime` format to use when parsing the
week. Returns :attr:`~WeekMixin.week_format` by default.
.. method:: get_week()
Returns the week for which this view will display data. Tries the
following sources, in order:
Returns the week for which this view will display data, as a string.
Tries the following sources, in order:
* The value of the :attr:`WeekMixin.week` attribute.
* The value of the `week` argument captured in the URL pattern
@ -172,11 +197,26 @@ WeekMixin
Raises a 404 if no valid week specification can be found.
.. method:: get_next_week(date)
Returns a date object containing the first day of the week after the
date provided. This function can also return ``None`` or raise an
:class:`~django.http.Http404` exception, depending on the values of
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
.. method:: get_prev_week(date)
Returns a date object containing the first day of the week before the
date provided. This function can also return ``None`` or raise an
:class:`~django.http.Http404` exception, depending on the values of
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
DateMixin
---------
.. class:: django.views.generic.dates.DateMixin
.. class:: DateMixin
A mixin class providing common behavior for all date-based views.
@ -186,7 +226,7 @@ DateMixin
The name of the ``DateField`` or ``DateTimeField`` in the
``QuerySet``'s model that the date-based archive should use to
determine the objects on the page.
determine the list of objects to display on the page.
When :doc:`time zone support </topics/i18n/timezones>` is enabled and
``date_field`` is a ``DateTimeField``, dates are assumed to be in the
@ -210,26 +250,26 @@ DateMixin
.. method:: get_date_field()
Returns the name of the field that contains the date data that this
view will operate on. Returns :attr:`DateMixin.date_field` by default.
view will operate on. Returns :attr:`~DateMixin.date_field` by default.
.. method:: get_allow_future()
Determine whether to include "future" objects on this page, where
"future" means objects in which the field specified in ``date_field``
is greater than the current date/time. Returns
:attr:`DateMixin.allow_future` by default.
:attr:`~DateMixin.allow_future` by default.
BaseDateListView
----------------
.. class:: django.views.generic.dates.BaseDateListView
.. class:: BaseDateListView
A base class that provides common behavior for all date-based views. There
won't normally be a reason to instantiate
:class:`~django.views.generic.dates.BaseDateListView`; instantiate one of
the subclasses instead.
While this view (and it's subclasses) are executing, ``self.object_list``
While this view (and its subclasses) are executing, ``self.object_list``
will contain the list of objects that the view is operating upon, and
``self.date_list`` will contain the list of dates for which data is
available.
@ -245,10 +285,18 @@ BaseDateListView
A boolean specifying whether to display the page if no objects are
available. If this is ``True`` and no objects are available, the view
will display an empty page instead of raising a 404. By default, this
is ``False``.
will display an empty page instead of raising a 404.
.. method:: get_dated_items():
This is identical to :attr:`MultipleObjectMixin.allow_empty`, except
for the default value, which is ``False``.
.. attribute:: date_list_period
**Optional** A string defining the aggregation period for
``date_list``. It must be one of ``'year'`` (default), ``'month'``, or
``'day'``.
.. method:: get_dated_items()
Returns a 3-tuple containing (``date_list``, ``object_list``,
``extra_context``).
@ -265,10 +313,17 @@ BaseDateListView
``lookup``. Enforces any restrictions on the queryset, such as
``allow_empty`` and ``allow_future``.
.. method:: get_date_list(queryset, date_type)
.. method:: get_date_list_period()
Returns the list of dates of type ``date_type`` for which
``queryset`` contains entries. For example, ``get_date_list(qs,
'year')`` will return the list of years for which ``qs`` has entries.
See :meth:`~django.db.models.query.QuerySet.dates()` for the
ways that the ``date_type`` argument can be used.
Returns the aggregation period for ``date_list``. Returns
:attr:`~BaseDateListView.date_list_period` by default.
.. method:: get_date_list(queryset, date_type=None)
Returns the list of dates of type ``date_type`` for which ``queryset``
contains entries. For example, ``get_date_list(qs, 'year')`` will
return the list of years for which ``qs`` has entries. If
``date_type`` isn't provided, the result of
:meth:`BaseDateListView.get_date_list_period` is used. See
:meth:`~django.db.models.query.QuerySet.dates()` for the ways that the
``date_type`` argument can be used.

View File

@ -86,7 +86,8 @@ MultipleObjectMixin
.. method:: get_queryset()
Returns the queryset that represents the data this view will display.
Get the list of items for this view. This must be an iterable and may
be a queryset (in which queryset-specific behavior will be enabled).
.. method:: paginate_queryset(queryset, page_size)

View File

@ -9,16 +9,17 @@ ContextMixin
.. versionadded:: 1.5
**classpath**
``django.views.generic.base.ContextMixin``
**Methods**
.. method:: get_context_data(**kwargs)
Returns a dictionary representing the template context. The keyword
arguments provided will make up the returned context.
arguments provided will make up the returned context. Example usage::
def get_context_data(self, **kwargs):
context = super(RandomNumberView, self).get_context_data(**kwargs)
context['number'] = random.randrange(1, 100)
return context
The template context of all class-based generic views include a
``view`` variable that points to the ``View`` instance.
@ -42,7 +43,13 @@ TemplateResponseMixin
suitable context. The template to use is configurable and can be
further customized by subclasses.
**Methods and Attributes**
**Attributes**
.. attribute:: template_name
The full name of a template to use as defined by a string. Not defining
a template_name will raise a
:class:`django.core.exceptions.ImproperlyConfigured` exception.
.. attribute:: response_class
@ -57,12 +64,14 @@ TemplateResponseMixin
instantiation, create a ``TemplateResponse`` subclass and assign it to
``response_class``.
**Methods**
.. method:: render_to_response(context, **response_kwargs)
Returns a ``self.response_class`` instance.
If any keyword arguments are provided, they will be
passed to the constructor of the response class.
If any keyword arguments are provided, they will be passed to the
constructor of the response class.
Calls :meth:`~TemplateResponseMixin.get_template_names()` to obtain the
list of template names that will be searched looking for an existent

View File

@ -36,7 +36,7 @@ following model, which would represent entries in a Weblog::
class Entry(models.Model):
title = models.CharField(maxlength=250)
body = models.TextField()
pub_date = models.DateTimeField()
pub_date = models.DateField()
enable_comments = models.BooleanField()
Now, suppose that we want the following steps to be applied whenever a

View File

@ -187,6 +187,14 @@ The ``ContentTypeManager``
probably won't ever need to call this method yourself; Django will call
it automatically when it's needed.
.. method:: get_for_id(id)
Lookup a :class:`~django.contrib.contenttypes.models.ContentType` by ID.
Since this method uses the same shared cache as
:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model`,
it's preferred to use this method over the usual
``ContentType.objects.get(pk=id)``
.. method:: get_for_model(model[, for_concrete_model=True])
Takes either a model class or an instance of a model, and returns the

View File

@ -155,7 +155,8 @@ or the
:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names()`
method, which are documented in the
:class:`~django.views.generic.base.TemplateResponseMixin` documentation. The
latter one allows you to use a different template for each form.
latter one allows you to use a different template for each form (:ref:`see the
example below <wizard-template-for-each-form>`).
This template expects a ``wizard`` object that has various items attached to
it:
@ -238,6 +239,65 @@ wizard's :meth:`as_view` method takes a list of your
(r'^contact/$', ContactWizard.as_view([ContactForm1, ContactForm2])),
)
.. _wizard-template-for-each-form:
Using a different template for each form
----------------------------------------
As mentioned above, you may specify a different template for each form.
Consider an example using a form wizard to implement a multi-step checkout
process for an online store. In the first step, the user specifies a billing
and shipping address. In the second step, the user chooses payment type. If
they chose to pay by credit card, they will enter credit card information in
the next step. In the final step, they will confirm the purchase.
Here's what the view code might look like::
from django.http import HttpResponseRedirect
from django.contrib.formtools.wizard.views import SessionWizardView
FORMS = [("address", myapp.forms.AddressForm),
("paytype", myapp.forms.PaymentChoiceForm),
("cc", myapp.forms.CreditCardForm),
("confirmation", myapp.forms.OrderForm)]
TEMPLATES = {"address": "checkout/billingaddress.html",
"paytype": "checkout/paymentmethod.html",
"cc": "checkout/creditcard.html",
"confirmation": "checkout/confirmation.html"}
def pay_by_credit_card(wizard):
"""Return true if user opts to pay by credit card"""
# Get cleaned data from payment step
cleaned_data = wizard.get_cleaned_data_for_step('paytype') or {'method': 'none'}
# Return true if the user selected credit card
return cleaned_data['method'] == 'cc'
class OrderWizard(SessionWizardView):
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def done(self, form_list, **kwargs):
do_something_with_the_form_data(form_list)
return HttpResponseRedirect('/page-to-redirect-to-when-done/')
...
The ``urls.py`` file would contain something like::
urlpatterns = patterns('',
(r'^checkout/$', OrderWizard.as_view(FORMS, condition_dict={'cc': pay_by_credit_card})),
)
Note that the ``OrderWizard`` object is initialized with a list of pairs.
The first element in the pair is a string that corresponds to the name of the
step and the second is the form class.
In this example, the
:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names()`
method returns a list containing a single template, which is selected based on
the name of the current step.
.. _wizardview-advanced-methods:
Advanced ``WizardView`` methods

View File

@ -959,15 +959,15 @@ Ubuntu & Debian GNU/Linux
Ubuntu
^^^^^^
11.10
~~~~~
11.10 through 12.04
~~~~~~~~~~~~~~~~~~~
In Ubuntu 11.10, PostgreSQL was upgraded to 9.1. The installation commands are:
In Ubuntu 11.10, PostgreSQL was upgraded to 9.1. The installation command is:
.. code-block:: bash
$ sudo apt-get install binutils gdal-bin libproj-dev postgresql-9.1-postgis \
postgresql-server-dev-9.1 python-psycopg2
$ sudo apt-get install binutils gdal-bin libproj-dev \
postgresql-9.1-postgis postgresql-server-dev-9.1 python-psycopg2
.. _ubuntu10:
@ -976,7 +976,7 @@ In Ubuntu 11.10, PostgreSQL was upgraded to 9.1. The installation commands are:
In Ubuntu 10.04, PostgreSQL was upgraded to 8.4 and GDAL was upgraded to 1.6.
Ubuntu 10.04 uses PostGIS 1.4, while Ubuntu 10.10 uses PostGIS 1.5 (with
geography support). The installation commands are:
geography support). The installation command is:
.. code-block:: bash

View File

@ -674,8 +674,8 @@ __ http://spatialreference.org/ref/epsg/32140/
.. admonition:: Raw queries
When using :doc:`raw queries </topics/db/sql>`, you should generally wrap
your geometry fields with the ``asText()`` SQL function so as the field
value will be recognized by GEOS::
your geometry fields with the ``asText()`` SQL function (or ``ST_AsText``
for PostGIS) so as the field value will be recognized by GEOS::
City.objects.raw('SELECT id, name, asText(point) from myapp_city')

View File

@ -5,6 +5,9 @@ django.contrib.markup
.. module:: django.contrib.markup
:synopsis: A collection of template filters that implement common markup languages.
.. deprecated:: 1.5
This module has been deprecated.
Django provides template filters that implement the following markup
languages:

View File

@ -5,14 +5,16 @@ The messages framework
.. module:: django.contrib.messages
:synopsis: Provides cookie- and session-based temporary message storage.
Quite commonly in web applications, you may need to display a one-time
notification message (also know as "flash message") to the user after
processing a form or some other types of user input. For this, Django provides
full support for cookie- and session-based messaging, for both anonymous and
authenticated users. The messages framework allows you to temporarily store
messages in one request and retrieve them for display in a subsequent request
(usually the next one). Every message is tagged with a specific ``level`` that
determines its priority (e.g., ``info``, ``warning``, or ``error``).
Quite commonly in web applications, you need to display a one-time
notification message (also known as "flash message") to the user after
processing a form or some other types of user input.
For this, Django provides full support for cookie- and session-based
messaging, for both anonymous and authenticated users. The messages framework
allows you to temporarily store messages in one request and retrieve them for
display in a subsequent request (usually the next one). Every message is
tagged with a specific ``level`` that determines its priority (e.g., ``info``,
``warning``, or ``error``).
Enabling messages
=================
@ -20,32 +22,27 @@ Enabling messages
Messages are implemented through a :doc:`middleware </ref/middleware>`
class and corresponding :doc:`context processor </ref/templates/api>`.
To enable message functionality, do the following:
The default ``settings.py`` created by ``django-admin.py startproject``
already contains all the settings required to enable message functionality:
* Edit the :setting:`MIDDLEWARE_CLASSES` setting and make sure
it contains ``'django.contrib.messages.middleware.MessageMiddleware'``.
* ``'django.contrib.messages'`` is in :setting:`INSTALLED_APPS`.
If you are using a :ref:`storage backend <message-storage-backends>` that
relies on :doc:`sessions </topics/http/sessions>` (the default),
``'django.contrib.sessions.middleware.SessionMiddleware'`` must be
enabled and appear before ``MessageMiddleware`` in your
* :setting:`MIDDLEWARE_CLASSES` contains
``'django.contrib.sessions.middleware.SessionMiddleware'`` and
``'django.contrib.messages.middleware.MessageMiddleware'``.
The default :ref:`storage backend <message-storage-backends>` relies on
:doc:`sessions </topics/http/sessions>`. That's why ``SessionMiddleware``
must be enabled and appear before ``MessageMiddleware`` in
:setting:`MIDDLEWARE_CLASSES`.
* Edit the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting and make sure
it contains ``'django.contrib.messages.context_processors.messages'``.
* :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains
``'django.contrib.messages.context_processors.messages'``.
* Add ``'django.contrib.messages'`` to your :setting:`INSTALLED_APPS`
setting
The default ``settings.py`` created by ``django-admin.py startproject`` has
``MessageMiddleware`` activated and the ``django.contrib.messages`` app
installed. Also, the default value for :setting:`TEMPLATE_CONTEXT_PROCESSORS`
contains ``'django.contrib.messages.context_processors.messages'``.
If you don't want to use messages, you can remove the
``MessageMiddleware`` line from :setting:`MIDDLEWARE_CLASSES`, the ``messages``
context processor from :setting:`TEMPLATE_CONTEXT_PROCESSORS` and
``'django.contrib.messages'`` from your :setting:`INSTALLED_APPS`.
If you don't want to use messages, you can remove
``'django.contrib.messages'`` from your :setting:`INSTALLED_APPS`, the
``MessageMiddleware`` line from :setting:`MIDDLEWARE_CLASSES`, and the
``messages`` context processor from :setting:`TEMPLATE_CONTEXT_PROCESSORS`.
Configuring the message engine
==============================
@ -56,34 +53,35 @@ Storage backends
----------------
The messages framework can use different backends to store temporary messages.
If the default FallbackStorage isn't suitable to your needs, you can change
which backend is being used by adding a `MESSAGE_STORAGE`_ to your
settings, referencing the module and class of the storage class. For
example::
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
Django provides three built-in storage classes:
The value should be the full path of the desired storage class.
.. class:: django.contrib.messages.storage.session.SessionStorage
Three storage classes are available:
This class stores all messages inside of the request's session. Therefore
it requires Django's ``contrib.sessions`` application.
``'django.contrib.messages.storage.session.SessionStorage'``
This class stores all messages inside of the request's session. It
requires Django's ``contrib.sessions`` application.
.. class:: django.contrib.messages.storage.cookie.CookieStorage
``'django.contrib.messages.storage.cookie.CookieStorage'``
This class stores the message data in a cookie (signed with a secret hash
to prevent manipulation) to persist notifications across requests. Old
messages are dropped if the cookie data size would exceed 4096 bytes.
messages are dropped if the cookie data size would exceed 2048 bytes.
``'django.contrib.messages.storage.fallback.FallbackStorage'``
This is the default storage class.
.. class:: django.contrib.messages.storage.fallback.FallbackStorage
This class first uses CookieStorage for all messages, falling back to using
SessionStorage for the messages that could not fit in a single cookie.
This class first uses ``CookieStorage``, and falls back to using
``SessionStorage`` for the messages that could not fit in a single cookie.
It also requires Django's ``contrib.sessions`` application.
Since it is uses SessionStorage, it also requires Django's
``contrib.sessions`` application.
This behavior avoids writing to the session whenever possible. It should
provide the best performance in the general case.
:class:`~django.contrib.messages.storage.fallback.FallbackStorage` is the
default storage class. If it isn't suitable to your needs, you can select
another storage class by setting `MESSAGE_STORAGE`_ to its full import path,
for example::
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
To write your own storage class, subclass the ``BaseStorage`` class in
``django.contrib.messages.storage.base`` and implement the ``_get`` and
@ -97,8 +95,8 @@ to that of the Python logging module. Message levels allow you to group
messages by type so they can be filtered or displayed differently in views and
templates.
The built-in levels (which can be imported from ``django.contrib.messages``
directly) are:
The built-in levels, which can be imported from ``django.contrib.messages``
directly, are:
=========== ========
Constant Purpose

View File

@ -398,11 +398,21 @@ For each field, we describe the default widget used if you don't specify
If no ``input_formats`` argument is provided, the default input formats are::
'%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
'%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
'%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
'%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
'%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
'%Y-%m-%d', # '2006-10-25'
'%m/%d/%Y', # '10/25/2006'
'%m/%d/%y', # '10/25/06'
Additionally, if you specify :setting:`USE_L10N=False<USE_L10N>` in your settings, the
following will also be included in the default input formats::
'%b %m %d', # 'Oct 25 2006'
'%b %d, %Y', # 'Oct 25, 2006'
'%d %b %Y', # '25 Oct 2006'
'%d %b, %Y', # '25 Oct, 2006'
'%B %d %Y', # 'October 25 2006'
'%B %d, %Y', # 'October 25, 2006'
'%d %B %Y', # '25 October 2006'
'%d %B, %Y', # '25 October, 2006'
``DateTimeField``
~~~~~~~~~~~~~~~~~
@ -842,7 +852,7 @@ Slightly complex built-in ``Field`` classes
``MultiValueField``
~~~~~~~~~~~~~~~~~~~
.. class:: MultiValueField(**kwargs)
.. class:: MultiValueField(fields=(), **kwargs)
* Default widget: ``TextInput``
* Empty value: ``''`` (an empty string)
@ -851,22 +861,39 @@ Slightly complex built-in ``Field`` classes
as an argument to the ``MultiValueField``.
* Error message keys: ``required``, ``invalid``
This abstract field (must be subclassed) aggregates the logic of multiple
fields. Subclasses should not have to implement clean(). Instead, they must
implement compress(), which takes a list of valid values and returns a
"compressed" version of those values -- a single value. For example,
:class:`SplitDateTimeField` is a subclass which combines a time field and
a date field into a datetime object.
Aggregates the logic of multiple fields that together produce a single
value.
This field is abstract and must be subclassed. In contrast with the
single-value fields, subclasses of :class:`MultiValueField` must not
implement :meth:`~django.forms.Field.clean` but instead - implement
:meth:`~MultiValueField.compress`.
Takes one extra required argument:
.. attribute:: fields
A list of fields which are cleaned into a single field. Each value in
``clean`` is cleaned by the corresponding field in ``fields`` -- the first
value is cleaned by the first field, the second value is cleaned by
the second field, etc. Once all fields are cleaned, the list of clean
values is "compressed" into a single value.
A tuple of fields whose values are cleaned and subsequently combined
into a single value. Each value of the field is cleaned by the
corresponding field in ``fields`` -- the first value is cleaned by the
first field, the second value is cleaned by the second field, etc.
Once all fields are cleaned, the list of clean values is combined into
a single value by :meth:`~MultiValueField.compress`.
.. attribute:: MultiValueField.widget
Must be a subclass of :class:`django.forms.MultiWidget`.
Default value is :class:`~django.forms.widgets.TextInput`, which
probably is not very useful in this case.
.. method:: compress(data_list)
Takes a list of valid values and returns a "compressed" version of
those values -- in a single value. For example,
:class:`SplitDateTimeField` is a subclass which combines a time field
and a date field into a ``datetime`` object.
This method must be implemented in the subclasses.
``SplitDateTimeField``
~~~~~~~~~~~~~~~~~~~~~~

View File

@ -11,6 +11,16 @@ A widget is Django's representation of a HTML input element. The widget
handles the rendering of the HTML, and the extraction of data from a GET/POST
dictionary that corresponds to the widget.
.. tip::
Widgets should not be confused with the :doc:`form fields </ref/forms/fields>`.
Form fields deal with the logic of input validation and are used directly
in templates. Widgets deal with rendering of HTML form input elements on
the web page and extraction of raw submitted data. However, widgets do
need to be :ref:`assigned <widget-to-field>` to form fields.
.. _widget-to-field:
Specifying widgets
------------------
@ -95,15 +105,23 @@ choices are inherent to the model and not just the representational widget.
Customizing widget instances
----------------------------
When Django renders a widget as HTML, it only renders the bare minimum
HTML - Django doesn't add a class definition, or any other widget-specific
attributes. This means that all :class:`TextInput` widgets will appear the same
on your Web page.
When Django renders a widget as HTML, it only renders very minimal markup -
Django doesn't add class names, or any other widget-specific attributes. This
means, for example, that all :class:`TextInput` widgets will appear the same
on your Web pages.
If you want to make one widget look different to another, you need to
specify additional attributes for each widget. When you specify a
widget, you can provide a list of attributes that will be added to the
rendered HTML for the widget.
There are two ways to customize widgets: :ref:`per widget instance
<styling-widget-instances>` and :ref:`per widget class <styling-widget-classes>`.
.. _styling-widget-instances:
Styling widget instances
^^^^^^^^^^^^^^^^^^^^^^^^
If you want to make one widget instance look different from another, you will
need to specify additional attributes at the time when the widget object is
instantiated and assigned to a form field (and perhaps add some rules to your
CSS files).
For example, take the following simple form::
@ -126,10 +144,9 @@ provided for each widget will be rendered exactly the same::
On a real Web page, you probably don't want every widget to look the same. You
might want a larger input element for the comment, and you might want the
'name' widget to have some special CSS class. To do this, you use the
:attr:`Widget.attrs` argument when creating the widget:
For example::
'name' widget to have some special CSS class. It is also possible to specify
the 'type' attribute to take advantage of the new HTML5 input types. To do
this, you use the :attr:`Widget.attrs` argument when creating the widget::
class CommentForm(forms.Form):
name = forms.CharField(
@ -146,24 +163,41 @@ Django will then include the extra attributes in the rendered output:
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
.. _built-in widgets:
.. _styling-widget-classes:
Built-in widgets
----------------
Styling widget classes
^^^^^^^^^^^^^^^^^^^^^^
Django provides a representation of all the basic HTML widgets, plus some
commonly used groups of widgets:
With widgets, it is possible to add media (``css`` and ``javascript``)
and more deeply customize their appearance and behavior.
``Widget``
~~~~~~~~~~
In a nutshell, you will need to subclass the widget and either
:ref:`define a class "Media" <media-as-a-static-definition>` as a member of the
subclass, or :ref:`create a property "media" <dynamic-property>`, returning an
instance of that class.
.. class:: Widget
These methods involve somewhat advanced Python programming and are described in
detail in the :doc:`Form Media </topics/forms/media>` topic guide.
This abstract class cannot be rendered, but provides the basic attribute :attr:`~Widget.attrs`.
.. _base-widget-classes:
Base Widget classes
-------------------
Base widget classes :class:`Widget` and :class:`MultiWidget` are subclassed by
all the :ref:`built-in widgets <built-in widgets>` and may serve as a
foundation for custom widgets.
.. class:: Widget(attrs=None)
This abstract class cannot be rendered, but provides the basic attribute
:attr:`~Widget.attrs`. You may also implement or override the
:meth:`~Widget.render()` method on custom widgets.
.. attribute:: Widget.attrs
A dictionary containing HTML attributes to be set on the rendered widget.
A dictionary containing HTML attributes to be set on the rendered
widget.
.. code-block:: python
@ -171,6 +205,74 @@ commonly used groups of widgets:
>>> name.render('name', 'A name')
u'<input title="Your name" type="text" name="name" value="A name" size="10" />'
.. method:: render(name, value, attrs=None)
Returns HTML for the widget, as a Unicode string. This method must be
implemented by the subclass, otherwise ``NotImplementedError`` will be
raised.
The 'value' given is not guaranteed to be valid input, therefore
subclass implementations should program defensively.
.. class:: MultiWidget(widgets, attrs=None)
A widget that is composed of multiple widgets.
:class:`~django.forms.widgets.MultiWidget` works hand in hand with the
:class:`~django.forms.MultiValueField`.
.. method:: render(name, value, attrs=None)
Argument `value` is handled differently in this method from the
subclasses of :class:`~Widget`.
If `value` is a list, output of :meth:`~MultiWidget.render` will be a
concatenation of rendered child widgets. If `value` is not a list, it
will be first processed by the method :meth:`~MultiWidget.decompress()`
to create the list and then processed as above.
Unlike in the single value widgets, method :meth:`~MultiWidget.render`
need not be implemented in the subclasses.
.. method:: decompress(value)
Returns a list of "decompressed" values for the given value of the
multi-value field that makes use of the widget. The input value can be
assumed as valid, but not necessarily non-empty.
This method **must be implemented** by the subclass, and since the
value may be empty, the implementation must be defensive.
The rationale behind "decompression" is that it is necessary to "split"
the combined value of the form field into the values of the individual
field encapsulated within the multi-value field (e.g. when displaying
the partially or fully filled-out form).
.. tip::
Note that :class:`~django.forms.MultiValueField` has a
complementary method :meth:`~django.forms.MultiValueField.compress`
with the opposite responsibility - to combine cleaned values of
all member fields into one.
.. _built-in widgets:
Built-in widgets
----------------
Django provides a representation of all the basic HTML widgets, plus some
commonly used groups of widgets in the ``django.forms.widgets`` module,
including :ref:`the input of text <text-widgets>`, :ref:`various checkboxes
and selectors <selector-widgets>`, :ref:`uploading files <file-upload-widgets>`,
and :ref:`handling of multi-valued input <composite-widgets>`.
.. _text-widgets:
Widgets handling input of text
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
These widgets make use of the HTML elements ``input`` and ``textarea``.
``TextInput``
~~~~~~~~~~~~~
@ -204,39 +306,8 @@ commonly used groups of widgets:
Hidden input: ``<input type='hidden' ...>``
``MultipleHiddenInput``
~~~~~~~~~~~~~~~~~~~~~~~
.. class:: MultipleHiddenInput
Multiple ``<input type='hidden' ...>`` widgets.
A widget that handles multiple hidden widgets for fields that have a list
of values.
.. attribute:: MultipleHiddenInput.choices
This attribute is optional when the field does not have a
:attr:`~Field.choices` attribute. If it does, it will override anything
you set here when the attribute is updated on the :class:`Field`.
``FileInput``
~~~~~~~~~~~~~
.. class:: FileInput
File upload input: ``<input type='file' ...>``
``ClearableFileInput``
~~~~~~~~~~~~~~~~~~~~~~
.. class:: ClearableFileInput
.. versionadded:: 1.3
File upload input: ``<input type='file' ...>``, with an additional checkbox
input to clear the field's value, if the field is not required and has
initial data.
Note that there also is a :class:`MultipleHiddenInput` widget that
encapsulates a set of hidden input elements.
``DateInput``
~~~~~~~~~~~~~
@ -245,7 +316,7 @@ commonly used groups of widgets:
Date input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: DateInput.format
@ -262,7 +333,7 @@ commonly used groups of widgets:
Date/time input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: DateTimeInput.format
@ -279,7 +350,7 @@ commonly used groups of widgets:
Time input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: TimeInput.format
@ -296,6 +367,11 @@ commonly used groups of widgets:
Text area: ``<textarea>...</textarea>``
.. _selector-widgets:
Selector and checkbox widgets
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``CheckboxInput``
~~~~~~~~~~~~~~~~~
@ -439,6 +515,50 @@ commonly used groups of widgets:
...
</ul>
.. _file-upload-widgets:
File upload widgets
^^^^^^^^^^^^^^^^^^^
``FileInput``
~~~~~~~~~~~~~
.. class:: FileInput
File upload input: ``<input type='file' ...>``
``ClearableFileInput``
~~~~~~~~~~~~~~~~~~~~~~
.. class:: ClearableFileInput
.. versionadded:: 1.3
File upload input: ``<input type='file' ...>``, with an additional checkbox
input to clear the field's value, if the field is not required and has
initial data.
.. _composite-widgets:
Composite widgets
^^^^^^^^^^^^^^^^^
``MultipleHiddenInput``
~~~~~~~~~~~~~~~~~~~~~~~
.. class:: MultipleHiddenInput
Multiple ``<input type='hidden' ...>`` widgets.
A widget that handles multiple hidden widgets for fields that have a list
of values.
.. attribute:: MultipleHiddenInput.choices
This attribute is optional when the field does not have a
:attr:`~Field.choices` attribute. If it does, it will override anything
you set here when the attribute is updated on the :class:`Field`.
``MultiWidget``
~~~~~~~~~~~~~~~

View File

@ -195,6 +195,14 @@ support tablespaces for indexes, this option is ignored.
The default value for the field. This can be a value or a callable object. If
callable it will be called every time a new object is created.
The default cannot be a mutable object (model instance, list, set, etc.), as a
reference to the same instance of that object would be used as the default
value in all new model instances. Instead, wrap the desired default in a
callable. For example, if you had a custom ``JSONField`` and wanted to specify
a dictionary as the default, use a ``lambda`` as follows::
contact_info = JSONField("ContactInfo", default=lambda:{"email": "to1@example.com"})
``editable``
------------
@ -983,10 +991,10 @@ define the details of how the relation works.
this with functions from the Python ``datetime`` module to limit choices of
objects by date. For example::
limit_choices_to = {'pub_date__lte': datetime.now}
limit_choices_to = {'pub_date__lte': datetime.date.today}
only allows the choice of related objects with a ``pub_date`` before the
current date/time to be chosen.
current date to be chosen.
Instead of a dictionary this can also be a :class:`~django.db.models.Q`
object for more :ref:`complex queries <complex-lookups-with-q>`. However,

View File

@ -135,7 +135,7 @@ access to more than a single field::
raise ValidationError('Draft entries may not have a publication date.')
# Set the pub_date for published items if it hasn't been set already.
if self.status == 'published' and self.pub_date is None:
self.pub_date = datetime.datetime.now()
self.pub_date = datetime.date.today()
Any :exc:`~django.core.exceptions.ValidationError` exceptions raised by
``Model.clean()`` will be stored in a special key error dictionary key,

View File

@ -31,6 +31,9 @@ You can evaluate a ``QuerySet`` in the following ways:
for e in Entry.objects.all():
print(e.headline)
Note: Don't use this if all you want to do is determine if at least one
result exists. It's more efficient to use :meth:`~QuerySet.exists`.
* **Slicing.** As explained in :ref:`limiting-querysets`, a ``QuerySet`` can
be sliced, using Python's array-slicing syntax. Slicing an unevaluated
``QuerySet`` usually returns another unevaluated ``QuerySet``, but Django
@ -75,7 +78,7 @@ You can evaluate a ``QuerySet`` in the following ways:
Note: *Don't* use this if all you want to do is determine if at least one
result exists, and don't need the actual objects. It's more efficient to
use :meth:`exists() <QuerySet.exists>` (see below).
use :meth:`~QuerySet.exists` (see below).
.. _pickling QuerySets:
@ -1047,7 +1050,7 @@ defer
In some complex data-modeling situations, your models might contain a lot of
fields, some of which could contain a lot of data (for example, text fields),
or require expensive processing to convert them to Python objects. If you are
using the results of a queryset in some situation where you know you don't know
using the results of a queryset in some situation where you don't know
if you need those particular fields when you initially fetch the data, you can
tell Django not to retrieve them from the database.
@ -1523,9 +1526,40 @@ exists
Returns ``True`` if the :class:`.QuerySet` contains any results, and ``False``
if not. This tries to perform the query in the simplest and fastest way
possible, but it *does* execute nearly the same query. This means that calling
:meth:`.QuerySet.exists` is faster than ``bool(some_query_set)``, but not by
a large degree. If ``some_query_set`` has not yet been evaluated, but you know
possible, but it *does* execute nearly the same query as a normal
:class:`.QuerySet` query.
:meth:`~.QuerySet.exists` is useful for searches relating to both
object membership in a :class:`.QuerySet` and to the existence of any objects in
a :class:`.QuerySet`, particularly in the context of a large :class:`.QuerySet`.
The most efficient method of finding whether a model with a unique field
(e.g. ``primary_key``) is a member of a :class:`.QuerySet` is::
entry = Entry.objects.get(pk=123)
if some_query_set.filter(pk=entry.pk).exists():
print("Entry contained in queryset")
Which will be faster than the following which requires evaluating and iterating
through the entire queryset::
if entry in some_query_set:
print("Entry contained in QuerySet")
And to find whether a queryset contains any items::
if some_query_set.exists():
print("There is at least one object in some_query_set")
Which will be faster than::
if some_query_set:
print("There is at least one object in some_query_set")
... but not by a large degree (hence needing a large queryset for efficiency
gains).
Additionally, if a ``some_query_set`` has not yet been evaluated, but you know
that it will be at some point, then using ``some_query_set.exists()`` will do
more overall work (one query for the existence check plus an extra one to later
retrieve the results) than simply using ``bool(some_query_set)``, which
@ -1945,6 +1979,17 @@ SQL equivalent::
You can use ``range`` anywhere you can use ``BETWEEN`` in SQL — for dates,
numbers and even characters.
.. warning::
Filtering a ``DateTimeField`` with dates won't include items on the last
day, because the bounds are interpreted as "0am on the given date". If
``pub_date`` was a ``DateTimeField``, the above expression would be turned
into this SQL::
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
Generally speaking, you can't mix dates and datetimes.
.. fieldlookup:: year
year
@ -1958,7 +2003,7 @@ Example::
SQL equivalent::
SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31 23:59:59.999999';
SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
(The exact SQL syntax varies for each database engine.)

View File

@ -1304,25 +1304,13 @@ The URL where requests are redirected after login when the
This is used by the :func:`~django.contrib.auth.decorators.login_required`
decorator, for example.
.. _`note on LOGIN_REDIRECT_URL setting`:
.. versionchanged:: 1.5
.. note::
You can use :func:`~django.core.urlresolvers.reverse_lazy` to reference
URLs by their name instead of providing a hardcoded value. Assuming a
``urls.py`` with an URLpattern named ``home``::
urlpatterns = patterns('',
url('^welcome/$', 'test_app.views.home', name='home'),
)
You can use :func:`~django.core.urlresolvers.reverse_lazy` like this::
from django.core.urlresolvers import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('home')
This also works fine with localized URLs using
:func:`~django.conf.urls.i18n.i18n_patterns`.
This setting now also accepts view function names and
:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
configuration duplication since you no longer have to define the URL in two
places (``settings`` and URLconf).
For backward compatibility reasons the default remains unchanged.
.. setting:: LOGIN_URL
@ -1334,8 +1322,13 @@ Default: ``'/accounts/login/'``
The URL where requests are redirected for login, especially when using the
:func:`~django.contrib.auth.decorators.login_required` decorator.
.. note::
See the `note on LOGIN_REDIRECT_URL setting`_
.. versionchanged:: 1.5
This setting now also accepts view function names and
:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
configuration duplication since you no longer have to define the URL in two
places (``settings`` and URLconf).
For backward compatibility reasons the default remains unchanged.
.. setting:: LOGOUT_URL
@ -1346,9 +1339,6 @@ Default: ``'/accounts/logout/'``
LOGIN_URL counterpart.
.. note::
See the `note on LOGIN_REDIRECT_URL setting`_
.. setting:: MANAGERS
MANAGERS

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