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:
commit
9313dea700
4
AUTHORS
4
AUTHORS
|
@ -31,6 +31,8 @@ The PRIMARY AUTHORS are (and/or have been):
|
||||||
* Claude Paroz
|
* Claude Paroz
|
||||||
* Anssi Kääriäinen
|
* Anssi Kääriäinen
|
||||||
* Florian Apolloner
|
* Florian Apolloner
|
||||||
|
* Jeremy Dunck
|
||||||
|
* Bryan Veloso
|
||||||
|
|
||||||
More information on the main contributors to Django can be found in
|
More information on the main contributors to Django can be found in
|
||||||
docs/internals/committers.txt.
|
docs/internals/committers.txt.
|
||||||
|
@ -167,7 +169,6 @@ answer newbie questions, and generally made Django that much better:
|
||||||
dready <wil@mojipage.com>
|
dready <wil@mojipage.com>
|
||||||
Maximillian Dornseif <md@hudora.de>
|
Maximillian Dornseif <md@hudora.de>
|
||||||
Daniel Duan <DaNmarner@gmail.com>
|
Daniel Duan <DaNmarner@gmail.com>
|
||||||
Jeremy Dunck <http://dunck.us/>
|
|
||||||
Andrew Durdin <adurdin@gmail.com>
|
Andrew Durdin <adurdin@gmail.com>
|
||||||
dusk@woofle.net
|
dusk@woofle.net
|
||||||
Andy Dustman <farcepest@gmail.com>
|
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>
|
Johan C. Stöver <johan@nilling.nl>
|
||||||
Nowell Strite <http://nowell.strite.org/>
|
Nowell Strite <http://nowell.strite.org/>
|
||||||
Thomas Stromberg <tstromberg@google.com>
|
Thomas Stromberg <tstromberg@google.com>
|
||||||
|
Travis Swicegood <travis@domain51.com>
|
||||||
Pascal Varet
|
Pascal Varet
|
||||||
SuperJared
|
SuperJared
|
||||||
Radek Švarz <http://www.svarz.cz/translate/>
|
Radek Švarz <http://www.svarz.cz/translate/>
|
||||||
|
|
|
@ -7,7 +7,6 @@ a list of all possible variables.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import time # Needed for Windows
|
import time # Needed for Windows
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
@ -26,7 +25,7 @@ class LazySettings(LazyObject):
|
||||||
The user can manually configure settings prior to using them. Otherwise,
|
The user can manually configure settings prior to using them. Otherwise,
|
||||||
Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE.
|
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
|
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
|
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.
|
if not settings_module: # If it's set but is an empty string.
|
||||||
raise KeyError
|
raise KeyError
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# NOTE: This is arguably an EnvironmentError, but that causes
|
raise ImproperlyConfigured(
|
||||||
# problems with Python's interactive help.
|
"Requested setting %s, but settings are not configured. "
|
||||||
raise ImportError("Settings cannot be imported, because environment variable %s is undefined." % ENVIRONMENT_VARIABLE)
|
"You must either define the environment variable %s "
|
||||||
|
"or call settings.configure() before accessing settings."
|
||||||
|
% (name, ENVIRONMENT_VARIABLE))
|
||||||
|
|
||||||
self._wrapped = Settings(settings_module)
|
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):
|
def configure(self, default_settings=global_settings, **options):
|
||||||
"""
|
"""
|
||||||
Called to manually configure the settings. The 'default_settings'
|
Called to manually configure the settings. The 'default_settings'
|
||||||
|
|
|
@ -8,13 +8,13 @@ certain test -- e.g. being a DateField or ForeignKey.
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.db import models
|
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.encoding import smart_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from django.contrib.admin.util import (get_model_from_relation,
|
from django.contrib.admin.util import (get_model_from_relation,
|
||||||
reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
|
reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
|
||||||
|
from django.contrib.admin.options import IncorrectLookupParameters
|
||||||
|
|
||||||
class ListFilter(object):
|
class ListFilter(object):
|
||||||
title = None # Human-readable title to appear in the right sidebar.
|
title = None # Human-readable title to appear in the right sidebar.
|
||||||
|
@ -129,7 +129,10 @@ class FieldListFilter(ListFilter):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
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
|
@classmethod
|
||||||
def register(cls, test, list_filter_class, take_priority=False):
|
def register(cls, test, list_filter_class, take_priority=False):
|
||||||
|
|
|
@ -14,9 +14,10 @@ from django.core.exceptions import PermissionDenied, ValidationError
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models, transaction, router
|
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.related import RelatedObject
|
||||||
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
|
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.http import Http404, HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.template.response import SimpleTemplateResponse, TemplateResponse
|
from django.template.response import SimpleTemplateResponse, TemplateResponse
|
||||||
|
@ -1456,8 +1457,10 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||||
return request.user.has_perm(
|
return request.user.has_perm(
|
||||||
self.opts.app_label + '.' + self.opts.get_delete_permission())
|
self.opts.app_label + '.' + self.opts.get_delete_permission())
|
||||||
|
|
||||||
|
|
||||||
class StackedInline(InlineModelAdmin):
|
class StackedInline(InlineModelAdmin):
|
||||||
template = 'admin/edit_inline/stacked.html'
|
template = 'admin/edit_inline/stacked.html'
|
||||||
|
|
||||||
|
|
||||||
class TabularInline(InlineModelAdmin):
|
class TabularInline(InlineModelAdmin):
|
||||||
template = 'admin/edit_inline/tabular.html'
|
template = 'admin/edit_inline/tabular.html'
|
||||||
|
|
|
@ -9,128 +9,264 @@
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Spiced up with Code from Zain Memon's GSoC project 2009
|
* 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
|
* Licensed under the New BSD License
|
||||||
* See: http://www.opensource.org/licenses/bsd-license.php
|
* See: http://www.opensource.org/licenses/bsd-license.php
|
||||||
*/
|
*/
|
||||||
(function($) {
|
(function($) {
|
||||||
$.fn.formset = function(opts) {
|
$.fn.formset = function(opts) {
|
||||||
var options = $.extend({}, $.fn.formset.defaults, opts);
|
var options = $.extend({}, $.fn.formset.defaults, opts);
|
||||||
var updateElementIndex = function(el, prefix, ndx) {
|
var $this = $(this);
|
||||||
var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
|
var $parent = $this.parent();
|
||||||
var replacement = prefix + "-" + ndx;
|
var updateElementIndex = function(el, prefix, ndx) {
|
||||||
if ($(el).attr("for")) {
|
var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
|
||||||
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
|
var replacement = prefix + "-" + ndx;
|
||||||
}
|
if ($(el).attr("for")) {
|
||||||
if (el.id) {
|
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
|
||||||
el.id = el.id.replace(id_regex, replacement);
|
}
|
||||||
}
|
if (el.id) {
|
||||||
if (el.name) {
|
el.id = el.id.replace(id_regex, replacement);
|
||||||
el.name = el.name.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");
|
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").attr("autocomplete", "off");
|
||||||
// only show the add button if we are allowed to add more items,
|
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.
|
// note that max_num = None translates to a blank string.
|
||||||
var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
|
var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
|
||||||
$(this).each(function(i) {
|
$this.each(function(i) {
|
||||||
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
|
$(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
|
||||||
});
|
});
|
||||||
if ($(this).length && showAddButton) {
|
if ($this.length && showAddButton) {
|
||||||
var addButton;
|
var addButton;
|
||||||
if ($(this).attr("tagName") == "TR") {
|
if ($this.attr("tagName") == "TR") {
|
||||||
// If forms are laid out as table rows, insert the
|
// If forms are laid out as table rows, insert the
|
||||||
// "add" button in a new table row:
|
// "add" button in a new table row:
|
||||||
var numCols = this.eq(-1).children().length;
|
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>");
|
$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");
|
addButton = $parent.find("tr:last a");
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, insert it immediately after the last form:
|
// 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>");
|
$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 = $this.filter(":last").next().find("a");
|
||||||
}
|
}
|
||||||
addButton.click(function(e) {
|
addButton.click(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
|
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
|
||||||
var template = $("#" + options.prefix + "-empty");
|
var template = $("#" + options.prefix + "-empty");
|
||||||
var row = template.clone(true);
|
var row = template.clone(true);
|
||||||
row.removeClass(options.emptyCssClass)
|
row.removeClass(options.emptyCssClass)
|
||||||
.addClass(options.formCssClass)
|
.addClass(options.formCssClass)
|
||||||
.attr("id", options.prefix + "-" + nextIndex);
|
.attr("id", options.prefix + "-" + nextIndex);
|
||||||
if (row.is("tr")) {
|
if (row.is("tr")) {
|
||||||
// If the forms are laid out in table rows, insert
|
// If the forms are laid out in table rows, insert
|
||||||
// the remove button into the last table cell:
|
// 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>");
|
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")) {
|
} else if (row.is("ul") || row.is("ol")) {
|
||||||
// If they're laid out as an ordered/unordered list,
|
// If they're laid out as an ordered/unordered list,
|
||||||
// insert an <li> after the last list item:
|
// insert an <li> after the last list item:
|
||||||
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
|
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, just insert the remove button as the
|
// Otherwise, just insert the remove button as the
|
||||||
// last child element of the form's container:
|
// 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.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
|
||||||
}
|
}
|
||||||
row.find("*").each(function() {
|
row.find("*").each(function() {
|
||||||
updateElementIndex(this, options.prefix, totalForms.val());
|
updateElementIndex(this, options.prefix, totalForms.val());
|
||||||
});
|
});
|
||||||
// Insert the new form when it has been fully edited
|
// Insert the new form when it has been fully edited
|
||||||
row.insertBefore($(template));
|
row.insertBefore($(template));
|
||||||
// Update number of total forms
|
// Update number of total forms
|
||||||
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
|
$(totalForms).val(parseInt(totalForms.val(), 10) + 1);
|
||||||
nextIndex += 1;
|
nextIndex += 1;
|
||||||
// Hide add button in case we've hit the max, except we want to add infinitely
|
// Hide add button in case we've hit the max, except we want to add infinitely
|
||||||
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
|
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
|
||||||
addButton.parent().hide();
|
addButton.parent().hide();
|
||||||
}
|
}
|
||||||
// The delete button of each row triggers a bunch of other things
|
// The delete button of each row triggers a bunch of other things
|
||||||
row.find("a." + options.deleteCssClass).click(function(e) {
|
row.find("a." + options.deleteCssClass).click(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Remove the parent form containing this button:
|
// Remove the parent form containing this button:
|
||||||
var row = $(this).parents("." + options.formCssClass);
|
var row = $(this).parents("." + options.formCssClass);
|
||||||
row.remove();
|
row.remove();
|
||||||
nextIndex -= 1;
|
nextIndex -= 1;
|
||||||
// If a post-delete callback was provided, call it with the deleted form:
|
// If a post-delete callback was provided, call it with the deleted form:
|
||||||
if (options.removed) {
|
if (options.removed) {
|
||||||
options.removed(row);
|
options.removed(row);
|
||||||
}
|
}
|
||||||
// Update the TOTAL_FORMS form count.
|
// Update the TOTAL_FORMS form count.
|
||||||
var forms = $("." + options.formCssClass);
|
var forms = $("." + options.formCssClass);
|
||||||
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
|
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
|
||||||
// Show add button again once we drop below max
|
// Show add button again once we drop below max
|
||||||
if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
|
if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
|
||||||
addButton.parent().show();
|
addButton.parent().show();
|
||||||
}
|
}
|
||||||
// Also, update names and ids for all remaining form controls
|
// Also, update names and ids for all remaining form controls
|
||||||
// so they remain in sequence:
|
// so they remain in sequence:
|
||||||
for (var i=0, formCount=forms.length; i<formCount; i++)
|
for (var i=0, formCount=forms.length; i<formCount; i++)
|
||||||
{
|
{
|
||||||
updateElementIndex($(forms).get(i), options.prefix, i);
|
updateElementIndex($(forms).get(i), options.prefix, i);
|
||||||
$(forms.get(i)).find("*").each(function() {
|
$(forms.get(i)).find("*").each(function() {
|
||||||
updateElementIndex(this, options.prefix, i);
|
updateElementIndex(this, options.prefix, i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// If a post-add callback was supplied, call it with the added form:
|
// If a post-add callback was supplied, call it with the added form:
|
||||||
if (options.added) {
|
if (options.added) {
|
||||||
options.added(row);
|
options.added(row);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
/* Setup plugin defaults */
|
|
||||||
$.fn.formset.defaults = {
|
/* Setup plugin defaults */
|
||||||
prefix: "form", // The form prefix for your django formset
|
$.fn.formset.defaults = {
|
||||||
addText: "add another", // Text for the add link
|
prefix: "form", // The form prefix for your django formset
|
||||||
deleteText: "remove", // Text for the delete link
|
addText: "add another", // Text for the add link
|
||||||
addCssClass: "add-row", // CSS class applied to the add link
|
deleteText: "remove", // Text for the delete link
|
||||||
deleteCssClass: "delete-row", // CSS class applied to the delete link
|
addCssClass: "add-row", // CSS class applied to the add link
|
||||||
emptyCssClass: "empty-row", // CSS class applied to the empty row
|
deleteCssClass: "delete-row", // CSS class applied to the delete link
|
||||||
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
|
emptyCssClass: "empty-row", // CSS class applied to the empty row
|
||||||
added: null, // Function called each time a new form is added
|
formCssClass: "dynamic-form", // CSS class applied to each form in a formset
|
||||||
removed: null // Function called each time a form is deleted
|
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);
|
})(django.jQuery);
|
||||||
|
|
|
@ -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("."+
|
(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(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();
|
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+
|
||||||
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)">'+
|
"-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.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,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,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);
|
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);
|
||||||
|
|
|
@ -20,63 +20,11 @@
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function($) {
|
(function($) {
|
||||||
$(document).ready(function() {
|
$("#{{ inline_admin_formset.formset.prefix }}-group .inline-related").stackedFormset({
|
||||||
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .inline-related";
|
prefix: '{{ inline_admin_formset.formset.prefix }}',
|
||||||
var updateInlineLabel = function(row) {
|
adminStaticPrefix: '{% static "admin/" %}',
|
||||||
$(rows).find(".inline_label").each(function(i) {
|
deleteText: "{% trans "Remove" %}",
|
||||||
var count = i + 1;
|
addText: "{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|title %}Add another {{ verbose_name }}{% endblocktrans %}"
|
||||||
$(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);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
})(django.jQuery);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -67,64 +67,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
$(document).ready(function($) {
|
$("#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr").tabularFormset({
|
||||||
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr";
|
prefix: "{{ inline_admin_formset.formset.prefix }}",
|
||||||
var alternatingRows = function(row) {
|
adminStaticPrefix: '{% static "admin/" %}',
|
||||||
$(rows).not(".add-row").removeClass("row1 row2")
|
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
|
||||||
.filter(":even").addClass("row1").end()
|
deleteText: "{% trans 'Remove' %}"
|
||||||
.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);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
})(django.jQuery);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -182,7 +182,7 @@ def items_for_result(cl, result, form):
|
||||||
row_class = ''
|
row_class = ''
|
||||||
try:
|
try:
|
||||||
f, attr, value = lookup_field(field_name, result, cl.model_admin)
|
f, attr, value = lookup_field(field_name, result, cl.model_admin)
|
||||||
except (AttributeError, ObjectDoesNotExist):
|
except ObjectDoesNotExist:
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
else:
|
else:
|
||||||
if f is None:
|
if f is None:
|
||||||
|
|
|
@ -4,7 +4,7 @@ import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
|
||||||
from django.db import models
|
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.deletion import Collector
|
||||||
from django.db.models.related import RelatedObject
|
from django.db.models.related import RelatedObject
|
||||||
from django.forms.forms import pretty_name
|
from django.forms.forms import pretty_name
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.utils.decorators import available_attrs
|
from django.utils.decorators import available_attrs
|
||||||
from django.utils.encoding import force_str
|
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):
|
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):
|
if test_func(request.user):
|
||||||
return view_func(request, *args, **kwargs)
|
return view_func(request, *args, **kwargs)
|
||||||
path = request.build_absolute_uri()
|
path = request.build_absolute_uri()
|
||||||
# urlparse chokes on lazy objects in Python 3
|
# urlparse chokes on lazy objects in Python 3, force to str
|
||||||
login_url_as_str = force_str(login_url or settings.LOGIN_URL)
|
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
|
# If the login url is the same scheme and net location then just
|
||||||
# use the path as the "next" url.
|
# 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]
|
current_scheme, current_netloc = urlparse(path)[:2]
|
||||||
if ((not login_scheme or login_scheme == current_scheme) and
|
if ((not login_scheme or login_scheme == current_scheme) and
|
||||||
(not login_netloc or login_netloc == current_netloc)):
|
(not login_netloc or login_netloc == current_netloc)):
|
||||||
path = request.get_full_path()
|
path = request.get_full_path()
|
||||||
from django.contrib.auth.views import redirect_to_login
|
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 _wrapped_view
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.contrib.auth.models import User
|
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.auth.tokens import default_token_generator
|
||||||
from django.contrib.sites.models import get_current_site
|
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):
|
class ReadOnlyPasswordHashWidget(forms.Widget):
|
||||||
def render(self, name, value, attrs):
|
def render(self, name, value, attrs):
|
||||||
encoded = value
|
encoded = value
|
||||||
|
|
||||||
if not is_password_usable(encoded):
|
|
||||||
return "None"
|
|
||||||
|
|
||||||
final_attrs = self.build_attrs(attrs)
|
final_attrs = self.build_attrs(attrs)
|
||||||
|
|
||||||
try:
|
if encoded == '' or encoded == UNUSABLE_PASSWORD:
|
||||||
hasher = identify_hasher(encoded)
|
summary = mark_safe("<strong>%s</strong>" % ugettext("No password set."))
|
||||||
except ValueError:
|
|
||||||
summary = mark_safe("<strong>Invalid password format or unknown hashing algorithm.</strong>")
|
|
||||||
else:
|
else:
|
||||||
summary = format_html_join('',
|
try:
|
||||||
"<strong>{0}</strong>: {1} ",
|
hasher = identify_hasher(encoded)
|
||||||
((ugettext(key), value)
|
except ValueError:
|
||||||
for key, value in hasher.safe_summary(encoded).items())
|
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)
|
return format_html("<div{0}>{1}</div>", flatatt(final_attrs), summary)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,13 @@ def reset_hashers(**kwargs):
|
||||||
|
|
||||||
|
|
||||||
def is_password_usable(encoded):
|
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'):
|
def check_password(password, encoded, setter=None, preferred='default'):
|
||||||
|
|
|
@ -25,7 +25,7 @@ def update_last_login(sender, user, **kwargs):
|
||||||
the user logging in.
|
the user logging in.
|
||||||
"""
|
"""
|
||||||
user.last_login = timezone.now()
|
user.last_login = timezone.now()
|
||||||
user.save()
|
user.save(update_fields=['last_login'])
|
||||||
user_logged_in.connect(update_last_login)
|
user_logged_in.connect(update_last_login)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ class LoginRequiredTestCase(AuthViewsTestCase):
|
||||||
pass
|
pass
|
||||||
login_required(normal_view)
|
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
|
Check that login_required works on a simple view wrapped in a
|
||||||
login_required decorator.
|
login_required decorator.
|
||||||
|
|
|
@ -236,23 +236,29 @@ class UserChangeFormTest(TestCase):
|
||||||
# Just check we can create it
|
# Just check we can create it
|
||||||
form = MyUserForm({})
|
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):
|
def test_bug_17944_empty_password(self):
|
||||||
user = User.objects.get(username='empty_password')
|
user = User.objects.get(username='empty_password')
|
||||||
form = UserChangeForm(instance=user)
|
form = UserChangeForm(instance=user)
|
||||||
# Just check that no error is raised.
|
self.assertIn(_("No password set."), form.as_table())
|
||||||
form.as_table()
|
|
||||||
|
|
||||||
def test_bug_17944_unmanageable_password(self):
|
def test_bug_17944_unmanageable_password(self):
|
||||||
user = User.objects.get(username='unmanageable_password')
|
user = User.objects.get(username='unmanageable_password')
|
||||||
form = UserChangeForm(instance=user)
|
form = UserChangeForm(instance=user)
|
||||||
# Just check that no error is raised.
|
self.assertIn(_("Invalid password format or unknown hashing algorithm."),
|
||||||
form.as_table()
|
form.as_table())
|
||||||
|
|
||||||
def test_bug_17944_unknown_password_algorithm(self):
|
def test_bug_17944_unknown_password_algorithm(self):
|
||||||
user = User.objects.get(username='unknown_password')
|
user = User.objects.get(username='unknown_password')
|
||||||
form = UserChangeForm(instance=user)
|
form = UserChangeForm(instance=user)
|
||||||
# Just check that no error is raised.
|
self.assertIn(_("Invalid password format or unknown hashing algorithm."),
|
||||||
form.as_table()
|
form.as_table())
|
||||||
|
|
||||||
|
|
||||||
@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
|
|
|
@ -100,6 +100,10 @@ class TestUtilsHashPass(unittest.TestCase):
|
||||||
self.assertRaises(ValueError, doit)
|
self.assertRaises(ValueError, doit)
|
||||||
self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")
|
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):
|
def test_low_level_pkbdf2(self):
|
||||||
hasher = PBKDF2PasswordHasher()
|
hasher = PBKDF2PasswordHasher()
|
||||||
encoded = hasher.encode('letmein', 'seasalt')
|
encoded = hasher.encode('letmein', 'seasalt')
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import re
|
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.sites.models import Site, RequestSite
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
|
@ -23,7 +23,8 @@ from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm,
|
||||||
('en', 'English'),
|
('en', 'English'),
|
||||||
),
|
),
|
||||||
LANGUAGE_CODE='en',
|
LANGUAGE_CODE='en',
|
||||||
TEMPLATE_DIRS = (
|
TEMPLATE_LOADERS=global_settings.TEMPLATE_LOADERS,
|
||||||
|
TEMPLATE_DIRS=(
|
||||||
os.path.join(os.path.dirname(__file__), 'templates'),
|
os.path.join(os.path.dirname(__file__), 'templates'),
|
||||||
),
|
),
|
||||||
USE_TZ=False,
|
USE_TZ=False,
|
||||||
|
|
|
@ -7,9 +7,9 @@ from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import HttpResponseRedirect, QueryDict
|
from django.http import HttpResponseRedirect, QueryDict
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.encoding import force_str
|
|
||||||
from django.utils.http import base36_to_int
|
from django.utils.http import base36_to_int
|
||||||
from django.utils.translation import ugettext as _
|
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.debug import sensitive_post_parameters
|
||||||
from django.views.decorators.cache import never_cache
|
from django.views.decorators.cache import never_cache
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
|
@ -38,16 +38,16 @@ def login(request, template_name='registration/login.html',
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = authentication_form(data=request.POST)
|
form = authentication_form(data=request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
netloc = urlparse(redirect_to)[1]
|
|
||||||
|
|
||||||
# Use default setting if redirect_to is empty
|
# Use default setting if redirect_to is empty
|
||||||
if not redirect_to:
|
if not redirect_to:
|
||||||
redirect_to = settings.LOGIN_REDIRECT_URL
|
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
|
# Heavier security check -- don't allow redirection to a different
|
||||||
# host.
|
# host.
|
||||||
elif netloc and netloc != request.get_host():
|
if netloc and netloc != request.get_host():
|
||||||
redirect_to = settings.LOGIN_REDIRECT_URL
|
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
|
||||||
|
|
||||||
# Okay, security checks complete. Log the user in.
|
# Okay, security checks complete. Log the user in.
|
||||||
auth_login(request, form.get_user())
|
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:
|
if not login_url:
|
||||||
login_url = settings.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)
|
return logout(request, login_url, current_app=current_app, extra_context=extra_context)
|
||||||
|
|
||||||
def redirect_to_login(next, login_url=None,
|
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
|
Redirects the user to the login page, passing the given 'next' page
|
||||||
"""
|
"""
|
||||||
# urlparse chokes on lazy objects in Python 3
|
resolved_url = resolve_url(login_url or settings.LOGIN_URL)
|
||||||
login_url_as_str = force_str(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:
|
if redirect_field_name:
|
||||||
querystring = QueryDict(login_url_parts[4], mutable=True)
|
querystring = QueryDict(login_url_parts[4], mutable=True)
|
||||||
querystring[redirect_field_name] = next
|
querystring[redirect_field_name] = next
|
||||||
|
@ -229,7 +229,7 @@ def password_reset_complete(request,
|
||||||
template_name='registration/password_reset_complete.html',
|
template_name='registration/password_reset_complete.html',
|
||||||
current_app=None, extra_context=None):
|
current_app=None, extra_context=None):
|
||||||
context = {
|
context = {
|
||||||
'login_url': settings.LOGIN_URL
|
'login_url': resolve_url(settings.LOGIN_URL)
|
||||||
}
|
}
|
||||||
if extra_context is not None:
|
if extra_context is not None:
|
||||||
context.update(extra_context)
|
context.update(extra_context)
|
||||||
|
|
|
@ -90,8 +90,6 @@ class BaseSpatialOperations(object):
|
||||||
|
|
||||||
# For quoting column values, rather than columns.
|
# For quoting column values, rather than columns.
|
||||||
def geo_quote_name(self, name):
|
def geo_quote_name(self, name):
|
||||||
if isinstance(name, six.text_type):
|
|
||||||
name = name.encode('ascii')
|
|
||||||
return "'%s'" % name
|
return "'%s'" % name
|
||||||
|
|
||||||
# GeometryField operations
|
# GeometryField operations
|
||||||
|
|
|
@ -3,20 +3,6 @@ A collection of utility routines and classes used by the spatial
|
||||||
backends.
|
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):
|
class SpatialOperation(object):
|
||||||
"""
|
"""
|
||||||
Base class for generating spatial SQL.
|
Base class for generating spatial SQL.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
from django.db.models.constants import LOOKUP_SEP
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
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.expressions import SQLEvaluator
|
||||||
from django.db.models.sql.where import Constraint, WhereNode
|
from django.db.models.sql.where import Constraint, WhereNode
|
||||||
from django.contrib.gis.db.models.fields import GeometryField
|
from django.contrib.gis.db.models.fields import GeometryField
|
||||||
|
|
|
@ -181,7 +181,11 @@ class DataSourceTest(unittest.TestCase):
|
||||||
|
|
||||||
# Making sure the SpatialReference is as expected.
|
# Making sure the SpatialReference is as expected.
|
||||||
if hasattr(source, 'srs_wkt'):
|
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):
|
def test06_spatial_filter(self):
|
||||||
"Testing the Layer.spatial_filter property."
|
"Testing the Layer.spatial_filter property."
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import json
|
||||||
from binascii import b2a_hex
|
from binascii import b2a_hex
|
||||||
try:
|
try:
|
||||||
from django.utils.six.moves import cPickle as pickle
|
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:
|
for g in self.geometries.json_geoms:
|
||||||
geom = OGRGeometry(g.wkt)
|
geom = OGRGeometry(g.wkt)
|
||||||
if not hasattr(g, 'not_equal'):
|
if not hasattr(g, 'not_equal'):
|
||||||
self.assertEqual(g.json, geom.json)
|
# Loading jsons to prevent decimal differences
|
||||||
self.assertEqual(g.json, geom.geojson)
|
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))
|
self.assertEqual(OGRGeometry(g.wkt), OGRGeometry(geom.json))
|
||||||
|
|
||||||
def test02_points(self):
|
def test02_points(self):
|
||||||
|
|
|
@ -110,7 +110,7 @@ def geos_version_info():
|
||||||
is a release candidate (and what number release candidate), and the C API
|
is a release candidate (and what number release candidate), and the C API
|
||||||
version.
|
version.
|
||||||
"""
|
"""
|
||||||
ver = geos_version()
|
ver = geos_version().decode()
|
||||||
m = version_regex.match(ver)
|
m = version_regex.match(ver)
|
||||||
if not m: raise GEOSException('Could not parse version info string "%s"' % 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'))
|
return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))
|
||||||
|
|
|
@ -215,15 +215,18 @@ class ListMixin(object):
|
||||||
"Standard list reverse method"
|
"Standard list reverse method"
|
||||||
self[:] = self[-1::-1]
|
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"
|
"Standard list sort method"
|
||||||
if key:
|
if key:
|
||||||
temp = [(key(v),v) for v in self]
|
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]
|
self[:] = [v[1] for v in temp]
|
||||||
else:
|
else:
|
||||||
temp = list(self)
|
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
|
self[:] = temp
|
||||||
|
|
||||||
### Private routines ###
|
### Private routines ###
|
||||||
|
|
|
@ -16,7 +16,8 @@ test_suites = [
|
||||||
def suite():
|
def suite():
|
||||||
"Builds a test suite for the GEOS tests."
|
"Builds a test suite for the GEOS tests."
|
||||||
s = TestSuite()
|
s = TestSuite()
|
||||||
map(s.addTest, test_suites)
|
for suite in test_suites:
|
||||||
|
s.addTest(suite)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def run(verbosity=1):
|
def run(verbosity=1):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import ctypes
|
import ctypes
|
||||||
|
import json
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
|
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:
|
for g in self.geometries.json_geoms:
|
||||||
geom = GEOSGeometry(g.wkt)
|
geom = GEOSGeometry(g.wkt)
|
||||||
if not hasattr(g, 'not_equal'):
|
if not hasattr(g, 'not_equal'):
|
||||||
self.assertEqual(g.json, geom.json)
|
# Loading jsons to prevent decimal differences
|
||||||
self.assertEqual(g.json, geom.geojson)
|
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))
|
self.assertEqual(GEOSGeometry(g.wkt), GEOSGeometry(geom.json))
|
||||||
|
|
||||||
def test_fromfile(self):
|
def test_fromfile(self):
|
||||||
|
|
|
@ -55,14 +55,14 @@ class ListMixinTest(unittest.TestCase):
|
||||||
|
|
||||||
def lists_of_len(self, length=None):
|
def lists_of_len(self, length=None):
|
||||||
if length is None: length = self.limit
|
if length is None: length = self.limit
|
||||||
pl = range(length)
|
pl = list(range(length))
|
||||||
return pl, self.listType(pl)
|
return pl, self.listType(pl)
|
||||||
|
|
||||||
def limits_plus(self, b):
|
def limits_plus(self, b):
|
||||||
return range(-self.limit - b, self.limit + b)
|
return range(-self.limit - b, self.limit + b)
|
||||||
|
|
||||||
def step_range(self):
|
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):
|
def test01_getslice(self):
|
||||||
'Slice retrieval'
|
'Slice retrieval'
|
||||||
|
@ -160,13 +160,13 @@ class ListMixinTest(unittest.TestCase):
|
||||||
del pl[i:j]
|
del pl[i:j]
|
||||||
del ul[i:j]
|
del ul[i:j]
|
||||||
self.assertEqual(pl[:], ul[:], 'del slice [%d:%d]' % (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)
|
pl, ul = self.lists_of_len(Len)
|
||||||
del pl[i:j:k]
|
del pl[i:j:k]
|
||||||
del ul[i:j:k]
|
del ul[i:j:k]
|
||||||
self.assertEqual(pl[:], ul[:], 'del slice [%d:%d:%d]' % (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)
|
pl, ul = self.lists_of_len(Len)
|
||||||
del pl[:i:k]
|
del pl[:i:k]
|
||||||
del ul[:i:k]
|
del ul[:i:k]
|
||||||
|
@ -177,7 +177,7 @@ class ListMixinTest(unittest.TestCase):
|
||||||
del ul[i::k]
|
del ul[i::k]
|
||||||
self.assertEqual(pl[:], ul[:], 'del slice [%d::%d]' % (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)
|
pl, ul = self.lists_of_len(Len)
|
||||||
del pl[::k]
|
del pl[::k]
|
||||||
del ul[::k]
|
del ul[::k]
|
||||||
|
@ -320,7 +320,7 @@ class ListMixinTest(unittest.TestCase):
|
||||||
pl.sort()
|
pl.sort()
|
||||||
ul.sort()
|
ul.sort()
|
||||||
self.assertEqual(pl[:], 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)
|
pl.sort(key=lambda x: (mid-x)**2)
|
||||||
ul.sort(key=lambda x: (mid-x)**2)
|
ul.sort(key=lambda x: (mid-x)**2)
|
||||||
self.assertEqual(pl[:], ul[:], 'sort w/ key')
|
self.assertEqual(pl[:], ul[:], 'sort w/ key')
|
||||||
|
@ -330,7 +330,7 @@ class ListMixinTest(unittest.TestCase):
|
||||||
pl.sort(reverse=True)
|
pl.sort(reverse=True)
|
||||||
ul.sort(reverse=True)
|
ul.sort(reverse=True)
|
||||||
self.assertEqual(pl[:], ul[:], 'sort w/ reverse')
|
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)
|
pl.sort(key=lambda x: (mid-x)**2)
|
||||||
ul.sort(key=lambda x: (mid-x)**2)
|
ul.sort(key=lambda x: (mid-x)**2)
|
||||||
self.assertEqual(pl[:], ul[:], 'sort w/ key')
|
self.assertEqual(pl[:], ul[:], 'sort w/ key')
|
||||||
|
@ -338,7 +338,7 @@ class ListMixinTest(unittest.TestCase):
|
||||||
def test_12_arithmetic(self):
|
def test_12_arithmetic(self):
|
||||||
'Arithmetic'
|
'Arithmetic'
|
||||||
pl, ul = self.lists_of_len()
|
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(list(pl + al), list(ul + al), 'add')
|
||||||
self.assertEqual(type(ul), type(ul + al), 'type of add result')
|
self.assertEqual(type(ul), type(ul + al), 'type of add result')
|
||||||
self.assertEqual(list(al + pl), list(al + ul), 'radd')
|
self.assertEqual(list(al + pl), list(al + ul), 'radd')
|
||||||
|
|
|
@ -191,7 +191,8 @@ class GeoModelTest(TestCase):
|
||||||
cities1 = City.objects.all()
|
cities1 = City.objects.all()
|
||||||
# Only PostGIS would support a 'select *' query because of its recognized
|
# Only PostGIS would support a 'select *' query because of its recognized
|
||||||
# HEXEWKB format for geometry fields
|
# 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.assertEqual(len(cities1), len(list(cities2)))
|
||||||
self.assertTrue(isinstance(cities2[0].point, Point))
|
self.assertTrue(isinstance(cities2[0].point, Point))
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@ from django.utils import unittest
|
||||||
test_srs = ({'srid' : 4326,
|
test_srs = ({'srid' : 4326,
|
||||||
'auth_name' : ('EPSG', True),
|
'auth_name' : ('EPSG', True),
|
||||||
'auth_srid' : 4326,
|
'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"]]',
|
# Only the beginning, because there are differences depending on installed libs
|
||||||
'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"]]',
|
'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"',
|
||||||
'proj4' : '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
|
'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',
|
'spheroid' : 'WGS 84', 'name' : 'WGS 84',
|
||||||
'geographic' : True, 'projected' : False, 'spatialite' : True,
|
'geographic' : True, 'projected' : False, 'spatialite' : True,
|
||||||
'ellipsoid' : (6378137.0, 6356752.3, 298.257223563), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
|
'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,
|
{'srid' : 32140,
|
||||||
'auth_name' : ('EPSG', False),
|
'auth_name' : ('EPSG', False),
|
||||||
'auth_srid' : 32140,
|
'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"]]',
|
'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980"',
|
||||||
'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 ',
|
||||||
'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',
|
'spheroid' : 'GRS 1980', 'name' : 'NAD83 / Texas South Central',
|
||||||
'geographic' : False, 'projected' : True, 'spatialite' : False,
|
'geographic' : False, 'projected' : True, 'spatialite' : False,
|
||||||
'ellipsoid' : (6378137.0, 6356752.31414, 298.257222101), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
|
'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 :(
|
# No proj.4 and different srtext on oracle backends :(
|
||||||
if postgis:
|
if postgis:
|
||||||
if connection.ops.spatial_version >= (1, 4, 0):
|
self.assertTrue(srs.wkt.startswith(sd['srtext']))
|
||||||
srtext = sd['srtext14']
|
self.assertTrue(srs.proj4text in sd['proj4'])
|
||||||
else:
|
|
||||||
srtext = sd['srtext']
|
|
||||||
self.assertEqual(srtext, srs.wkt)
|
|
||||||
self.assertEqual(sd['proj4'], srs.proj4text)
|
|
||||||
|
|
||||||
@no_mysql
|
@no_mysql
|
||||||
def test02_osr(self):
|
def test02_osr(self):
|
||||||
"Testing getting OSR objects from SpatialRefSys model objects."
|
"Testing getting OSR objects from SpatialRefSys model objects."
|
||||||
from django.contrib.gis.gdal import GDAL_VERSION
|
|
||||||
for sd in test_srs:
|
for sd in test_srs:
|
||||||
sr = SpatialRefSys.objects.get(srid=sd['srid'])
|
sr = SpatialRefSys.objects.get(srid=sd['srid'])
|
||||||
self.assertEqual(True, sr.spheroid.startswith(sd['spheroid']))
|
self.assertEqual(True, sr.spheroid.startswith(sd['spheroid']))
|
||||||
|
@ -76,15 +73,10 @@ class SpatialRefSysTest(unittest.TestCase):
|
||||||
# Testing the SpatialReference object directly.
|
# Testing the SpatialReference object directly.
|
||||||
if postgis or spatialite:
|
if postgis or spatialite:
|
||||||
srs = sr.srs
|
srs = sr.srs
|
||||||
if GDAL_VERSION <= (1, 8):
|
self.assertTrue(srs.proj4 in sd['proj4'])
|
||||||
self.assertEqual(sd['proj4'], srs.proj4)
|
|
||||||
# No `srtext` field in the `spatial_ref_sys` table in SpatiaLite
|
# No `srtext` field in the `spatial_ref_sys` table in SpatiaLite
|
||||||
if not spatialite:
|
if not spatialite:
|
||||||
if connection.ops.spatial_version >= (1, 4, 0):
|
self.assertTrue(srs.wkt.startswith(sd['srtext']))
|
||||||
srtext = sd['srtext14']
|
|
||||||
else:
|
|
||||||
srtext = sd['srtext']
|
|
||||||
self.assertEqual(srtext, srs.wkt)
|
|
||||||
|
|
||||||
@no_mysql
|
@no_mysql
|
||||||
def test03_ellipsoid(self):
|
def test03_ellipsoid(self):
|
||||||
|
|
|
@ -47,6 +47,9 @@ def markdown(value, arg=''):
|
||||||
they will be silently ignored.
|
they will be silently ignored.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import warnings
|
||||||
|
warnings.warn('The markdown filter has been deprecated',
|
||||||
|
category=DeprecationWarning)
|
||||||
try:
|
try:
|
||||||
import markdown
|
import markdown
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -72,6 +75,9 @@ def markdown(value, arg=''):
|
||||||
|
|
||||||
@register.filter(is_safe=True)
|
@register.filter(is_safe=True)
|
||||||
def restructuredtext(value):
|
def restructuredtext(value):
|
||||||
|
import warnings
|
||||||
|
warnings.warn('The restructuredtext filter has been deprecated',
|
||||||
|
category=DeprecationWarning)
|
||||||
try:
|
try:
|
||||||
from docutils.core import publish_parts
|
from docutils.core import publish_parts
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
# Quick tests for the markup templatetags (django.contrib.markup)
|
# Quick tests for the markup templatetags (django.contrib.markup)
|
||||||
import re
|
import re
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.template import Template, Context
|
from django.template import Template, Context
|
||||||
|
from django import test
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
docutils = None
|
docutils = None
|
||||||
|
|
||||||
class Templates(unittest.TestCase):
|
class Templates(test.TestCase):
|
||||||
|
|
||||||
textile_content = """Paragraph 1
|
textile_content = """Paragraph 1
|
||||||
|
|
||||||
|
@ -37,6 +39,13 @@ Paragraph 2 with a link_
|
||||||
|
|
||||||
.. _link: http://www.example.com/"""
|
.. _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')
|
@unittest.skipUnless(textile, 'textile not installed')
|
||||||
def test_textile(self):
|
def test_textile(self):
|
||||||
t = Template("{% load markup %}{{ textile_content|textile }}")
|
t = Template("{% load markup %}{{ textile_content|textile }}")
|
||||||
|
|
|
@ -46,10 +46,10 @@ class CookieStorage(BaseStorage):
|
||||||
Stores messages in a cookie.
|
Stores messages in a cookie.
|
||||||
"""
|
"""
|
||||||
cookie_name = 'messages'
|
cookie_name = 'messages'
|
||||||
# We should be able to store 4K in a cookie, but Internet Explorer
|
# uwsgi's default configuration enforces a maximum size of 4kb for all the
|
||||||
# imposes 4K as the *total* limit for a domain. To allow other
|
# HTTP headers. In order to leave some room for other cookies and headers,
|
||||||
# cookies, we go for 3/4 of 4K.
|
# restrict the session cookie to 1/2 of 4kb. See #18781.
|
||||||
max_cookie_size = 3072
|
max_cookie_size = 2048
|
||||||
not_finished = '__messagesnotfinished__'
|
not_finished = '__messagesnotfinished__'
|
||||||
|
|
||||||
def _get(self, *args, **kwargs):
|
def _get(self, *args, **kwargs):
|
||||||
|
|
|
@ -152,7 +152,7 @@ class BaseTest(TestCase):
|
||||||
cycle.
|
cycle.
|
||||||
"""
|
"""
|
||||||
data = {
|
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')
|
show_url = reverse('django.contrib.messages.tests.urls.show')
|
||||||
for level in ('debug', 'info', 'success', 'warning', 'error'):
|
for level in ('debug', 'info', 'success', 'warning', 'error'):
|
||||||
|
@ -170,7 +170,7 @@ class BaseTest(TestCase):
|
||||||
@override_settings(MESSAGE_LEVEL=constants.DEBUG)
|
@override_settings(MESSAGE_LEVEL=constants.DEBUG)
|
||||||
def test_with_template_response(self):
|
def test_with_template_response(self):
|
||||||
data = {
|
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')
|
show_url = reverse('django.contrib.messages.tests.urls.show_template_response')
|
||||||
for level in self.levels.keys():
|
for level in self.levels.keys():
|
||||||
|
@ -194,7 +194,7 @@ class BaseTest(TestCase):
|
||||||
before a GET.
|
before a GET.
|
||||||
"""
|
"""
|
||||||
data = {
|
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')
|
show_url = reverse('django.contrib.messages.tests.urls.show')
|
||||||
messages = []
|
messages = []
|
||||||
|
@ -226,7 +226,7 @@ class BaseTest(TestCase):
|
||||||
when one attempts to store a message.
|
when one attempts to store a message.
|
||||||
"""
|
"""
|
||||||
data = {
|
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')
|
show_url = reverse('django.contrib.messages.tests.urls.show')
|
||||||
for level in ('debug', 'info', 'success', 'warning', 'error'):
|
for level in ('debug', 'info', 'success', 'warning', 'error'):
|
||||||
|
@ -251,7 +251,7 @@ class BaseTest(TestCase):
|
||||||
raised if 'fail_silently' = True
|
raised if 'fail_silently' = True
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
'messages': ['Test message %d' % x for x in range(10)],
|
'messages': ['Test message %d' % x for x in range(5)],
|
||||||
'fail_silently': True,
|
'fail_silently': True,
|
||||||
}
|
}
|
||||||
show_url = reverse('django.contrib.messages.tests.urls.show')
|
show_url = reverse('django.contrib.messages.tests.urls.show')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import timedelta
|
||||||
import shutil
|
import shutil
|
||||||
import string
|
import string
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -302,11 +302,11 @@ class CacheDBSessionTests(SessionTestsMixin, TestCase):
|
||||||
self.assertTrue(self.session.exists(self.session.session_key))
|
self.assertTrue(self.session.exists(self.session.session_key))
|
||||||
|
|
||||||
def test_load_overlong_key(self):
|
def test_load_overlong_key(self):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
# Some backends might issue a warning
|
||||||
warnings.simplefilter("always")
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
self.session._session_key = (string.ascii_letters + string.digits) * 20
|
self.session._session_key = (string.ascii_letters + string.digits) * 20
|
||||||
self.assertEqual(self.session.load(), {})
|
self.assertEqual(self.session.load(), {})
|
||||||
self.assertEqual(len(w), 1)
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(USE_TZ=True)
|
@override_settings(USE_TZ=True)
|
||||||
|
@ -352,11 +352,11 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
|
||||||
backend = CacheSession
|
backend = CacheSession
|
||||||
|
|
||||||
def test_load_overlong_key(self):
|
def test_load_overlong_key(self):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
# Some backends might issue a warning
|
||||||
warnings.simplefilter("always")
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
self.session._session_key = (string.ascii_letters + string.digits) * 20
|
self.session._session_key = (string.ascii_letters + string.digits) * 20
|
||||||
self.assertEqual(self.session.load(), {})
|
self.assertEqual(self.session.load(), {})
|
||||||
self.assertEqual(len(w), 1)
|
|
||||||
|
|
||||||
|
|
||||||
class SessionMiddlewareTests(unittest.TestCase):
|
class SessionMiddlewareTests(unittest.TestCase):
|
||||||
|
|
|
@ -223,18 +223,17 @@ class WSGIHandler(base.BaseHandler):
|
||||||
set_script_prefix(base.get_script_name(environ))
|
set_script_prefix(base.get_script_name(environ))
|
||||||
signals.request_started.send(sender=self.__class__)
|
signals.request_started.send(sender=self.__class__)
|
||||||
try:
|
try:
|
||||||
try:
|
request = self.request_class(environ)
|
||||||
request = self.request_class(environ)
|
except UnicodeDecodeError:
|
||||||
except UnicodeDecodeError:
|
logger.warning('Bad Request (UnicodeDecodeError)',
|
||||||
logger.warning('Bad Request (UnicodeDecodeError)',
|
exc_info=sys.exc_info(),
|
||||||
exc_info=sys.exc_info(),
|
extra={
|
||||||
extra={
|
'status_code': 400,
|
||||||
'status_code': 400,
|
}
|
||||||
}
|
)
|
||||||
)
|
response = http.HttpResponseBadRequest()
|
||||||
response = http.HttpResponseBadRequest()
|
else:
|
||||||
else:
|
response = self.get_response(request)
|
||||||
response = self.get_response(request)
|
|
||||||
finally:
|
finally:
|
||||||
signals.request_finished.send(sender=self.__class__)
|
signals.request_finished.send(sender=self.__class__)
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ from optparse import OptionParser, NO_DEFAULT
|
||||||
import imp
|
import imp
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.management.base import BaseCommand, CommandError, handle_default_options
|
from django.core.management.base import BaseCommand, CommandError, handle_default_options
|
||||||
from django.core.management.color import color_style
|
from django.core.management.color import color_style
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
|
@ -105,7 +106,7 @@ def get_commands():
|
||||||
try:
|
try:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
apps = settings.INSTALLED_APPS
|
apps = settings.INSTALLED_APPS
|
||||||
except (AttributeError, EnvironmentError, ImportError):
|
except (AttributeError, ImproperlyConfigured):
|
||||||
apps = []
|
apps = []
|
||||||
|
|
||||||
# Find and load the management module for each installed app.
|
# Find and load the management module for each installed app.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
from datetime import datetime
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -90,10 +91,12 @@ class Command(BaseCommand):
|
||||||
self.stdout.write("Validating models...\n\n")
|
self.stdout.write("Validating models...\n\n")
|
||||||
self.validate(display_num_errors=True)
|
self.validate(display_num_errors=True)
|
||||||
self.stdout.write((
|
self.stdout.write((
|
||||||
|
"%(started_at)s\n"
|
||||||
"Django version %(version)s, using settings %(settings)r\n"
|
"Django version %(version)s, using settings %(settings)r\n"
|
||||||
"Development server is running at http://%(addr)s:%(port)s/\n"
|
"Development server is running at http://%(addr)s:%(port)s/\n"
|
||||||
"Quit the server with %(quit_command)s.\n"
|
"Quit the server with %(quit_command)s.\n"
|
||||||
) % {
|
) % {
|
||||||
|
"started_at": datetime.now().strftime('%B %d, %Y - %X'),
|
||||||
"version": self.get_version(),
|
"version": self.get_version(),
|
||||||
"settings": settings.SETTINGS_MODULE,
|
"settings": settings.SETTINGS_MODULE,
|
||||||
"addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr,
|
"addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr,
|
||||||
|
|
|
@ -1054,9 +1054,12 @@ class BaseDatabaseIntrospection(object):
|
||||||
|
|
||||||
def get_primary_key_column(self, cursor, table_name):
|
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):
|
def get_indexes(self, cursor, table_name):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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.introspection import DatabaseIntrospection
|
||||||
from django.db.backends.mysql.validation import DatabaseValidation
|
from django.db.backends.mysql.validation import DatabaseValidation
|
||||||
from django.db.backends.mysql.schema import DatabaseSchemaEditor
|
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.functional import cached_property
|
||||||
from django.utils.safestring import SafeBytes, SafeText
|
from django.utils.safestring import SafeBytes, SafeText
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -392,7 +393,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
if settings_dict['NAME']:
|
if settings_dict['NAME']:
|
||||||
kwargs['db'] = settings_dict['NAME']
|
kwargs['db'] = settings_dict['NAME']
|
||||||
if settings_dict['PASSWORD']:
|
if settings_dict['PASSWORD']:
|
||||||
kwargs['passwd'] = settings_dict['PASSWORD']
|
kwargs['passwd'] = force_str(settings_dict['PASSWORD'])
|
||||||
if settings_dict['HOST'].startswith('/'):
|
if settings_dict['HOST'].startswith('/'):
|
||||||
kwargs['unix_socket'] = settings_dict['HOST']
|
kwargs['unix_socket'] = settings_dict['HOST']
|
||||||
elif settings_dict['HOST']:
|
elif settings_dict['HOST']:
|
||||||
|
|
|
@ -2,7 +2,6 @@ import re
|
||||||
from .base import FIELD_TYPE
|
from .base import FIELD_TYPE
|
||||||
|
|
||||||
from django.db.backends import BaseDatabaseIntrospection
|
from django.db.backends import BaseDatabaseIntrospection
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
|
|
||||||
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
||||||
|
@ -88,15 +87,6 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
||||||
key_columns.extend(cursor.fetchall())
|
key_columns.extend(cursor.fetchall())
|
||||||
return key_columns
|
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):
|
def get_indexes(self, cursor, table_name):
|
||||||
cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(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
|
# Do a two-pass search for indexes: on first pass check which indexes
|
||||||
|
|
|
@ -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.version import get_version
|
||||||
from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
|
from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
|
||||||
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
|
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.log import getLogger
|
||||||
from django.utils.safestring import SafeText, SafeBytes
|
from django.utils.safestring import SafeText, SafeBytes
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -175,7 +176,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
if settings_dict['USER']:
|
if settings_dict['USER']:
|
||||||
conn_params['user'] = settings_dict['USER']
|
conn_params['user'] = settings_dict['USER']
|
||||||
if settings_dict['PASSWORD']:
|
if settings_dict['PASSWORD']:
|
||||||
conn_params['password'] = settings_dict['PASSWORD']
|
conn_params['password'] = force_str(settings_dict['PASSWORD'])
|
||||||
if settings_dict['HOST']:
|
if settings_dict['HOST']:
|
||||||
conn_params['host'] = settings_dict['HOST']
|
conn_params['host'] = settings_dict['HOST']
|
||||||
if settings_dict['PORT']:
|
if settings_dict['PORT']:
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Constants used across the ORM in general.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Separator used to split filter strings apart.
|
||||||
|
LOOKUP_SEP = '__'
|
||||||
|
|
|
@ -8,6 +8,7 @@ import sys
|
||||||
|
|
||||||
from django.core import exceptions
|
from django.core import exceptions
|
||||||
from django.db import connections, router, transaction, IntegrityError
|
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.fields import AutoField
|
||||||
from django.db.models.query_utils import (Q, select_related_descend,
|
from django.db.models.query_utils import (Q, select_related_descend,
|
||||||
deferred_class_factory, InvalidQuery)
|
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
|
Populates prefetched objects caches for a list of results
|
||||||
from a QuerySet
|
from a QuerySet
|
||||||
"""
|
"""
|
||||||
from django.db.models.sql.constants import LOOKUP_SEP
|
|
||||||
|
|
||||||
if len(result_cache) == 0:
|
if len(result_cache) == 0:
|
||||||
return # nothing to do
|
return # nothing to do
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ from django.utils.six.moves import zip
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.backends.util import truncate_name
|
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.query_utils import select_related_descend
|
||||||
from django.db.models.sql.constants import (SINGLE, MULTI, ORDER_DIR,
|
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.datastructures import EmptyResultSet
|
||||||
from django.db.models.sql.expressions import SQLEvaluator
|
from django.db.models.sql.expressions import SQLEvaluator
|
||||||
from django.db.models.sql.query import get_order_dir, Query
|
from django.db.models.sql.query import get_order_dir, Query
|
||||||
|
@ -608,8 +609,12 @@ class SQLCompiler(object):
|
||||||
restricted = False
|
restricted = False
|
||||||
|
|
||||||
for f, model in opts.get_fields_with_model():
|
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,
|
if not select_related_descend(f, restricted, requested,
|
||||||
only_load.get(model or self.query.model)):
|
only_load.get(field_model)):
|
||||||
continue
|
continue
|
||||||
# The "avoid" set is aliases we want to avoid just for this
|
# The "avoid" set is aliases we want to avoid just for this
|
||||||
# particular branch of the recursion. They aren't permanently
|
# particular branch of the recursion. They aren't permanently
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
|
"""
|
||||||
|
Constants specific to the SQL storage portion of the ORM.
|
||||||
|
"""
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import re
|
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([
|
QUERY_TERMS = set([
|
||||||
'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in',
|
'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in',
|
||||||
'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year',
|
'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.
|
# Larger values are slightly faster at the expense of more storage space.
|
||||||
GET_ITERATOR_CHUNK_SIZE = 100
|
GET_ITERATOR_CHUNK_SIZE = 100
|
||||||
|
|
||||||
# Separator used to split filter strings apart.
|
|
||||||
LOOKUP_SEP = '__'
|
|
||||||
|
|
||||||
# Constants to make looking up tuple values clearer.
|
# Constants to make looking up tuple values clearer.
|
||||||
# Join lists (indexes into the tuples that are values in the alias_map
|
# Join lists (indexes into the tuples that are values in the alias_map
|
||||||
# dictionary in the Query class).
|
# dictionary in the Query class).
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.core.exceptions import FieldError
|
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.fields import FieldDoesNotExist
|
||||||
from django.db.models.sql.constants import LOOKUP_SEP
|
|
||||||
|
|
||||||
class SQLEvaluator(object):
|
class SQLEvaluator(object):
|
||||||
def __init__(self, expression, query, allow_joins=True):
|
def __init__(self, expression, query, allow_joins=True):
|
||||||
|
|
|
@ -15,11 +15,12 @@ from django.utils.tree import Node
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.db import connections, DEFAULT_DB_ALIAS
|
from django.db import connections, DEFAULT_DB_ALIAS
|
||||||
from django.db.models import signals
|
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.expressions import ExpressionNode
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
from django.db.models.sql import aggregates as base_aggregates_module
|
from django.db.models.sql import aggregates as base_aggregates_module
|
||||||
from django.db.models.sql.constants import (QUERY_TERMS, LOOKUP_SEP, ORDER_DIR,
|
from django.db.models.sql.constants import (QUERY_TERMS, ORDER_DIR, SINGLE,
|
||||||
SINGLE, ORDER_PATTERN, JoinInfo)
|
ORDER_PATTERN, JoinInfo)
|
||||||
from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
|
from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
|
||||||
from django.db.models.sql.expressions import SQLEvaluator
|
from django.db.models.sql.expressions import SQLEvaluator
|
||||||
from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode,
|
from django.db.models.sql.where import (WhereNode, Constraint, EverythingNode,
|
||||||
|
|
|
@ -3,6 +3,7 @@ Query subclasses which provide extra functionality beyond simple data retrieval.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.core.exceptions import FieldError
|
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.fields import DateField, FieldDoesNotExist
|
||||||
from django.db.models.sql.constants import *
|
from django.db.models.sql.constants import *
|
||||||
from django.db.models.sql.datastructures import Date
|
from django.db.models.sql.datastructures import Date
|
||||||
|
|
|
@ -199,7 +199,7 @@ class CharField(Field):
|
||||||
|
|
||||||
def widget_attrs(self, widget):
|
def widget_attrs(self, widget):
|
||||||
attrs = super(CharField, self).widget_attrs(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.
|
# The HTML attribute is maxlength, not max_length.
|
||||||
attrs.update({'maxlength': str(self.max_length)})
|
attrs.update({'maxlength': str(self.max_length)})
|
||||||
return attrs
|
return attrs
|
||||||
|
|
|
@ -260,10 +260,17 @@ class Input(Widget):
|
||||||
final_attrs['value'] = force_text(self._format_value(value))
|
final_attrs['value'] = force_text(self._format_value(value))
|
||||||
return format_html('<input{0} />', flatatt(final_attrs))
|
return format_html('<input{0} />', flatatt(final_attrs))
|
||||||
|
|
||||||
|
|
||||||
class TextInput(Input):
|
class TextInput(Input):
|
||||||
input_type = 'text'
|
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'
|
input_type = 'password'
|
||||||
|
|
||||||
def __init__(self, attrs=None, render_value=False):
|
def __init__(self, attrs=None, render_value=False):
|
||||||
|
@ -400,9 +407,8 @@ class Textarea(Widget):
|
||||||
flatatt(final_attrs),
|
flatatt(final_attrs),
|
||||||
force_text(value))
|
force_text(value))
|
||||||
|
|
||||||
class DateInput(Input):
|
|
||||||
input_type = 'text'
|
|
||||||
|
|
||||||
|
class DateInput(TextInput):
|
||||||
def __init__(self, attrs=None, format=None):
|
def __init__(self, attrs=None, format=None):
|
||||||
super(DateInput, self).__init__(attrs)
|
super(DateInput, self).__init__(attrs)
|
||||||
if format:
|
if format:
|
||||||
|
@ -431,9 +437,8 @@ class DateInput(Input):
|
||||||
pass
|
pass
|
||||||
return super(DateInput, self)._has_changed(self._format_value(initial), data)
|
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):
|
def __init__(self, attrs=None, format=None):
|
||||||
super(DateTimeInput, self).__init__(attrs)
|
super(DateTimeInput, self).__init__(attrs)
|
||||||
if format:
|
if format:
|
||||||
|
@ -462,9 +467,8 @@ class DateTimeInput(Input):
|
||||||
pass
|
pass
|
||||||
return super(DateTimeInput, self)._has_changed(self._format_value(initial), data)
|
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):
|
def __init__(self, attrs=None, format=None):
|
||||||
super(TimeInput, self).__init__(attrs)
|
super(TimeInput, self).__init__(attrs)
|
||||||
if format:
|
if format:
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
from email.header import Header
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -560,31 +561,44 @@ class HttpResponse(object):
|
||||||
else:
|
else:
|
||||||
__str__ = serialize
|
__str__ = serialize
|
||||||
|
|
||||||
def _convert_to_ascii(self, *values):
|
def _convert_to_charset(self, value, charset, mime_encode=False):
|
||||||
"""Converts all values to ascii strings."""
|
"""Converts headers key/value to ascii/latin1 native strings.
|
||||||
for value in values:
|
|
||||||
if not isinstance(value, six.string_types):
|
`charset` must be 'ascii' or 'latin-1'. If `mime_encode` is True and
|
||||||
value = str(value)
|
`value` value can't be represented in the given charset, MIME-encoding
|
||||||
try:
|
is applied.
|
||||||
if six.PY3:
|
"""
|
||||||
# Ensure string only contains ASCII
|
if not isinstance(value, (bytes, six.text_type)):
|
||||||
value.encode('us-ascii')
|
value = str(value)
|
||||||
|
try:
|
||||||
|
if six.PY3:
|
||||||
|
if isinstance(value, str):
|
||||||
|
# Ensure string is valid in given charset
|
||||||
|
value.encode(charset)
|
||||||
else:
|
else:
|
||||||
if isinstance(value, str):
|
# Convert bytestring using given charset
|
||||||
# Ensure string only contains ASCII
|
value = value.decode(charset)
|
||||||
value.decode('us-ascii')
|
else:
|
||||||
else:
|
if isinstance(value, str):
|
||||||
# Convert unicode to an ASCII string
|
# Ensure string is valid in given charset
|
||||||
value = value.encode('us-ascii')
|
value.decode(charset)
|
||||||
except UnicodeError as e:
|
else:
|
||||||
e.reason += ', HTTP response headers must be in US-ASCII format'
|
# 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
|
raise
|
||||||
if '\n' in value or '\r' in value:
|
if str('\n') in value or str('\r') in value:
|
||||||
raise BadHeaderError("Header values can't contain newlines (got %r)" % value)
|
raise BadHeaderError("Header values can't contain newlines (got %r)" % value)
|
||||||
yield value
|
return value
|
||||||
|
|
||||||
def __setitem__(self, header, 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)
|
self._headers[header.lower()] = (header, value)
|
||||||
|
|
||||||
def __delitem__(self, header):
|
def __delitem__(self, header):
|
||||||
|
|
|
@ -105,7 +105,7 @@ class CsrfViewMiddleware(object):
|
||||||
if getattr(callback, 'csrf_exempt', False):
|
if getattr(callback, 'csrf_exempt', False):
|
||||||
return None
|
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 request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
|
||||||
if getattr(request, '_dont_enforce_csrf_checks', False):
|
if getattr(request, '_dont_enforce_csrf_checks', False):
|
||||||
# Mechanism to turn off CSRF checks for test suite.
|
# Mechanism to turn off CSRF checks for test suite.
|
||||||
|
|
|
@ -66,23 +66,7 @@ def redirect(to, *args, **kwargs):
|
||||||
else:
|
else:
|
||||||
redirect_class = HttpResponseRedirect
|
redirect_class = HttpResponseRedirect
|
||||||
|
|
||||||
# If it's a model, use get_absolute_url()
|
return redirect_class(resolve_url(to, *args, **kwargs))
|
||||||
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)
|
|
||||||
|
|
||||||
def _get_queryset(klass):
|
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)
|
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
|
||||||
return obj_list
|
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
|
||||||
|
|
|
@ -531,11 +531,9 @@ def cycle(parser, token):
|
||||||
The optional flag "silent" can be used to prevent the cycle declaration
|
The optional flag "silent" can be used to prevent the cycle declaration
|
||||||
from returning any value::
|
from returning any value::
|
||||||
|
|
||||||
{% cycle 'row1' 'row2' as rowcolors silent %}{# no value here #}
|
|
||||||
{% for o in some_list %}
|
{% for o in some_list %}
|
||||||
<tr class="{% cycle rowcolors %}">{# first value will be "row1" #}
|
{% cycle 'row1' 'row2' as rowcolors silent %}
|
||||||
...
|
<tr class="{{ rowcolors }}">{% include "subtemplate.html " %}</tr>
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -51,6 +51,13 @@ def clear_context_processors_cache(**kwargs):
|
||||||
context._standard_context_processors = None
|
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)
|
@receiver(setting_changed)
|
||||||
def clear_serializers_cache(**kwargs):
|
def clear_serializers_cache(**kwargs):
|
||||||
if kwargs['setting'] == 'SERIALIZATION_MODULES':
|
if kwargs['setting'] == 'SERIALIZATION_MODULES':
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import stat
|
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.encoding import force_text
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
@ -41,13 +41,16 @@ def safe_join(base, *paths):
|
||||||
paths = [force_text(p) for p in paths]
|
paths = [force_text(p) for p in paths]
|
||||||
final_path = abspathu(join(base, *paths))
|
final_path = abspathu(join(base, *paths))
|
||||||
base_path = abspathu(base)
|
base_path = abspathu(base)
|
||||||
base_path_len = len(base_path)
|
|
||||||
# Ensure final_path starts with base_path (using normcase to ensure we
|
# Ensure final_path starts with base_path (using normcase to ensure we
|
||||||
# don't false-negative on case insensitive operating systems like Windows)
|
# 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,
|
# further, one of the following conditions must be true:
|
||||||
# in which case final_path must be equal to base_path).
|
# a) The next character is the path separator (to prevent conditions like
|
||||||
if not normcase(final_path).startswith(normcase(base_path)) \
|
# safe_join("/dir", "/../d"))
|
||||||
or final_path[base_path_len:base_path_len+1] not in ('', sep):
|
# 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 '
|
raise ValueError('The joined path (%s) is located outside of the base '
|
||||||
'path component (%s)' % (final_path, base_path))
|
'path component (%s)' % (final_path, base_path))
|
||||||
return final_path
|
return final_path
|
||||||
|
|
|
@ -5,8 +5,7 @@ import sys
|
||||||
current_version = sys.version_info
|
current_version = sys.version_info
|
||||||
|
|
||||||
use_workaround = (
|
use_workaround = (
|
||||||
(current_version < (2, 6, 8)) or
|
(current_version < (2, 7, 3)) or
|
||||||
(current_version >= (2, 7) and current_version < (2, 7, 3)) or
|
|
||||||
(current_version >= (3, 0) and current_version < (3, 2, 3))
|
(current_version >= (3, 0) and current_version < (3, 2, 3))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -372,6 +372,9 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def get_date_list_period(self):
|
def get_date_list_period(self):
|
||||||
|
"""
|
||||||
|
Get the aggregation period for the list of dates: 'year', 'month', or 'day'.
|
||||||
|
"""
|
||||||
return self.date_list_period
|
return self.date_list_period
|
||||||
|
|
||||||
def get_date_list(self, queryset, date_type=None):
|
def get_date_list(self, queryset, date_type=None):
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
The documentation in this tree is in plain text files and can be viewed using
|
The documentation in this tree is in plain text files and can be viewed using
|
||||||
any text file viewer.
|
any text file viewer.
|
||||||
|
|
||||||
Technically speaking, it uses ReST (reStructuredText) [1], and the Sphinx
|
It uses ReST (reStructuredText) [1], and the Sphinx documentation system [2].
|
||||||
documentation system [2]. This allows it to be built into other forms for
|
This allows it to be built into other forms for easier viewing and browsing.
|
||||||
easier viewing and browsing.
|
|
||||||
|
|
||||||
To create an HTML version of the docs:
|
To create an HTML version of the docs:
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,3 @@ Indices, glossary and tables
|
||||||
* :ref:`genindex`
|
* :ref:`genindex`
|
||||||
* :ref:`modindex`
|
* :ref:`modindex`
|
||||||
* :ref:`glossary`
|
* :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
|
|
||||||
|
|
|
@ -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
|
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
|
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
|
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
|
like to make should be possible by editing the stylesheet.
|
||||||
:doc:`guide to the CSS used in the admin </obsolete/admin-css>` to get you started.
|
|
||||||
|
|
||||||
What browsers are supported for using the admin?
|
What browsers are supported for using the admin?
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
@ -104,5 +103,5 @@ There *may* be minor stylistic differences between supported browsers—for
|
||||||
example, some browsers may not support rounded corners. These are considered
|
example, some browsers may not support rounded corners. These are considered
|
||||||
acceptable variations in rendering.
|
acceptable variations in rendering.
|
||||||
|
|
||||||
.. _YUI's A-grade: http://yuilibrary.com/yui/docs/tutorials/gbs/
|
.. _YUI's A-grade: http://yuilibrary.com/yui/docs/tutorials/gbs/
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,9 @@ How do I get started?
|
||||||
What are Django's prerequisites?
|
What are Django's prerequisites?
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
Django requires Python_, specifically Python 2.6.5 - 2.7.x. No other Python
|
Django requires Python, specifically Python 2.6.5 - 2.7.x. No other Python
|
||||||
libraries are required for basic Django usage.
|
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 --
|
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
|
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
|
Third-party applications for use with Django are, of course, free to set their
|
||||||
own version requirements.
|
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
|
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
|
(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
|
improvements and optimizations to the Python language since version 2.6.
|
||||||
will help ease the process of dropping support for older Python versions on
|
|
||||||
the road to Python 3.
|
Generally speaking, we don't recommend running Django on Python 3 yet; see
|
||||||
|
below for more.
|
||||||
|
|
||||||
What Python version can I use with Django?
|
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.2 2.4, 2.5, 2.6, 2.7
|
||||||
1.3 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.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?
|
Can I use Django with Python 3?
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Not at the moment. Python 3.0 introduced a number of
|
Django 1.5 introduces experimental support for Python 3.2 and 3.3. However, we
|
||||||
backwards-incompatible changes to the Python language, and although
|
don't yet suggest that you use Django and Python 3 in production.
|
||||||
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).
|
|
||||||
|
|
||||||
In the meantime, Python 2.x releases will be supported and provided
|
Python 3 support should be considered a "preview". It's offered to bootstrap
|
||||||
with bug fixes and security updates by the Python development team, so
|
the transition of the Django ecosystem to Python 3, and to help you start
|
||||||
continuing to use a Python 2.x release during the transition should
|
porting your apps for future Python 3 compatibility. But we're not yet
|
||||||
not present any risk.
|
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)?
|
Will Django run under shared hosting (like TextDrive or Dreamhost)?
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
|
|
|
@ -11,7 +11,7 @@ Then, just do this::
|
||||||
|
|
||||||
>>> from django.db import connection
|
>>> from django.db import connection
|
||||||
>>> connection.queries
|
>>> 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'}]
|
'time': '0.002'}]
|
||||||
|
|
||||||
``connection.queries`` is only available if :setting:`DEBUG` is ``True``.
|
``connection.queries`` is only available if :setting:`DEBUG` is ``True``.
|
||||||
|
|
|
@ -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
|
Many of Django's model fields accept options that they don't do anything
|
||||||
with. For example, you can pass both
|
with. For example, you can pass both
|
||||||
:attr:`~django.db.models.Field.editable` and
|
: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
|
:class:`django.db.models.DateField` and it will simply ignore the
|
||||||
:attr:`~django.db.models.Field.editable` parameter
|
: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.
|
``editable=False``). No error is raised in this case.
|
||||||
|
|
||||||
This behavior simplifies the field classes, because they don't need to
|
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
|
You only need to override this method if you want to preprocess the value
|
||||||
somehow, just before saving. For example, Django's
|
somehow, just before saving. For example, Django's
|
||||||
:class:`~django.db.models.DateTimeField` uses this method to set the attribute
|
: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
|
correctly in the case of :attr:`~django.db.models.DateField.auto_now` or
|
||||||
:attr:`~django.db.models.Field.auto_now_add`.
|
:attr:`~django.db.models.DateField.auto_now_add`.
|
||||||
|
|
||||||
If you do override this method, you must return the value of the attribute at
|
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
|
the end. You should also update the model's attribute if you make any changes
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
Django documentation
|
Django documentation
|
||||||
====================
|
====================
|
||||||
|
|
||||||
.. rubric:: Everything you need to know about Django (and then some).
|
.. rubric:: Everything you need to know about Django.
|
||||||
|
|
||||||
Getting help
|
Getting help
|
||||||
============
|
============
|
||||||
|
|
|
@ -379,6 +379,34 @@ Florian Apolloner
|
||||||
.. _Graz University of Technology: http://tugraz.at/
|
.. _Graz University of Technology: http://tugraz.at/
|
||||||
.. _Ubuntuusers webteam: http://wiki.ubuntuusers.de/ubuntuusers/Webteam
|
.. _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
|
Specialists
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -403,16 +431,6 @@ Ian Kelly
|
||||||
Matt Boersma
|
Matt Boersma
|
||||||
Matt is also responsible for Django's Oracle support.
|
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 Meers`_
|
||||||
Simon discovered Django 0.96 during his Computer Science PhD research and
|
Simon discovered Django 0.96 during his Computer Science PhD research and
|
||||||
has been developing with it full-time ever since. His core code
|
has been developing with it full-time ever since. His core code
|
||||||
|
|
|
@ -264,6 +264,9 @@ these changes.
|
||||||
in 1.4. The backward compatibility will be removed --
|
in 1.4. The backward compatibility will be removed --
|
||||||
``HttpRequest.raw_post_data`` will no longer work.
|
``HttpRequest.raw_post_data`` will no longer work.
|
||||||
|
|
||||||
|
* ``django.contrib.markup`` will be removed following an accelerated
|
||||||
|
deprecation.
|
||||||
|
|
||||||
1.7
|
1.7
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,9 @@ Install Python
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Being a Python Web framework, Django requires Python. It works with any 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,
|
version from 2.6.5 to 2.7. It also features experimental support for versions
|
||||||
Django does not currently work with Python 3.0; see :doc:`the Django FAQ
|
3.2 and 3.3. All these versions of Python include a lightweight database
|
||||||
</faq/install>` for more information on supported Python versions and the 3.0
|
called SQLite_ so you won't need to set up a database just yet.
|
||||||
transition), 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/
|
.. _sqlite: http://sqlite.org/
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ the file ``mysite/news/models.py``::
|
||||||
return self.full_name
|
return self.full_name
|
||||||
|
|
||||||
class Article(models.Model):
|
class Article(models.Model):
|
||||||
pub_date = models.DateTimeField()
|
pub_date = models.DateField()
|
||||||
headline = models.CharField(max_length=200)
|
headline = models.CharField(max_length=200)
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
reporter = models.ForeignKey(Reporter)
|
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}
|
DoesNotExist: Reporter matching query does not exist. Lookup parameters were {'id': 2}
|
||||||
|
|
||||||
# Create an article.
|
# Create an article.
|
||||||
>>> from datetime import datetime
|
>>> from datetime import date
|
||||||
>>> a = Article(pub_date=datetime.now(), headline='Django is cool',
|
>>> a = Article(pub_date=date.today(), headline='Django is cool',
|
||||||
... content='Yeah.', reporter=r)
|
... content='Yeah.', reporter=r)
|
||||||
>>> a.save()
|
>>> a.save()
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ as registering your model in the admin site::
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Article(models.Model):
|
class Article(models.Model):
|
||||||
pub_date = models.DateTimeField()
|
pub_date = models.DateField()
|
||||||
headline = models.CharField(max_length=200)
|
headline = models.CharField(max_length=200)
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
reporter = models.ForeignKey(Reporter)
|
reporter = models.ForeignKey(Reporter)
|
||||||
|
|
|
@ -513,8 +513,9 @@ Here's what happens if a user goes to "/polls/34/" in this system:
|
||||||
further processing.
|
further processing.
|
||||||
|
|
||||||
Now that we've decoupled that, we need to decouple the ``polls.urls``
|
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
|
URLconf by removing the leading "polls/" from each line, removing the
|
||||||
lines registering the admin site. Your ``polls/urls.py`` file should now look like
|
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::
|
this::
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
|
|
|
@ -218,7 +218,7 @@ Read on for details.
|
||||||
First, open the ``polls/urls.py`` URLconf. It looks like this, according to the
|
First, open the ``polls/urls.py`` URLconf. It looks like this, according to the
|
||||||
tutorial so far::
|
tutorial so far::
|
||||||
|
|
||||||
from django.conf.urls import patterns, include, url
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
urlpatterns = patterns('polls.views',
|
urlpatterns = patterns('polls.views',
|
||||||
url(r'^$', 'index'),
|
url(r'^$', 'index'),
|
||||||
|
@ -229,7 +229,7 @@ tutorial so far::
|
||||||
|
|
||||||
Change it like so::
|
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 django.views.generic import DetailView, ListView
|
||||||
from polls.models import Poll
|
from polls.models import Poll
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,7 @@ different needs:
|
||||||
whathaveyou.
|
whathaveyou.
|
||||||
|
|
||||||
* Finally, there's some "specialized" documentation not usually relevant to
|
* Finally, there's some "specialized" documentation not usually relevant to
|
||||||
most developers. This includes the :doc:`release notes </releases/index>`,
|
most developers. This includes the :doc:`release notes </releases/index>` and
|
||||||
:doc:`documentation of obsolete features </obsolete/index>`,
|
|
||||||
:doc:`internals documentation </internals/index>` for those who want to add
|
: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
|
code to Django itself, and a :doc:`few other things that simply don't fit
|
||||||
elsewhere </misc/index>`.
|
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 |
|
@ -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``.
|
|
|
@ -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
|
|
|
@ -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
|
required for projects, in which case there are Mixins and Generic class-based
|
||||||
views.
|
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
|
View
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -20,6 +25,7 @@ View
|
||||||
|
|
||||||
1. :meth:`dispatch()`
|
1. :meth:`dispatch()`
|
||||||
2. :meth:`http_method_not_allowed()`
|
2. :meth:`http_method_not_allowed()`
|
||||||
|
3. :meth:`options()`
|
||||||
|
|
||||||
**Example views.py**::
|
**Example views.py**::
|
||||||
|
|
||||||
|
@ -41,8 +47,20 @@ View
|
||||||
url(r'^mine/$', MyView.as_view(), name='my-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**
|
**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)
|
.. method:: dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
The ``view`` part of the view -- the method that accepts a ``request``
|
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()`,
|
delegated to :meth:`~View.get()`, a ``POST`` to :meth:`~View.post()`,
|
||||||
and so on.
|
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
|
The default implementation also sets ``request``, ``args`` and
|
||||||
``kwargs`` as instance variables, so any method on the view can know
|
``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.
|
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
|
If the view was called with a HTTP method it doesn't support, this
|
||||||
method is called instead.
|
method is called instead.
|
||||||
|
|
||||||
The default implementation returns ``HttpResponseNotAllowed`` with list
|
The default implementation returns ``HttpResponseNotAllowed`` with a
|
||||||
of allowed methods in plain text.
|
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
|
Handles responding to requests for the OPTIONS HTTP verb. Returns a
|
||||||
methods defined directly on the class are documented here, not methods
|
list of the allowed HTTP method names for the view.
|
||||||
defined on superclasses.
|
|
||||||
|
|
||||||
TemplateView
|
TemplateView
|
||||||
------------
|
------------
|
||||||
|
@ -81,6 +103,8 @@ TemplateView
|
||||||
|
|
||||||
**Ancestors (MRO)**
|
**Ancestors (MRO)**
|
||||||
|
|
||||||
|
This view inherits methods and attributes from the following views:
|
||||||
|
|
||||||
* :class:`django.views.generic.base.TemplateView`
|
* :class:`django.views.generic.base.TemplateView`
|
||||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||||
* :class:`django.views.generic.base.View`
|
* :class:`django.views.generic.base.View`
|
||||||
|
@ -116,28 +140,11 @@ TemplateView
|
||||||
url(r'^$', HomePageView.as_view(), name='home'),
|
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**
|
**Context**
|
||||||
|
|
||||||
* ``params``: The dictionary of keyword arguments captured from the URL
|
* ``params``: The dictionary of keyword arguments captured from the URL
|
||||||
pattern that served the view.
|
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
|
RedirectView
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -156,6 +163,8 @@ RedirectView
|
||||||
|
|
||||||
**Ancestors (MRO)**
|
**Ancestors (MRO)**
|
||||||
|
|
||||||
|
This view inherits methods and attributes from the following view:
|
||||||
|
|
||||||
* :class:`django.views.generic.base.View`
|
* :class:`django.views.generic.base.View`
|
||||||
|
|
||||||
**Method Flowchart**
|
**Method Flowchart**
|
||||||
|
@ -194,7 +203,7 @@ RedirectView
|
||||||
url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
|
url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
|
||||||
)
|
)
|
||||||
|
|
||||||
**Methods and Attributes**
|
**Attributes**
|
||||||
|
|
||||||
.. attribute:: url
|
.. attribute:: url
|
||||||
|
|
||||||
|
@ -215,6 +224,8 @@ RedirectView
|
||||||
then the query string is discarded. By default, ``query_string`` is
|
then the query string is discarded. By default, ``query_string`` is
|
||||||
``False``.
|
``False``.
|
||||||
|
|
||||||
|
**Methods**
|
||||||
|
|
||||||
.. method:: get_redirect_url(**kwargs)
|
.. method:: get_redirect_url(**kwargs)
|
||||||
|
|
||||||
Constructs the target URL for redirection.
|
Constructs the target URL for redirection.
|
||||||
|
@ -225,9 +236,3 @@ RedirectView
|
||||||
:attr:`~RedirectView.query_string`. Subclasses may implement any
|
:attr:`~RedirectView.query_string`. Subclasses may implement any
|
||||||
behavior they wish, as long as the method returns a redirect-ready URL
|
behavior they wish, as long as the method returns a redirect-ready URL
|
||||||
string.
|
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.
|
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
Generic date views
|
Generic date views
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Date-based generic views (in the module :mod:`django.views.generic.dates`)
|
.. module:: django.views.generic.dates
|
||||||
are views for displaying drilldown pages for date-based data.
|
|
||||||
|
Date-based generic views, provided in :mod:`django.views.generic.dates`, are
|
||||||
|
views for displaying drilldown pages for date-based data.
|
||||||
|
|
||||||
ArchiveIndexView
|
ArchiveIndexView
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.ArchiveIndexView
|
.. class:: ArchiveIndexView
|
||||||
|
|
||||||
A top-level index page showing the "latest" objects, by date. Objects with
|
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
|
a date in the *future* are not included unless you set ``allow_future`` to
|
||||||
|
@ -36,7 +38,7 @@ ArchiveIndexView
|
||||||
YearArchiveView
|
YearArchiveView
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.YearArchiveView
|
.. class:: YearArchiveView
|
||||||
|
|
||||||
A yearly archive page showing all available months in a given year. Objects
|
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
|
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
|
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
|
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``.
|
``False``.
|
||||||
|
|
||||||
.. method:: get_make_object_list()
|
.. method:: get_make_object_list()
|
||||||
|
|
||||||
Determine if an object list will be returned as part of the context. If
|
Determine if an object list will be returned as part of the context.
|
||||||
``False``, the ``None`` queryset will be used as the object list.
|
Returns :attr:`~YearArchiveView.make_object_list` by default.
|
||||||
|
|
||||||
|
|
||||||
**Context**
|
**Context**
|
||||||
|
|
||||||
|
@ -80,16 +84,18 @@ YearArchiveView
|
||||||
:class:`datetime.datetime<python:datetime.datetime>` objects, in
|
:class:`datetime.datetime<python:datetime.datetime>` objects, in
|
||||||
ascending order.
|
ascending order.
|
||||||
|
|
||||||
* ``year``: A :class:`datetime.date<python:datetime.date>` object
|
* ``year``: A :class:`~datetime.date` object
|
||||||
representing the given year.
|
representing the given year.
|
||||||
|
|
||||||
* ``next_year``: A :class:`datetime.date<python:datetime.date>` object
|
* ``next_year``: A :class:`~datetime.date` object
|
||||||
representing the first day of the next year. If the next year is in the
|
representing the first day of the next year, according to
|
||||||
future, this will be ``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
* ``previous_year``: A :class:`datetime.date<python:datetime.date>` object
|
* ``previous_year``: A :class:`~datetime.date` object
|
||||||
representing the first day of the previous year. Unlike ``next_year``,
|
representing the first day of the previous year, according to
|
||||||
this will never be ``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
**Notes**
|
**Notes**
|
||||||
|
|
||||||
|
@ -98,7 +104,7 @@ YearArchiveView
|
||||||
MonthArchiveView
|
MonthArchiveView
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.MonthArchiveView
|
.. class:: MonthArchiveView
|
||||||
|
|
||||||
A monthly archive page showing all objects in a given month. Objects with a
|
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
|
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
|
:class:`datetime.datetime<python:datetime.datetime>` objects, in
|
||||||
ascending order.
|
ascending order.
|
||||||
|
|
||||||
* ``month``: A :class:`datetime.date<python:datetime.date>` object
|
* ``month``: A :class:`~datetime.date` object
|
||||||
representing the given month.
|
representing the given month.
|
||||||
|
|
||||||
* ``next_month``: A :class:`datetime.date<python:datetime.date>` object
|
* ``next_month``: A :class:`~datetime.date` object
|
||||||
representing the first day of the next month. If the next month is in the
|
representing the first day of the next month, according to
|
||||||
future, this will be ``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
* ``previous_month``: A :class:`datetime.date<python:datetime.date>` object
|
* ``previous_month``: A :class:`~datetime.date` object
|
||||||
representing the first day of the previous month. Unlike ``next_month``,
|
representing the first day of the previous month, according to
|
||||||
this will never be ``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
**Notes**
|
**Notes**
|
||||||
|
|
||||||
|
@ -149,7 +157,7 @@ MonthArchiveView
|
||||||
WeekArchiveView
|
WeekArchiveView
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.WeekArchiveView
|
.. class:: WeekArchiveView
|
||||||
|
|
||||||
A weekly archive page showing all objects in a given week. Objects with a
|
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
|
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
|
:class:`~django.views.generic.dates.BaseDateListView`), the template's
|
||||||
context will be:
|
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.
|
representing the first day of the given week.
|
||||||
|
|
||||||
* ``next_week``: A :class:`datetime.date<python:datetime.date>` object
|
* ``next_week``: A :class:`~datetime.date` object
|
||||||
representing the first day of the next week. If the next week is in the
|
representing the first day of the next week, according to
|
||||||
future, this will be ``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
* ``previous_week``: A :class:`datetime.date<python:datetime.date>` object
|
* ``previous_week``: A :class:`~datetime.date` object
|
||||||
representing the first day of the previous week. Unlike ``next_week``,
|
representing the first day of the previous week, according to
|
||||||
this will never be ``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
**Notes**
|
**Notes**
|
||||||
|
|
||||||
|
@ -193,7 +203,7 @@ WeekArchiveView
|
||||||
DayArchiveView
|
DayArchiveView
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.DayArchiveView
|
.. class:: DayArchiveView
|
||||||
|
|
||||||
A day archive page showing all objects in a given day. Days in the future
|
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,
|
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
|
:class:`~django.views.generic.dates.BaseDateListView`), the template's
|
||||||
context will be:
|
context will be:
|
||||||
|
|
||||||
* ``day``: A :class:`datetime.date<python:datetime.date>` object
|
* ``day``: A :class:`~datetime.date` object
|
||||||
representing the given day.
|
representing the given day.
|
||||||
|
|
||||||
* ``next_day``: A :class:`datetime.date<python:datetime.date>` object
|
* ``next_day``: A :class:`~datetime.date` object
|
||||||
representing the next day. If the next day is in the future, this will be
|
representing the next day, according to
|
||||||
``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
* ``previous_day``: A :class:`datetime.date<python:datetime.date>` object
|
* ``previous_day``: A :class:`~datetime.date` object
|
||||||
representing the previous day. Unlike ``next_day``, this will never be
|
representing the previous day, according to
|
||||||
``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
* ``next_month``: A :class:`datetime.date<python:datetime.date>` object
|
* ``next_month``: A :class:`~datetime.date` object
|
||||||
representing the first day of the next month. If the next month is in the
|
representing the first day of the next month, according to
|
||||||
future, this will be ``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
* ``previous_month``: A :class:`datetime.date<python:datetime.date>` object
|
* ``previous_month``: A :class:`~datetime.date` object
|
||||||
representing the first day of the previous month. Unlike ``next_month``,
|
representing the first day of the previous month, according to
|
||||||
this will never be ``None``.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
**Notes**
|
**Notes**
|
||||||
|
|
||||||
|
@ -246,7 +260,7 @@ DayArchiveView
|
||||||
TodayArchiveView
|
TodayArchiveView
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.TodayArchiveView
|
.. class:: TodayArchiveView
|
||||||
|
|
||||||
A day archive page showing all objects for *today*. This is exactly the
|
A day archive page showing all objects for *today*. This is exactly the
|
||||||
same as :class:`django.views.generic.dates.DayArchiveView`, except today's
|
same as :class:`django.views.generic.dates.DayArchiveView`, except today's
|
||||||
|
@ -271,7 +285,7 @@ TodayArchiveView
|
||||||
DateDetailView
|
DateDetailView
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.DateDetailView
|
.. class:: DateDetailView
|
||||||
|
|
||||||
A page representing an individual object. If the object has a date value in
|
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
|
the future, the view will throw a 404 error by default, unless you set
|
||||||
|
@ -293,6 +307,22 @@ DateDetailView
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
All of the generic views listed above have matching Base* views that only
|
All of the generic views listed above have matching ``Base`` views that
|
||||||
differ in that the they do not include the
|
only differ in that the they do not include the
|
||||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`:
|
||||||
|
|
||||||
|
.. class:: BaseArchiveIndexView
|
||||||
|
|
||||||
|
.. class:: BaseYearArchiveView
|
||||||
|
|
||||||
|
.. class:: BaseMonthArchiveView
|
||||||
|
|
||||||
|
.. class:: BaseWeekArchiveView
|
||||||
|
|
||||||
|
.. class:: BaseDayArchiveView
|
||||||
|
|
||||||
|
.. class:: BaseTodayArchiveView
|
||||||
|
|
||||||
|
.. class:: BaseDateDetailView
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ DetailView
|
||||||
|
|
||||||
**Ancestors (MRO)**
|
**Ancestors (MRO)**
|
||||||
|
|
||||||
|
This view inherits methods and attributes from the following views:
|
||||||
|
|
||||||
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||||
* :class:`django.views.generic.detail.BaseDetailView`
|
* :class:`django.views.generic.detail.BaseDetailView`
|
||||||
|
@ -71,7 +73,9 @@ ListView
|
||||||
objects (usually, but not necessarily a queryset) that the view is
|
objects (usually, but not necessarily a queryset) that the view is
|
||||||
operating upon.
|
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.ListView`
|
||||||
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
||||||
|
@ -90,3 +94,54 @@ ListView
|
||||||
6. :meth:`get_context_data()`
|
6. :meth:`get_context_data()`
|
||||||
7. :meth:`get()`
|
7. :meth:`get()`
|
||||||
8. :meth:`render_to_response()`
|
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.
|
||||||
|
|
|
@ -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 thread-safe operation).
|
||||||
|
|
||||||
A class-based view is deployed into a URL pattern using the
|
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('',
|
urlpatterns = patterns('',
|
||||||
(r'^view/$', MyView.as_view(size=42)),
|
(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
|
is modified, the actions of one user visiting your view could have an
|
||||||
effect on subsequent users visiting the same view.
|
effect on subsequent users visiting the same view.
|
||||||
|
|
||||||
Any argument passed into :meth:`~View.as_view()` will be assigned onto the
|
Any argument passed into :meth:`~django.views.generic.base.View.as_view()` will
|
||||||
instance that is used to service a request. Using the previous example,
|
be assigned onto the instance that is used to service a request. Using the
|
||||||
this means that every request on ``MyView`` is able to use ``self.size``.
|
previous example, this means that every request on ``MyView`` is able to use
|
||||||
|
``self.size``.
|
||||||
|
|
||||||
Base vs Generic views
|
Base vs Generic views
|
||||||
---------------------
|
---------------------
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
Date-based mixins
|
Date-based mixins
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
.. currentmodule:: django.views.generic.dates
|
||||||
|
|
||||||
YearMixin
|
YearMixin
|
||||||
---------
|
---------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.YearMixin
|
.. class:: YearMixin
|
||||||
|
|
||||||
A mixin that can be used to retrieve and provide parsing information for a
|
A mixin that can be used to retrieve and provide parsing information for a
|
||||||
year component of a date.
|
year component of a date.
|
||||||
|
@ -20,29 +21,45 @@ YearMixin
|
||||||
|
|
||||||
.. attribute:: year
|
.. 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.
|
``None``, which means the year will be determined using other means.
|
||||||
|
|
||||||
.. method:: get_year_format()
|
.. method:: get_year_format()
|
||||||
|
|
||||||
Returns the :func:`~time.strftime` format to use when parsing the year. Returns
|
Returns the :func:`~time.strftime` format to use when parsing the
|
||||||
:attr:`YearMixin.year_format` by default.
|
year. Returns :attr:`~YearMixin.year_format` by default.
|
||||||
|
|
||||||
.. method:: get_year()
|
.. method:: get_year()
|
||||||
|
|
||||||
Returns the year for which this view will display data. Tries the
|
Returns the year for which this view will display data, as a string.
|
||||||
following sources, in order:
|
Tries the following sources, in order:
|
||||||
|
|
||||||
* The value of the :attr:`YearMixin.year` attribute.
|
* 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.
|
* The value of the `year` GET query argument.
|
||||||
|
|
||||||
Raises a 404 if no valid year specification can be found.
|
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
|
MonthMixin
|
||||||
----------
|
----------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.MonthMixin
|
.. class:: MonthMixin
|
||||||
|
|
||||||
A mixin that can be used to retrieve and provide parsing information for a
|
A mixin that can be used to retrieve and provide parsing information for a
|
||||||
month component of a date.
|
month component of a date.
|
||||||
|
@ -51,26 +68,26 @@ MonthMixin
|
||||||
|
|
||||||
.. attribute:: month_format
|
.. attribute:: month_format
|
||||||
|
|
||||||
The :func:`~time.strftime` format to use when parsing the month. By default, this is
|
The :func:`~time.strftime` format to use when parsing the month. By
|
||||||
``'%b'``.
|
default, this is ``'%b'``.
|
||||||
|
|
||||||
.. attribute:: month
|
.. 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.
|
``None``, which means the month will be determined using other means.
|
||||||
|
|
||||||
.. method:: get_month_format()
|
.. method:: get_month_format()
|
||||||
|
|
||||||
Returns the :func:`~time.strftime` format to use when parsing the month. Returns
|
Returns the :func:`~time.strftime` format to use when parsing the
|
||||||
:attr:`MonthMixin.month_format` by default.
|
month. Returns :attr:`~MonthMixin.month_format` by default.
|
||||||
|
|
||||||
.. method:: get_month()
|
.. method:: get_month()
|
||||||
|
|
||||||
Returns the month for which this view will display data. Tries the
|
Returns the month for which this view will display data, as a string.
|
||||||
following sources, in order:
|
Tries the following sources, in order:
|
||||||
|
|
||||||
* The value of the :attr:`MonthMixin.month` attribute.
|
* 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.
|
* The value of the `month` GET query argument.
|
||||||
|
|
||||||
Raises a 404 if no valid month specification can be found.
|
Raises a 404 if no valid month specification can be found.
|
||||||
|
@ -78,20 +95,23 @@ MonthMixin
|
||||||
.. method:: get_next_month(date)
|
.. method:: get_next_month(date)
|
||||||
|
|
||||||
Returns a date object containing the first day of the month after the
|
Returns a date object containing the first day of the month after the
|
||||||
date provided. Returns ``None`` if mixed with a view that sets
|
date provided. This function can also return ``None`` or raise an
|
||||||
``allow_future = False``, and the next month is in the future. If
|
:class:`~django.http.Http404` exception, depending on the values of
|
||||||
``allow_empty = False``, returns the next month that contains data.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
.. method:: get_prev_month(date)
|
.. method:: get_prev_month(date)
|
||||||
|
|
||||||
Returns a date object containing the first day of the month before the
|
Returns a date object containing the first day of the month before the
|
||||||
date provided. If ``allow_empty = False``, returns the previous month
|
date provided. This function can also return ``None`` or raise an
|
||||||
that contained data.
|
:class:`~django.http.Http404` exception, depending on the values of
|
||||||
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
DayMixin
|
DayMixin
|
||||||
--------
|
--------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.DayMixin
|
.. class:: DayMixin
|
||||||
|
|
||||||
A mixin that can be used to retrieve and provide parsing information for a
|
A mixin that can be used to retrieve and provide parsing information for a
|
||||||
day component of a date.
|
day component of a date.
|
||||||
|
@ -100,46 +120,50 @@ DayMixin
|
||||||
|
|
||||||
.. attribute:: day_format
|
.. attribute:: day_format
|
||||||
|
|
||||||
The :func:`~time.strftime` format to use when parsing the day. By default, this is
|
The :func:`~time.strftime` format to use when parsing the day. By
|
||||||
``'%d'``.
|
default, this is ``'%d'``.
|
||||||
|
|
||||||
.. attribute:: day
|
.. 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.
|
``None``, which means the day will be determined using other means.
|
||||||
|
|
||||||
.. method:: get_day_format()
|
.. method:: get_day_format()
|
||||||
|
|
||||||
Returns the :func:`~time.strftime` format to use when parsing the day. Returns
|
Returns the :func:`~time.strftime` format to use when parsing the day.
|
||||||
:attr:`DayMixin.day_format` by default.
|
Returns :attr:`~DayMixin.day_format` by default.
|
||||||
|
|
||||||
.. method:: get_day()
|
.. method:: get_day()
|
||||||
|
|
||||||
Returns the day for which this view will display data. Tries the
|
Returns the day for which this view will display data, as a string.
|
||||||
following sources, in order:
|
Tries the following sources, in order:
|
||||||
|
|
||||||
* The value of the :attr:`DayMixin.day` attribute.
|
* 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.
|
* The value of the `day` GET query argument.
|
||||||
|
|
||||||
Raises a 404 if no valid day specification can be found.
|
Raises a 404 if no valid day specification can be found.
|
||||||
|
|
||||||
.. method:: get_next_day(date)
|
.. method:: get_next_day(date)
|
||||||
|
|
||||||
Returns a date object containing the next day after the date provided.
|
Returns a date object containing the next valid day after the date
|
||||||
Returns ``None`` if mixed with a view that sets ``allow_future = False``,
|
provided. This function can also return ``None`` or raise an
|
||||||
and the next day is in the future. If ``allow_empty = False``, returns
|
:class:`~django.http.Http404` exception, depending on the values of
|
||||||
the next day that contains data.
|
:attr:`~BaseDateListView.allow_empty` and
|
||||||
|
:attr:`~DateMixin.allow_future`.
|
||||||
|
|
||||||
.. method:: get_prev_day(date)
|
.. method:: get_prev_day(date)
|
||||||
|
|
||||||
Returns a date object containing the previous day. If
|
Returns a date object containing the previous valid day. This function
|
||||||
``allow_empty = False``, returns the previous day that contained data.
|
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
|
WeekMixin
|
||||||
---------
|
---------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.WeekMixin
|
.. class:: WeekMixin
|
||||||
|
|
||||||
A mixin that can be used to retrieve and provide parsing information for a
|
A mixin that can be used to retrieve and provide parsing information for a
|
||||||
week component of a date.
|
week component of a date.
|
||||||
|
@ -148,23 +172,24 @@ WeekMixin
|
||||||
|
|
||||||
.. attribute:: week_format
|
.. attribute:: week_format
|
||||||
|
|
||||||
The :func:`~time.strftime` format to use when parsing the week. By default, this is
|
The :func:`~time.strftime` format to use when parsing the week. By
|
||||||
``'%U'``.
|
default, this is ``'%U'``, which means the week starts on Sunday. Set
|
||||||
|
it to ``'%W'`` if your week starts on Monday.
|
||||||
|
|
||||||
.. attribute:: week
|
.. 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.
|
``None``, which means the week will be determined using other means.
|
||||||
|
|
||||||
.. method:: get_week_format()
|
.. method:: get_week_format()
|
||||||
|
|
||||||
Returns the :func:`~time.strftime` format to use when parsing the week. Returns
|
Returns the :func:`~time.strftime` format to use when parsing the
|
||||||
:attr:`WeekMixin.week_format` by default.
|
week. Returns :attr:`~WeekMixin.week_format` by default.
|
||||||
|
|
||||||
.. method:: get_week()
|
.. method:: get_week()
|
||||||
|
|
||||||
Returns the week for which this view will display data. Tries the
|
Returns the week for which this view will display data, as a string.
|
||||||
following sources, in order:
|
Tries the following sources, in order:
|
||||||
|
|
||||||
* The value of the :attr:`WeekMixin.week` attribute.
|
* The value of the :attr:`WeekMixin.week` attribute.
|
||||||
* The value of the `week` argument captured in the URL pattern
|
* 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.
|
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
|
DateMixin
|
||||||
---------
|
---------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.DateMixin
|
.. class:: DateMixin
|
||||||
|
|
||||||
A mixin class providing common behavior for all date-based views.
|
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
|
The name of the ``DateField`` or ``DateTimeField`` in the
|
||||||
``QuerySet``'s model that the date-based archive should use to
|
``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
|
When :doc:`time zone support </topics/i18n/timezones>` is enabled and
|
||||||
``date_field`` is a ``DateTimeField``, dates are assumed to be in the
|
``date_field`` is a ``DateTimeField``, dates are assumed to be in the
|
||||||
|
@ -210,26 +250,26 @@ DateMixin
|
||||||
.. method:: get_date_field()
|
.. method:: get_date_field()
|
||||||
|
|
||||||
Returns the name of the field that contains the date data that this
|
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()
|
.. method:: get_allow_future()
|
||||||
|
|
||||||
Determine whether to include "future" objects on this page, where
|
Determine whether to include "future" objects on this page, where
|
||||||
"future" means objects in which the field specified in ``date_field``
|
"future" means objects in which the field specified in ``date_field``
|
||||||
is greater than the current date/time. Returns
|
is greater than the current date/time. Returns
|
||||||
:attr:`DateMixin.allow_future` by default.
|
:attr:`~DateMixin.allow_future` by default.
|
||||||
|
|
||||||
BaseDateListView
|
BaseDateListView
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
.. class:: django.views.generic.dates.BaseDateListView
|
.. class:: BaseDateListView
|
||||||
|
|
||||||
A base class that provides common behavior for all date-based views. There
|
A base class that provides common behavior for all date-based views. There
|
||||||
won't normally be a reason to instantiate
|
won't normally be a reason to instantiate
|
||||||
:class:`~django.views.generic.dates.BaseDateListView`; instantiate one of
|
:class:`~django.views.generic.dates.BaseDateListView`; instantiate one of
|
||||||
the subclasses instead.
|
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
|
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
|
``self.date_list`` will contain the list of dates for which data is
|
||||||
available.
|
available.
|
||||||
|
@ -245,10 +285,18 @@ BaseDateListView
|
||||||
|
|
||||||
A boolean specifying whether to display the page if no objects are
|
A boolean specifying whether to display the page if no objects are
|
||||||
available. If this is ``True`` and no objects are available, the view
|
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
|
will display an empty page instead of raising a 404.
|
||||||
is ``False``.
|
|
||||||
|
|
||||||
.. 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``,
|
Returns a 3-tuple containing (``date_list``, ``object_list``,
|
||||||
``extra_context``).
|
``extra_context``).
|
||||||
|
@ -265,10 +313,17 @@ BaseDateListView
|
||||||
``lookup``. Enforces any restrictions on the queryset, such as
|
``lookup``. Enforces any restrictions on the queryset, such as
|
||||||
``allow_empty`` and ``allow_future``.
|
``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
|
Returns the aggregation period for ``date_list``. Returns
|
||||||
``queryset`` contains entries. For example, ``get_date_list(qs,
|
:attr:`~BaseDateListView.date_list_period` by default.
|
||||||
'year')`` will return the list of years for which ``qs`` has entries.
|
|
||||||
See :meth:`~django.db.models.query.QuerySet.dates()` for the
|
.. method:: get_date_list(queryset, date_type=None)
|
||||||
ways that the ``date_type`` argument can be used.
|
|
||||||
|
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.
|
||||||
|
|
|
@ -86,7 +86,8 @@ MultipleObjectMixin
|
||||||
|
|
||||||
.. method:: get_queryset()
|
.. 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)
|
.. method:: paginate_queryset(queryset, page_size)
|
||||||
|
|
||||||
|
|
|
@ -9,16 +9,17 @@ ContextMixin
|
||||||
|
|
||||||
.. versionadded:: 1.5
|
.. versionadded:: 1.5
|
||||||
|
|
||||||
**classpath**
|
|
||||||
|
|
||||||
``django.views.generic.base.ContextMixin``
|
|
||||||
|
|
||||||
**Methods**
|
**Methods**
|
||||||
|
|
||||||
.. method:: get_context_data(**kwargs)
|
.. method:: get_context_data(**kwargs)
|
||||||
|
|
||||||
Returns a dictionary representing the template context. The keyword
|
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
|
The template context of all class-based generic views include a
|
||||||
``view`` variable that points to the ``View`` instance.
|
``view`` variable that points to the ``View`` instance.
|
||||||
|
@ -42,7 +43,13 @@ TemplateResponseMixin
|
||||||
suitable context. The template to use is configurable and can be
|
suitable context. The template to use is configurable and can be
|
||||||
further customized by subclasses.
|
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
|
.. attribute:: response_class
|
||||||
|
|
||||||
|
@ -57,12 +64,14 @@ TemplateResponseMixin
|
||||||
instantiation, create a ``TemplateResponse`` subclass and assign it to
|
instantiation, create a ``TemplateResponse`` subclass and assign it to
|
||||||
``response_class``.
|
``response_class``.
|
||||||
|
|
||||||
|
**Methods**
|
||||||
|
|
||||||
.. method:: render_to_response(context, **response_kwargs)
|
.. method:: render_to_response(context, **response_kwargs)
|
||||||
|
|
||||||
Returns a ``self.response_class`` instance.
|
Returns a ``self.response_class`` instance.
|
||||||
|
|
||||||
If any keyword arguments are provided, they will be
|
If any keyword arguments are provided, they will be passed to the
|
||||||
passed to the constructor of the response class.
|
constructor of the response class.
|
||||||
|
|
||||||
Calls :meth:`~TemplateResponseMixin.get_template_names()` to obtain the
|
Calls :meth:`~TemplateResponseMixin.get_template_names()` to obtain the
|
||||||
list of template names that will be searched looking for an existent
|
list of template names that will be searched looking for an existent
|
||||||
|
|
|
@ -32,11 +32,11 @@ A simple example is the best illustration of this. Suppose we have the
|
||||||
following model, which would represent entries in a Weblog::
|
following model, which would represent entries in a Weblog::
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Entry(models.Model):
|
class Entry(models.Model):
|
||||||
title = models.CharField(maxlength=250)
|
title = models.CharField(maxlength=250)
|
||||||
body = models.TextField()
|
body = models.TextField()
|
||||||
pub_date = models.DateTimeField()
|
pub_date = models.DateField()
|
||||||
enable_comments = models.BooleanField()
|
enable_comments = models.BooleanField()
|
||||||
|
|
||||||
Now, suppose that we want the following steps to be applied whenever a
|
Now, suppose that we want the following steps to be applied whenever a
|
||||||
|
@ -55,11 +55,11 @@ Accomplishing this is fairly straightforward and requires very little
|
||||||
code::
|
code::
|
||||||
|
|
||||||
from django.contrib.comments.moderation import CommentModerator, moderator
|
from django.contrib.comments.moderation import CommentModerator, moderator
|
||||||
|
|
||||||
class EntryModerator(CommentModerator):
|
class EntryModerator(CommentModerator):
|
||||||
email_notification = True
|
email_notification = True
|
||||||
enable_field = 'enable_comments'
|
enable_field = 'enable_comments'
|
||||||
|
|
||||||
moderator.register(Entry, EntryModerator)
|
moderator.register(Entry, EntryModerator)
|
||||||
|
|
||||||
The :class:`CommentModerator` class pre-defines a number of useful moderation
|
The :class:`CommentModerator` class pre-defines a number of useful moderation
|
||||||
|
|
|
@ -187,6 +187,14 @@ The ``ContentTypeManager``
|
||||||
probably won't ever need to call this method yourself; Django will call
|
probably won't ever need to call this method yourself; Django will call
|
||||||
it automatically when it's needed.
|
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])
|
.. method:: get_for_model(model[, for_concrete_model=True])
|
||||||
|
|
||||||
Takes either a model class or an instance of a model, and returns the
|
Takes either a model class or an instance of a model, and returns the
|
||||||
|
|
|
@ -155,7 +155,8 @@ or the
|
||||||
:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names()`
|
:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names()`
|
||||||
method, which are documented in the
|
method, which are documented in the
|
||||||
:class:`~django.views.generic.base.TemplateResponseMixin` documentation. 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
|
This template expects a ``wizard`` object that has various items attached to
|
||||||
it:
|
it:
|
||||||
|
@ -238,6 +239,65 @@ wizard's :meth:`as_view` method takes a list of your
|
||||||
(r'^contact/$', ContactWizard.as_view([ContactForm1, ContactForm2])),
|
(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:
|
.. _wizardview-advanced-methods:
|
||||||
|
|
||||||
Advanced ``WizardView`` methods
|
Advanced ``WizardView`` methods
|
||||||
|
|
|
@ -959,15 +959,15 @@ Ubuntu & Debian GNU/Linux
|
||||||
Ubuntu
|
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
|
.. code-block:: bash
|
||||||
|
|
||||||
$ sudo apt-get install binutils gdal-bin libproj-dev postgresql-9.1-postgis \
|
$ sudo apt-get install binutils gdal-bin libproj-dev \
|
||||||
postgresql-server-dev-9.1 python-psycopg2
|
postgresql-9.1-postgis postgresql-server-dev-9.1 python-psycopg2
|
||||||
|
|
||||||
.. _ubuntu10:
|
.. _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.
|
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
|
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
|
.. code-block:: bash
|
||||||
|
|
||||||
|
|
|
@ -674,8 +674,8 @@ __ http://spatialreference.org/ref/epsg/32140/
|
||||||
.. admonition:: Raw queries
|
.. admonition:: Raw queries
|
||||||
|
|
||||||
When using :doc:`raw queries </topics/db/sql>`, you should generally wrap
|
When using :doc:`raw queries </topics/db/sql>`, you should generally wrap
|
||||||
your geometry fields with the ``asText()`` SQL function so as the field
|
your geometry fields with the ``asText()`` SQL function (or ``ST_AsText``
|
||||||
value will be recognized by GEOS::
|
for PostGIS) so as the field value will be recognized by GEOS::
|
||||||
|
|
||||||
City.objects.raw('SELECT id, name, asText(point) from myapp_city')
|
City.objects.raw('SELECT id, name, asText(point) from myapp_city')
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@ django.contrib.markup
|
||||||
.. module:: django.contrib.markup
|
.. module:: django.contrib.markup
|
||||||
:synopsis: A collection of template filters that implement common markup languages.
|
: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
|
Django provides template filters that implement the following markup
|
||||||
languages:
|
languages:
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,16 @@ The messages framework
|
||||||
.. module:: django.contrib.messages
|
.. module:: django.contrib.messages
|
||||||
:synopsis: Provides cookie- and session-based temporary message storage.
|
:synopsis: Provides cookie- and session-based temporary message storage.
|
||||||
|
|
||||||
Quite commonly in web applications, you may need to display a one-time
|
Quite commonly in web applications, you need to display a one-time
|
||||||
notification message (also know as "flash message") to the user after
|
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
|
processing a form or some other types of user input.
|
||||||
full support for cookie- and session-based messaging, for both anonymous and
|
|
||||||
authenticated users. The messages framework allows you to temporarily store
|
For this, Django provides full support for cookie- and session-based
|
||||||
messages in one request and retrieve them for display in a subsequent request
|
messaging, for both anonymous and authenticated users. The messages framework
|
||||||
(usually the next one). Every message is tagged with a specific ``level`` that
|
allows you to temporarily store messages in one request and retrieve them for
|
||||||
determines its priority (e.g., ``info``, ``warning``, or ``error``).
|
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
|
Enabling messages
|
||||||
=================
|
=================
|
||||||
|
@ -20,32 +22,27 @@ Enabling messages
|
||||||
Messages are implemented through a :doc:`middleware </ref/middleware>`
|
Messages are implemented through a :doc:`middleware </ref/middleware>`
|
||||||
class and corresponding :doc:`context processor </ref/templates/api>`.
|
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
|
* ``'django.contrib.messages'`` is in :setting:`INSTALLED_APPS`.
|
||||||
it contains ``'django.contrib.messages.middleware.MessageMiddleware'``.
|
|
||||||
|
|
||||||
If you are using a :ref:`storage backend <message-storage-backends>` that
|
* :setting:`MIDDLEWARE_CLASSES` contains
|
||||||
relies on :doc:`sessions </topics/http/sessions>` (the default),
|
``'django.contrib.sessions.middleware.SessionMiddleware'`` and
|
||||||
``'django.contrib.sessions.middleware.SessionMiddleware'`` must be
|
``'django.contrib.messages.middleware.MessageMiddleware'``.
|
||||||
enabled and appear before ``MessageMiddleware`` in your
|
|
||||||
|
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`.
|
:setting:`MIDDLEWARE_CLASSES`.
|
||||||
|
|
||||||
* Edit the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting and make sure
|
* :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains
|
||||||
it contains ``'django.contrib.messages.context_processors.messages'``.
|
``'django.contrib.messages.context_processors.messages'``.
|
||||||
|
|
||||||
* Add ``'django.contrib.messages'`` to your :setting:`INSTALLED_APPS`
|
If you don't want to use messages, you can remove
|
||||||
setting
|
``'django.contrib.messages'`` from your :setting:`INSTALLED_APPS`, the
|
||||||
|
``MessageMiddleware`` line from :setting:`MIDDLEWARE_CLASSES`, and the
|
||||||
The default ``settings.py`` created by ``django-admin.py startproject`` has
|
``messages`` context processor from :setting:`TEMPLATE_CONTEXT_PROCESSORS`.
|
||||||
``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`.
|
|
||||||
|
|
||||||
Configuring the message engine
|
Configuring the message engine
|
||||||
==============================
|
==============================
|
||||||
|
@ -56,34 +53,35 @@ Storage backends
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The messages framework can use different backends to store temporary messages.
|
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'``
|
.. class:: django.contrib.messages.storage.cookie.CookieStorage
|
||||||
This class stores all messages inside of the request's session. It
|
|
||||||
requires Django's ``contrib.sessions`` application.
|
|
||||||
|
|
||||||
``'django.contrib.messages.storage.cookie.CookieStorage'``
|
|
||||||
This class stores the message data in a cookie (signed with a secret hash
|
This class stores the message data in a cookie (signed with a secret hash
|
||||||
to prevent manipulation) to persist notifications across requests. Old
|
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'``
|
.. class:: django.contrib.messages.storage.fallback.FallbackStorage
|
||||||
This is the default storage class.
|
|
||||||
|
|
||||||
This class first uses CookieStorage for all messages, falling back to using
|
This class first uses ``CookieStorage``, and falls back to using
|
||||||
SessionStorage for the messages that could not fit in a single cookie.
|
``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
|
This behavior avoids writing to the session whenever possible. It should
|
||||||
``contrib.sessions`` application.
|
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
|
To write your own storage class, subclass the ``BaseStorage`` class in
|
||||||
``django.contrib.messages.storage.base`` and implement the ``_get`` and
|
``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
|
messages by type so they can be filtered or displayed differently in views and
|
||||||
templates.
|
templates.
|
||||||
|
|
||||||
The built-in levels (which can be imported from ``django.contrib.messages``
|
The built-in levels, which can be imported from ``django.contrib.messages``
|
||||||
directly) are:
|
directly, are:
|
||||||
|
|
||||||
=========== ========
|
=========== ========
|
||||||
Constant Purpose
|
Constant Purpose
|
||||||
|
|
|
@ -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::
|
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'
|
'%Y-%m-%d', # '2006-10-25'
|
||||||
'%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
|
'%m/%d/%Y', # '10/25/2006'
|
||||||
'%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
|
'%m/%d/%y', # '10/25/06'
|
||||||
'%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
|
|
||||||
'%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
|
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``
|
``DateTimeField``
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
@ -842,7 +852,7 @@ Slightly complex built-in ``Field`` classes
|
||||||
``MultiValueField``
|
``MultiValueField``
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. class:: MultiValueField(**kwargs)
|
.. class:: MultiValueField(fields=(), **kwargs)
|
||||||
|
|
||||||
* Default widget: ``TextInput``
|
* Default widget: ``TextInput``
|
||||||
* Empty value: ``''`` (an empty string)
|
* Empty value: ``''`` (an empty string)
|
||||||
|
@ -851,22 +861,39 @@ Slightly complex built-in ``Field`` classes
|
||||||
as an argument to the ``MultiValueField``.
|
as an argument to the ``MultiValueField``.
|
||||||
* Error message keys: ``required``, ``invalid``
|
* Error message keys: ``required``, ``invalid``
|
||||||
|
|
||||||
This abstract field (must be subclassed) aggregates the logic of multiple
|
Aggregates the logic of multiple fields that together produce a single
|
||||||
fields. Subclasses should not have to implement clean(). Instead, they must
|
value.
|
||||||
implement compress(), which takes a list of valid values and returns a
|
|
||||||
"compressed" version of those values -- a single value. For example,
|
This field is abstract and must be subclassed. In contrast with the
|
||||||
:class:`SplitDateTimeField` is a subclass which combines a time field and
|
single-value fields, subclasses of :class:`MultiValueField` must not
|
||||||
a date field into a datetime object.
|
implement :meth:`~django.forms.Field.clean` but instead - implement
|
||||||
|
:meth:`~MultiValueField.compress`.
|
||||||
|
|
||||||
Takes one extra required argument:
|
Takes one extra required argument:
|
||||||
|
|
||||||
.. attribute:: fields
|
.. attribute:: fields
|
||||||
|
|
||||||
A list of fields which are cleaned into a single field. Each value in
|
A tuple of fields whose values are cleaned and subsequently combined
|
||||||
``clean`` is cleaned by the corresponding field in ``fields`` -- the first
|
into a single value. Each value of the field is cleaned by the
|
||||||
value is cleaned by the first field, the second value is cleaned by
|
corresponding field in ``fields`` -- the first value is cleaned by the
|
||||||
the second field, etc. Once all fields are cleaned, the list of clean
|
first field, the second value is cleaned by the second field, etc.
|
||||||
values is "compressed" into a single value.
|
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``
|
``SplitDateTimeField``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -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
|
handles the rendering of the HTML, and the extraction of data from a GET/POST
|
||||||
dictionary that corresponds to the widget.
|
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
|
Specifying widgets
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -95,15 +105,23 @@ choices are inherent to the model and not just the representational widget.
|
||||||
Customizing widget instances
|
Customizing widget instances
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
When Django renders a widget as HTML, it only renders the bare minimum
|
When Django renders a widget as HTML, it only renders very minimal markup -
|
||||||
HTML - Django doesn't add a class definition, or any other widget-specific
|
Django doesn't add class names, or any other widget-specific attributes. This
|
||||||
attributes. This means that all :class:`TextInput` widgets will appear the same
|
means, for example, that all :class:`TextInput` widgets will appear the same
|
||||||
on your Web page.
|
on your Web pages.
|
||||||
|
|
||||||
If you want to make one widget look different to another, you need to
|
There are two ways to customize widgets: :ref:`per widget instance
|
||||||
specify additional attributes for each widget. When you specify a
|
<styling-widget-instances>` and :ref:`per widget class <styling-widget-classes>`.
|
||||||
widget, you can provide a list of attributes that will be added to the
|
|
||||||
rendered HTML for the widget.
|
.. _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::
|
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
|
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
|
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
|
'name' widget to have some special CSS class. It is also possible to specify
|
||||||
:attr:`Widget.attrs` argument when creating the widget:
|
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::
|
||||||
For example::
|
|
||||||
|
|
||||||
class CommentForm(forms.Form):
|
class CommentForm(forms.Form):
|
||||||
name = forms.CharField(
|
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>Url:</th><td><input type="text" name="url"/></td></tr>
|
||||||
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></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
|
With widgets, it is possible to add media (``css`` and ``javascript``)
|
||||||
commonly used groups of widgets:
|
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
|
.. 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
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -171,6 +205,74 @@ commonly used groups of widgets:
|
||||||
>>> name.render('name', 'A name')
|
>>> name.render('name', 'A name')
|
||||||
u'<input title="Your name" type="text" name="name" value="A name" size="10" />'
|
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``
|
``TextInput``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -204,39 +306,8 @@ commonly used groups of widgets:
|
||||||
|
|
||||||
Hidden input: ``<input type='hidden' ...>``
|
Hidden input: ``<input type='hidden' ...>``
|
||||||
|
|
||||||
``MultipleHiddenInput``
|
Note that there also is a :class:`MultipleHiddenInput` widget that
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
encapsulates a set of hidden input elements.
|
||||||
|
|
||||||
.. 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.
|
|
||||||
|
|
||||||
``DateInput``
|
``DateInput``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
@ -245,7 +316,7 @@ commonly used groups of widgets:
|
||||||
|
|
||||||
Date input as a simple text box: ``<input type='text' ...>``
|
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
|
.. attribute:: DateInput.format
|
||||||
|
|
||||||
|
@ -262,7 +333,7 @@ commonly used groups of widgets:
|
||||||
|
|
||||||
Date/time input as a simple text box: ``<input type='text' ...>``
|
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
|
.. attribute:: DateTimeInput.format
|
||||||
|
|
||||||
|
@ -279,7 +350,7 @@ commonly used groups of widgets:
|
||||||
|
|
||||||
Time input as a simple text box: ``<input type='text' ...>``
|
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
|
.. attribute:: TimeInput.format
|
||||||
|
|
||||||
|
@ -296,6 +367,11 @@ commonly used groups of widgets:
|
||||||
|
|
||||||
Text area: ``<textarea>...</textarea>``
|
Text area: ``<textarea>...</textarea>``
|
||||||
|
|
||||||
|
.. _selector-widgets:
|
||||||
|
|
||||||
|
Selector and checkbox widgets
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
``CheckboxInput``
|
``CheckboxInput``
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -439,6 +515,50 @@ commonly used groups of widgets:
|
||||||
...
|
...
|
||||||
</ul>
|
</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``
|
``MultiWidget``
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -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
|
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.
|
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``
|
``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
|
this with functions from the Python ``datetime`` module to limit choices of
|
||||||
objects by date. For example::
|
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
|
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`
|
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,
|
object for more :ref:`complex queries <complex-lookups-with-q>`. However,
|
||||||
|
|
|
@ -135,7 +135,7 @@ access to more than a single field::
|
||||||
raise ValidationError('Draft entries may not have a publication date.')
|
raise ValidationError('Draft entries may not have a publication date.')
|
||||||
# Set the pub_date for published items if it hasn't been set already.
|
# Set the pub_date for published items if it hasn't been set already.
|
||||||
if self.status == 'published' and self.pub_date is None:
|
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
|
Any :exc:`~django.core.exceptions.ValidationError` exceptions raised by
|
||||||
``Model.clean()`` will be stored in a special key error dictionary key,
|
``Model.clean()`` will be stored in a special key error dictionary key,
|
||||||
|
|
|
@ -31,6 +31,9 @@ You can evaluate a ``QuerySet`` in the following ways:
|
||||||
for e in Entry.objects.all():
|
for e in Entry.objects.all():
|
||||||
print(e.headline)
|
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
|
* **Slicing.** As explained in :ref:`limiting-querysets`, a ``QuerySet`` can
|
||||||
be sliced, using Python's array-slicing syntax. Slicing an unevaluated
|
be sliced, using Python's array-slicing syntax. Slicing an unevaluated
|
||||||
``QuerySet`` usually returns another unevaluated ``QuerySet``, but Django
|
``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
|
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
|
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:
|
.. _pickling QuerySets:
|
||||||
|
|
||||||
|
@ -1047,7 +1050,7 @@ defer
|
||||||
In some complex data-modeling situations, your models might contain a lot of
|
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),
|
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
|
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
|
if you need those particular fields when you initially fetch the data, you can
|
||||||
tell Django not to retrieve them from the database.
|
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``
|
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
|
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
|
possible, but it *does* execute nearly the same query as a normal
|
||||||
:meth:`.QuerySet.exists` is faster than ``bool(some_query_set)``, but not by
|
:class:`.QuerySet` query.
|
||||||
a large degree. If ``some_query_set`` has not yet been evaluated, but you know
|
|
||||||
|
: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
|
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
|
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
|
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,
|
You can use ``range`` anywhere you can use ``BETWEEN`` in SQL — for dates,
|
||||||
numbers and even characters.
|
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
|
.. fieldlookup:: year
|
||||||
|
|
||||||
year
|
year
|
||||||
|
@ -1958,7 +2003,7 @@ Example::
|
||||||
|
|
||||||
SQL equivalent::
|
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.)
|
(The exact SQL syntax varies for each database engine.)
|
||||||
|
|
||||||
|
|
|
@ -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`
|
This is used by the :func:`~django.contrib.auth.decorators.login_required`
|
||||||
decorator, for example.
|
decorator, for example.
|
||||||
|
|
||||||
.. _`note on LOGIN_REDIRECT_URL setting`:
|
.. versionchanged:: 1.5
|
||||||
|
|
||||||
.. note::
|
This setting now also accepts view function names and
|
||||||
You can use :func:`~django.core.urlresolvers.reverse_lazy` to reference
|
:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
|
||||||
URLs by their name instead of providing a hardcoded value. Assuming a
|
configuration duplication since you no longer have to define the URL in two
|
||||||
``urls.py`` with an URLpattern named ``home``::
|
places (``settings`` and URLconf).
|
||||||
|
For backward compatibility reasons the default remains unchanged.
|
||||||
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`.
|
|
||||||
|
|
||||||
.. setting:: LOGIN_URL
|
.. setting:: LOGIN_URL
|
||||||
|
|
||||||
|
@ -1334,8 +1322,13 @@ Default: ``'/accounts/login/'``
|
||||||
The URL where requests are redirected for login, especially when using the
|
The URL where requests are redirected for login, especially when using the
|
||||||
:func:`~django.contrib.auth.decorators.login_required` decorator.
|
:func:`~django.contrib.auth.decorators.login_required` decorator.
|
||||||
|
|
||||||
.. note::
|
.. versionchanged:: 1.5
|
||||||
See the `note on LOGIN_REDIRECT_URL setting`_
|
|
||||||
|
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
|
.. setting:: LOGOUT_URL
|
||||||
|
|
||||||
|
@ -1346,9 +1339,6 @@ Default: ``'/accounts/logout/'``
|
||||||
|
|
||||||
LOGIN_URL counterpart.
|
LOGIN_URL counterpart.
|
||||||
|
|
||||||
.. note::
|
|
||||||
See the `note on LOGIN_REDIRECT_URL setting`_
|
|
||||||
|
|
||||||
.. setting:: MANAGERS
|
.. setting:: MANAGERS
|
||||||
|
|
||||||
MANAGERS
|
MANAGERS
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue