mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
newforms-admin: Merged changes from trunk up to [6863].
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6864 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
22aa9db4a2
commit
c68f751f8c
8
AUTHORS
8
AUTHORS
@ -90,6 +90,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Paul Collier <paul@paul-collier.com>
|
Paul Collier <paul@paul-collier.com>
|
||||||
Pete Crosier <pete.crosier@gmail.com>
|
Pete Crosier <pete.crosier@gmail.com>
|
||||||
Matt Croydon <http://www.postneo.com/>
|
Matt Croydon <http://www.postneo.com/>
|
||||||
|
Leah Culver <leah@pownce.com>
|
||||||
flavio.curella@gmail.com
|
flavio.curella@gmail.com
|
||||||
Jure Cuhalev <gandalf@owca.info>
|
Jure Cuhalev <gandalf@owca.info>
|
||||||
John D'Agostino <john.dagostino@gmail.com>
|
John D'Agostino <john.dagostino@gmail.com>
|
||||||
@ -133,6 +134,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Jorge Gajon <gajon@gajon.org>
|
Jorge Gajon <gajon@gajon.org>
|
||||||
gandalf@owca.info
|
gandalf@owca.info
|
||||||
Marc Garcia <marc.garcia@accopensys.com>
|
Marc Garcia <marc.garcia@accopensys.com>
|
||||||
|
Andy Gayton <andy-django@thecablelounge.com>
|
||||||
Baishampayan Ghose
|
Baishampayan Ghose
|
||||||
Dimitris Glezos <dimitris@glezos.com>
|
Dimitris Glezos <dimitris@glezos.com>
|
||||||
glin@seznam.cz
|
glin@seznam.cz
|
||||||
@ -173,6 +175,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
junzhang.jn@gmail.com
|
junzhang.jn@gmail.com
|
||||||
Antti Kaihola <http://akaihola.blogspot.com/>
|
Antti Kaihola <http://akaihola.blogspot.com/>
|
||||||
Nagy Károly <charlie@rendszergazda.com>
|
Nagy Károly <charlie@rendszergazda.com>
|
||||||
|
Erik Karulf <erik@karulf.com>
|
||||||
Ben Dean Kawamura <ben.dean.kawamura@gmail.com>
|
Ben Dean Kawamura <ben.dean.kawamura@gmail.com>
|
||||||
Ian G. Kelly <ian.g.kelly@gmail.com>
|
Ian G. Kelly <ian.g.kelly@gmail.com>
|
||||||
Thomas Kerpe <thomas@kerpe.net>
|
Thomas Kerpe <thomas@kerpe.net>
|
||||||
@ -192,6 +195,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Joseph Kocherhans
|
Joseph Kocherhans
|
||||||
konrad@gwu.edu
|
konrad@gwu.edu
|
||||||
knox <christobzr@gmail.com>
|
knox <christobzr@gmail.com>
|
||||||
|
David Krauth
|
||||||
kurtiss@meetro.com
|
kurtiss@meetro.com
|
||||||
lakin.wecker@gmail.com
|
lakin.wecker@gmail.com
|
||||||
Nick Lane <nick.lane.au@gmail.com>
|
Nick Lane <nick.lane.au@gmail.com>
|
||||||
@ -207,6 +211,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
limodou
|
limodou
|
||||||
Philip Lindborg <philip.lindborg@gmail.com>
|
Philip Lindborg <philip.lindborg@gmail.com>
|
||||||
Simon Litchfield <simon@quo.com.au>
|
Simon Litchfield <simon@quo.com.au>
|
||||||
|
Trey Long <trey@ktrl.com>
|
||||||
msaelices <msaelices@gmail.com>
|
msaelices <msaelices@gmail.com>
|
||||||
Matt McClanahan <http://mmcc.cx/>
|
Matt McClanahan <http://mmcc.cx/>
|
||||||
Martin Maney <http://www.chipy.org/Martin_Maney>
|
Martin Maney <http://www.chipy.org/Martin_Maney>
|
||||||
@ -256,6 +261,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Gustavo Picon
|
Gustavo Picon
|
||||||
Luke Plant <http://lukeplant.me.uk/>
|
Luke Plant <http://lukeplant.me.uk/>
|
||||||
plisk
|
plisk
|
||||||
|
Mihai Preda <mihai_preda@yahoo.com>
|
||||||
Daniel Poelzleithner <http://poelzi.org/>
|
Daniel Poelzleithner <http://poelzi.org/>
|
||||||
polpak@yahoo.com
|
polpak@yahoo.com
|
||||||
Matthias Pronk <django@masida.nl>
|
Matthias Pronk <django@masida.nl>
|
||||||
@ -268,6 +274,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Massimiliano Ravelli <massimiliano.ravelli@gmail.com>
|
Massimiliano Ravelli <massimiliano.ravelli@gmail.com>
|
||||||
Brian Ray <http://brianray.chipy.org/>
|
Brian Ray <http://brianray.chipy.org/>
|
||||||
remco@diji.biz
|
remco@diji.biz
|
||||||
|
David Reynolds <david@reynoldsfamily.org.uk>
|
||||||
rhettg@gmail.com
|
rhettg@gmail.com
|
||||||
ricardojbarrios@gmail.com
|
ricardojbarrios@gmail.com
|
||||||
Matt Riggott
|
Matt Riggott
|
||||||
@ -287,6 +294,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Pete Shinners <pete@shinners.org>
|
Pete Shinners <pete@shinners.org>
|
||||||
jason.sidabras@gmail.com
|
jason.sidabras@gmail.com
|
||||||
Jozko Skrablin <jozko.skrablin@gmail.com>
|
Jozko Skrablin <jozko.skrablin@gmail.com>
|
||||||
|
Ben Slavin <benjamin.slavin@gmail.com>
|
||||||
SmileyChris <smileychris@gmail.com>
|
SmileyChris <smileychris@gmail.com>
|
||||||
smurf@smurf.noris.de
|
smurf@smurf.noris.de
|
||||||
Vsevolod Solovyov
|
Vsevolod Solovyov
|
||||||
|
@ -52,7 +52,7 @@ class LazySettings(object):
|
|||||||
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:
|
||||||
raise EnvironmentError, "Environment variable %s is undefined." % ENVIRONMENT_VARIABLE
|
raise ImportError, "Environment variable %s is undefined so settings cannot be imported." % ENVIRONMENT_VARIABLE # NOTE: This is arguably an EnvironmentError, but that causes problems with Python's interactive help
|
||||||
|
|
||||||
self._target = Settings(settings_module)
|
self._target = Settings(settings_module)
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ class LazySettings(object):
|
|||||||
argument must support attribute access (__getattr__)).
|
argument must support attribute access (__getattr__)).
|
||||||
"""
|
"""
|
||||||
if self._target != None:
|
if self._target != None:
|
||||||
raise EnvironmentError, 'Settings already configured.'
|
raise RuntimeError, 'Settings already configured.'
|
||||||
holder = UserSettingsHolder(default_settings)
|
holder = UserSettingsHolder(default_settings)
|
||||||
for name, value in options.items():
|
for name, value in options.items():
|
||||||
setattr(holder, name, value)
|
setattr(holder, name, value)
|
||||||
@ -82,7 +82,7 @@ class Settings(object):
|
|||||||
try:
|
try:
|
||||||
mod = __import__(self.SETTINGS_MODULE, {}, {}, [''])
|
mod = __import__(self.SETTINGS_MODULE, {}, {}, [''])
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
raise EnvironmentError, "Could not import settings '%s' (Is it on sys.path? Does it have syntax errors?): %s" % (self.SETTINGS_MODULE, e)
|
raise ImportError, "Could not import settings '%s' (Is it on sys.path? Does it have syntax errors?): %s" % (self.SETTINGS_MODULE, e)
|
||||||
|
|
||||||
# Settings that should be converted into tuples if they're mistakenly entered
|
# Settings that should be converted into tuples if they're mistakenly entered
|
||||||
# as strings.
|
# as strings.
|
||||||
|
@ -253,6 +253,10 @@ TRANSACTIONS_MANAGED = False
|
|||||||
from django import get_version
|
from django import get_version
|
||||||
URL_VALIDATOR_USER_AGENT = "Django/%s (http://www.djangoproject.com)" % get_version()
|
URL_VALIDATOR_USER_AGENT = "Django/%s (http://www.djangoproject.com)" % get_version()
|
||||||
|
|
||||||
|
# The tablespaces to use for each model when not specified otherwise.
|
||||||
|
DEFAULT_TABLESPACE = ''
|
||||||
|
DEFAULT_INDEX_TABLESPACE = ''
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# MIDDLEWARE #
|
# MIDDLEWARE #
|
||||||
##############
|
##############
|
||||||
@ -289,7 +293,7 @@ SESSION_FILE_PATH = '/tmp/' # Directory to store ses
|
|||||||
|
|
||||||
# The cache backend to use. See the docstring in django.core.cache for the
|
# The cache backend to use. See the docstring in django.core.cache for the
|
||||||
# possible values.
|
# possible values.
|
||||||
CACHE_BACKEND = 'simple://'
|
CACHE_BACKEND = 'locmem://'
|
||||||
CACHE_MIDDLEWARE_KEY_PREFIX = ''
|
CACHE_MIDDLEWARE_KEY_PREFIX = ''
|
||||||
CACHE_MIDDLEWARE_SECONDS = 600
|
CACHE_MIDDLEWARE_SECONDS = 600
|
||||||
|
|
||||||
|
Binary file not shown.
@ -1,19 +1,21 @@
|
|||||||
|
# translation of djangojs.po to español
|
||||||
# translation of djangojs.po to
|
# translation of djangojs.po to
|
||||||
# Catalan translation for the django-admin JS files.
|
# Catalan translation for the django-admin JS files.
|
||||||
# This file is distributed under the same license as the Django package.
|
# This file is distributed under the same license as the Django package.
|
||||||
#
|
#
|
||||||
|
# Antoni Aloy <antoni.aloy@trespams.com>, 2007.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: djangojs\n"
|
"Project-Id-Version: djangojs\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2007-05-20 18:25+0200\n"
|
"POT-Creation-Date: 2007-05-20 18:25+0200\n"
|
||||||
"PO-Revision-Date: 2007-06-25 17:47+0200\n"
|
"PO-Revision-Date: 2007-12-01 12:06+0100\n"
|
||||||
"Last-Translator: Marc Fargas <telenieko@telenieko.com>\n"
|
"Last-Translator: Antoni Aloy <antoni.aloy@trespams.com>\n"
|
||||||
"Language-Team: <es@li.org>\n"
|
"Language-Team: español <ca@li.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: VIM 7.0\n"
|
"X-Generator: KBabel 1.11.4\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: contrib/admin/media/js/SelectFilter2.js:33
|
#: contrib/admin/media/js/SelectFilter2.js:33
|
||||||
@ -51,8 +53,7 @@ msgstr "Deseleccionar tots"
|
|||||||
msgid ""
|
msgid ""
|
||||||
"January February March April May June July August September October November "
|
"January February March April May June July August September October November "
|
||||||
"December"
|
"December"
|
||||||
msgstr ""
|
msgstr "Gener Febrer Març Abril Maig Juny Juliol Agost Setembre Octubre Novembre Desembre"
|
||||||
"Febrer Març Abril Maig Juny Juliol Agost Setembre Octubre Novembre Desembre"
|
|
||||||
|
|
||||||
#: contrib/admin/media/js/dateparse.js:33
|
#: contrib/admin/media/js/dateparse.js:33
|
||||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
||||||
@ -117,3 +118,4 @@ msgstr "Mostrar"
|
|||||||
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
|
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
|
||||||
msgid "Hide"
|
msgid "Hide"
|
||||||
msgstr "Ocultar"
|
msgstr "Ocultar"
|
||||||
|
|
||||||
|
0
django/contrib/localflavor/mx/__init__.py
Normal file
0
django/contrib/localflavor/mx/__init__.py
Normal file
14
django/contrib/localflavor/mx/forms.py
Normal file
14
django/contrib/localflavor/mx/forms.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
"""
|
||||||
|
Mexican-specific form helpers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.newforms.fields import Select
|
||||||
|
|
||||||
|
class MXStateSelect(Select):
|
||||||
|
"""
|
||||||
|
A Select widget that uses a list of Mexican states as its choices.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
from mx_states import STATE_CHOICES
|
||||||
|
super(MXStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
|
||||||
|
|
45
django/contrib/localflavor/mx/mx_states.py
Normal file
45
django/contrib/localflavor/mx/mx_states.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
A list of Mexican states for use as `choices` in a formfield.
|
||||||
|
|
||||||
|
This exists in this standalone file so that it's only imported into memory
|
||||||
|
when explicitly needed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
STATE_CHOICES = (
|
||||||
|
('AGU', _(u'Aguascalientes')),
|
||||||
|
('BCN', _(u'Baja California')),
|
||||||
|
('BCS', _(u'Baja California Sur')),
|
||||||
|
('CAM', _(u'Campeche')),
|
||||||
|
('CHH', _(u'Chihuahua')),
|
||||||
|
('CHP', _(u'Chiapas')),
|
||||||
|
('COA', _(u'Coahuila')),
|
||||||
|
('COL', _(u'Colima')),
|
||||||
|
('DIF', _(u'Distrito Federal')),
|
||||||
|
('DUR', _(u'Durango')),
|
||||||
|
('GRO', _(u'Guerrero')),
|
||||||
|
('GUA', _(u'Guanajuato')),
|
||||||
|
('HID', _(u'Hidalgo')),
|
||||||
|
('JAL', _(u'Jalisco')),
|
||||||
|
('MEX', _(u'Estado de México')),
|
||||||
|
('MIC', _(u'Michoacán')),
|
||||||
|
('MOR', _(u'Morelos')),
|
||||||
|
('NAY', _(u'Nayarit')),
|
||||||
|
('NLE', _(u'Nuevo León')),
|
||||||
|
('OAX', _(u'Oaxaca')),
|
||||||
|
('PUE', _(u'Puebla')),
|
||||||
|
('QUE', _(u'Querétaro')),
|
||||||
|
('ROO', _(u'Quintana Roo')),
|
||||||
|
('SIN', _(u'Sinaloa')),
|
||||||
|
('SLP', _(u'San Luis Potosí')),
|
||||||
|
('SON', _(u'Sonora')),
|
||||||
|
('TAB', _(u'Tabasco')),
|
||||||
|
('TAM', _(u'Tamaulipas')),
|
||||||
|
('TLA', _(u'Tlaxcala')),
|
||||||
|
('VER', _(u'Veracruz')),
|
||||||
|
('YUC', _(u'Yucatán')),
|
||||||
|
('ZAC', _(u'Zacatecas')),
|
||||||
|
)
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
UK-specific Form helpers
|
UK-specific Form helpers
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.newforms.fields import RegexField
|
from django.newforms.fields import RegexField, Select
|
||||||
from django.utils.translation import ugettext
|
from django.utils.translation import ugettext
|
||||||
|
|
||||||
class UKPostcodeField(RegexField):
|
class UKPostcodeField(RegexField):
|
||||||
@ -17,3 +17,19 @@ class UKPostcodeField(RegexField):
|
|||||||
max_length=None, min_length=None,
|
max_length=None, min_length=None,
|
||||||
error_message=ugettext(u'Enter a postcode. A space is required between the two postcode parts.'),
|
error_message=ugettext(u'Enter a postcode. A space is required between the two postcode parts.'),
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
class UKCountySelect(Select):
|
||||||
|
"""
|
||||||
|
A Select widget that uses a list of UK Counties/Regions as its choices.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
from uk_regions import UK_REGION_CHOICES
|
||||||
|
super(UKCountySelect, self).__init__(attrs, choices=UK_REGION_CHOICES)
|
||||||
|
|
||||||
|
class UKNationSelect(Select):
|
||||||
|
"""
|
||||||
|
A Select widget that uses a list of UK Nations as its choices.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
from uk_regions import UK_NATIONS_CHOICES
|
||||||
|
super(UKNationSelect, self).__init__(attrs, choices=UK_NATIONS_CHOICES)
|
||||||
|
97
django/contrib/localflavor/uk/uk_regions.py
Normal file
97
django/contrib/localflavor/uk/uk_regions.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
"""
|
||||||
|
Sources:
|
||||||
|
English regions: http://www.statistics.gov.uk/geography/downloads/31_10_01_REGION_names_and_codes_12_00.xls
|
||||||
|
Northern Ireland regions: http://en.wikipedia.org/wiki/List_of_Irish_counties_by_area
|
||||||
|
Welsh regions: http://en.wikipedia.org/wiki/Preserved_counties_of_Wales
|
||||||
|
Scottish regions: http://en.wikipedia.org/wiki/Regions_and_districts_of_Scotland
|
||||||
|
"""
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
ENGLAND_REGION_CHOICES = (
|
||||||
|
("Bedfordshire", _("Bedfordshire")),
|
||||||
|
("Buckinghamshire", _("Buckinghamshire")),
|
||||||
|
("Cambridgeshire", ("Cambridgeshire")),
|
||||||
|
("Cheshire", _("Cheshire")),
|
||||||
|
("Cornwall and Isles of Scilly", _("Cornwall and Isles of Scilly")),
|
||||||
|
("Cumbria", _("Cumbria")),
|
||||||
|
("Derbyshire", _("Derbyshire")),
|
||||||
|
("Devon", _("Devon")),
|
||||||
|
("Dorset", _("Dorset")),
|
||||||
|
("Durham", _("Durham")),
|
||||||
|
("East Sussex", _("East Sussex")),
|
||||||
|
("Essex", _("Essex")),
|
||||||
|
("Gloucestershire", _("Gloucestershire")),
|
||||||
|
("Greater London", _("Greater London")),
|
||||||
|
("Greater Manchester", _("Greater Manchester")),
|
||||||
|
("Hampshire", _("Hampshire")),
|
||||||
|
("Hertfordshire", _("Hertfordshire")),
|
||||||
|
("Kent", _("Kent")),
|
||||||
|
("Lancashire", _("Lancashire")),
|
||||||
|
("Leicestershire", _("Leicestershire")),
|
||||||
|
("Lincolnshire", _("Lincolnshire")),
|
||||||
|
("Merseyside", _("Merseyside")),
|
||||||
|
("Norfolk", _("Norfolk")),
|
||||||
|
("North Yorkshire", _("North Yorkshire")),
|
||||||
|
("Northamptonshire", _("Northamptonshire")),
|
||||||
|
("Northumberland", _("Northumberland")),
|
||||||
|
("Nottinghamshire", _("Nottinghamshire")),
|
||||||
|
("Oxfordshire", _("Oxfordshire")),
|
||||||
|
("Shropshire", _("Shropshire")),
|
||||||
|
("Somerset", _("Somerset")),
|
||||||
|
("South Yorkshire", _("South Yorkshire")),
|
||||||
|
("Staffordshire", _("Staffordshire")),
|
||||||
|
("Suffolk", _("Suffolk")),
|
||||||
|
("Surrey", _("Surrey")),
|
||||||
|
("Tyne and Wear", _("Tyne and Wear")),
|
||||||
|
("Warwickshire", _("Warwickshire")),
|
||||||
|
("West Midlands", _("West Midlands")),
|
||||||
|
("West Sussex", _("West Sussex")),
|
||||||
|
("West Yorkshire", _("West Yorkshire")),
|
||||||
|
("Wiltshire", _("Wiltshire")),
|
||||||
|
("Worcestershire", _("Worcestershire")),
|
||||||
|
)
|
||||||
|
|
||||||
|
NORTHERN_IRELAND_REGION_CHOICES = (
|
||||||
|
("County Antrim", _("County Antrim")),
|
||||||
|
("County Armagh", _("County Armagh")),
|
||||||
|
("County Down", _("County Down")),
|
||||||
|
("County Fermanagh", _("County Down")),
|
||||||
|
("County Londonderry", _("County Londonderry")),
|
||||||
|
("County Tyrone", _("County Tyrone")),
|
||||||
|
)
|
||||||
|
|
||||||
|
WALES_REGION_CHOICES = (
|
||||||
|
("Clwyd", _("Clwyd")),
|
||||||
|
("Dyfed", _("Dyfed")),
|
||||||
|
("Gwent", _("Gwent")),
|
||||||
|
("Gwynedd", _("Gwynedd")),
|
||||||
|
("Mid Glamorgan", _("Mid Glamorgan")),
|
||||||
|
("Powys", _("Powys")),
|
||||||
|
("South Glamorgan", _("South Glamorgan")),
|
||||||
|
("West Glamorgan", _("West Glamorgan")),
|
||||||
|
)
|
||||||
|
|
||||||
|
SCOTTISH_REGION_CHOICES = (
|
||||||
|
("Borders", _("Borders")),
|
||||||
|
("Central Scotland", _("Central Scotland")),
|
||||||
|
("Dumfries and Galloway", _("Dumfries and Galloway")),
|
||||||
|
("Fife", _("Fife")),
|
||||||
|
("Grampian", _("Grampian")),
|
||||||
|
("Highland", _("Highland")),
|
||||||
|
("Lothian", _("Lothian")),
|
||||||
|
("Orkney Islands", _("Orkney Islands")),
|
||||||
|
("Shetland Islands", _("Shetland Islands")),
|
||||||
|
("Strathclyde", _("Strathclyde")),
|
||||||
|
("Tayside", _("Tayside")),
|
||||||
|
("Western Isles", _("Western Isles")),
|
||||||
|
)
|
||||||
|
|
||||||
|
UK_NATIONS_CHOICES = (
|
||||||
|
("England", _("England")),
|
||||||
|
("Northern Ireland", _("Northern Ireland")),
|
||||||
|
("Scotland", _("Scotland")),
|
||||||
|
("Wales", _("Wales")),
|
||||||
|
)
|
||||||
|
|
||||||
|
UK_REGION_CHOICES = ENGLAND_REGION_CHOICES + NORTHERN_IRELAND_REGION_CHOICES + WALES_REGION_CHOICES + SCOTTISH_REGION_CHOICES
|
||||||
|
|
0
django/contrib/localflavor/za/__init__.py
Normal file
0
django/contrib/localflavor/za/__init__.py
Normal file
57
django/contrib/localflavor/za/forms.py
Normal file
57
django/contrib/localflavor/za/forms.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
"""
|
||||||
|
South Africa-specific Form helpers
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.newforms import ValidationError
|
||||||
|
from django.newforms.fields import Field, RegexField, EMPTY_VALUES
|
||||||
|
from django.utils.checksums import luhn
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
import re
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
id_re = re.compile(r'^(?P<yy>\d\d)(?P<mm>\d\d)(?P<dd>\d\d)(?P<mid>\d{4})(?P<end>\d{3})')
|
||||||
|
|
||||||
|
class ZAIDField(Field):
|
||||||
|
"""A form field for South African ID numbers -- the checksum is validated
|
||||||
|
using the Luhn checksum, and uses a simlistic (read: not entirely accurate)
|
||||||
|
check for the birthdate
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(ZAIDField, self).__init__()
|
||||||
|
self.error_message = _(u'Enter a valid South African ID number')
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
# strip spaces and dashes
|
||||||
|
value = value.strip().replace(' ', '').replace('-', '')
|
||||||
|
|
||||||
|
super(ZAIDField, self).clean(value)
|
||||||
|
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
match = re.match(id_re, value)
|
||||||
|
|
||||||
|
if not match:
|
||||||
|
raise ValidationError(self.error_message)
|
||||||
|
|
||||||
|
g = match.groupdict()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# The year 2000 is conveniently a leapyear.
|
||||||
|
# This algorithm will break in xx00 years which aren't leap years
|
||||||
|
# There is no way to guess the century of a ZA ID number
|
||||||
|
d = date(int(g['yy']) + 2000, int(g['mm']), int(g['dd']))
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError(self.error_message)
|
||||||
|
|
||||||
|
if not luhn(value):
|
||||||
|
raise ValidationError(self.error_message)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
class ZAPostCodeField(RegexField):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(ZAPostCodeField, self).__init__(r'^\d{4}$',
|
||||||
|
max_length=None, min_length=None,
|
||||||
|
error_message=_(u'Enter a valid South African postal code'))
|
13
django/contrib/localflavor/za/za_provinces.py
Normal file
13
django/contrib/localflavor/za/za_provinces.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
PROVINCE_CHOICES = (
|
||||||
|
('EC', _('Eastern Cape')),
|
||||||
|
('FS', _('Free State')),
|
||||||
|
('GP', _('Gauteng')),
|
||||||
|
('KN', _('KwaZulu-Natal')),
|
||||||
|
('LP', _('Limpopo')),
|
||||||
|
('MP', _('Mpumalanga')),
|
||||||
|
('NC', _('Northern Cape')),
|
||||||
|
('NW', _('North West')),
|
||||||
|
('WC', _('Western Cape')),
|
||||||
|
)
|
@ -32,7 +32,23 @@ def textile(value):
|
|||||||
return mark_safe(force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8')))
|
return mark_safe(force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8')))
|
||||||
textile.is_safe = True
|
textile.is_safe = True
|
||||||
|
|
||||||
def markdown(value):
|
def markdown(value, arg=''):
|
||||||
|
"""
|
||||||
|
Runs Markdown over a given value, optionally using various
|
||||||
|
extensions python-markdown supports.
|
||||||
|
|
||||||
|
Syntax::
|
||||||
|
|
||||||
|
{{ value|markdown:"extension1_name,extension2_name..." }}
|
||||||
|
|
||||||
|
To enable safe mode, which strips raw HTML and only returns HTML
|
||||||
|
generated by actual Markdown syntax, pass "safe" as the first
|
||||||
|
extension in the list.
|
||||||
|
|
||||||
|
If the version of Markdown in use does not support extensions,
|
||||||
|
they will be silently ignored.
|
||||||
|
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
import markdown
|
import markdown
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -40,7 +56,18 @@ def markdown(value):
|
|||||||
raise template.TemplateSyntaxError, "Error in {% markdown %} filter: The Python markdown library isn't installed."
|
raise template.TemplateSyntaxError, "Error in {% markdown %} filter: The Python markdown library isn't installed."
|
||||||
return force_unicode(value)
|
return force_unicode(value)
|
||||||
else:
|
else:
|
||||||
return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
|
# markdown.version was first added in 1.6b. The only version of markdown
|
||||||
|
# to fully support extensions before 1.6b was the shortlived 1.6a.
|
||||||
|
if hasattr(markdown, 'version'):
|
||||||
|
extensions = [e for e in arg.split(",") if e]
|
||||||
|
if len(extensions) > 0 and extensions[0] == "safe":
|
||||||
|
extensions = extensions[1:]
|
||||||
|
safe_mode = True
|
||||||
|
else:
|
||||||
|
safe_mode = False
|
||||||
|
return mark_safe(force_unicode(markdown.markdown(smart_str(value), extensions, safe_mode=safe_mode)))
|
||||||
|
else:
|
||||||
|
return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
|
||||||
markdown.is_safe = True
|
markdown.is_safe = True
|
||||||
|
|
||||||
def restructuredtext(value):
|
def restructuredtext(value):
|
||||||
|
@ -61,8 +61,15 @@ Paragraph 2 with a link_
|
|||||||
t = Template("{{ rest_content|restructuredtext }}")
|
t = Template("{{ rest_content|restructuredtext }}")
|
||||||
rendered = t.render(Context(locals())).strip()
|
rendered = t.render(Context(locals())).strip()
|
||||||
if docutils:
|
if docutils:
|
||||||
self.assertEqual(rendered, """<p>Paragraph 1</p>
|
# Different versions of docutils return slightly different HTML
|
||||||
|
try:
|
||||||
|
# Docutils v0.4 and earlier
|
||||||
|
self.assertEqual(rendered, """<p>Paragraph 1</p>
|
||||||
<p>Paragraph 2 with a <a class="reference" href="http://www.example.com/">link</a></p>""")
|
<p>Paragraph 2 with a <a class="reference" href="http://www.example.com/">link</a></p>""")
|
||||||
|
except AssertionError, e:
|
||||||
|
# Docutils from SVN (which will become 0.5)
|
||||||
|
self.assertEqual(rendered, """<p>Paragraph 1</p>
|
||||||
|
<p>Paragraph 2 with a <a class="reference external" href="http://www.example.com/">link</a></p>""")
|
||||||
else:
|
else:
|
||||||
self.assertEqual(rendered, rest_content)
|
self.assertEqual(rendered, rest_content)
|
||||||
|
|
||||||
|
@ -51,6 +51,14 @@ class SessionBase(object):
|
|||||||
self.modified = self.modified or key in self._session
|
self.modified = self.modified or key in self._session
|
||||||
return self._session.pop(key, *args)
|
return self._session.pop(key, *args)
|
||||||
|
|
||||||
|
def setdefault(self, key, value):
|
||||||
|
if key in self._session:
|
||||||
|
return self._session[key]
|
||||||
|
else:
|
||||||
|
self.modified = True
|
||||||
|
self._session[key] = value
|
||||||
|
return value
|
||||||
|
|
||||||
def set_test_cookie(self):
|
def set_test_cookie(self):
|
||||||
self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE
|
self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE
|
||||||
|
|
||||||
|
@ -18,40 +18,6 @@ class SessionManager(models.Manager):
|
|||||||
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
|
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
|
||||||
return base64.encodestring(pickled + pickled_md5)
|
return base64.encodestring(pickled + pickled_md5)
|
||||||
|
|
||||||
def get_new_session_key(self):
|
|
||||||
"Returns session key that isn't being used."
|
|
||||||
# The random module is seeded when this Apache child is created.
|
|
||||||
# Use SECRET_KEY as added salt.
|
|
||||||
try:
|
|
||||||
pid = os.getpid()
|
|
||||||
except AttributeError:
|
|
||||||
# No getpid() in Jython, for example
|
|
||||||
pid = 1
|
|
||||||
while 1:
|
|
||||||
session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), pid, time.time(), settings.SECRET_KEY)).hexdigest()
|
|
||||||
try:
|
|
||||||
self.get(session_key=session_key)
|
|
||||||
except self.model.DoesNotExist:
|
|
||||||
break
|
|
||||||
return session_key
|
|
||||||
|
|
||||||
def get_new_session_object(self):
|
|
||||||
"""
|
|
||||||
Returns a new session object.
|
|
||||||
"""
|
|
||||||
# FIXME: There is a *small* chance of collision here, meaning we will
|
|
||||||
# return an existing object. That can be fixed when we add a way to
|
|
||||||
# validate (and guarantee) that non-auto primary keys are unique. For
|
|
||||||
# now, we save immediately in order to reduce the "window of
|
|
||||||
# misfortune" as much as possible.
|
|
||||||
created = False
|
|
||||||
while not created:
|
|
||||||
obj, created = self.get_or_create(session_key=self.get_new_session_key(),
|
|
||||||
expire_date = datetime.datetime.now())
|
|
||||||
# Collision in key generation, so re-seed the generator
|
|
||||||
random.seed()
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def save(self, session_key, session_dict, expire_date):
|
def save(self, session_key, session_dict, expire_date):
|
||||||
s = self.model(session_key, self.encode(session_dict), expire_date)
|
s = self.model(session_key, self.encode(session_dict), expire_date)
|
||||||
if session_dict:
|
if session_dict:
|
||||||
|
@ -66,6 +66,11 @@ False
|
|||||||
>>> s.accessed, s.modified
|
>>> s.accessed, s.modified
|
||||||
(True, False)
|
(True, False)
|
||||||
|
|
||||||
|
>>> s.setdefault('foo', 'bar')
|
||||||
|
'bar'
|
||||||
|
>>> s.setdefault('foo', 'baz')
|
||||||
|
'bar'
|
||||||
|
|
||||||
>>> s.accessed = False # Reset the accessed flag
|
>>> s.accessed = False # Reset the accessed flag
|
||||||
|
|
||||||
>>> s.pop('some key')
|
>>> s.pop('some key')
|
||||||
|
11
django/core/cache/__init__.py
vendored
11
django/core/cache/__init__.py
vendored
@ -22,19 +22,28 @@ from django.core.cache.backends.base import InvalidCacheBackendError
|
|||||||
BACKENDS = {
|
BACKENDS = {
|
||||||
# name for use in settings file --> name of module in "backends" directory
|
# name for use in settings file --> name of module in "backends" directory
|
||||||
'memcached': 'memcached',
|
'memcached': 'memcached',
|
||||||
'simple': 'simple',
|
|
||||||
'locmem': 'locmem',
|
'locmem': 'locmem',
|
||||||
'file': 'filebased',
|
'file': 'filebased',
|
||||||
'db': 'db',
|
'db': 'db',
|
||||||
'dummy': 'dummy',
|
'dummy': 'dummy',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEPRECATED_BACKENDS = {
|
||||||
|
# deprecated backend --> replacement module
|
||||||
|
'simple': 'locmem',
|
||||||
|
}
|
||||||
|
|
||||||
def get_cache(backend_uri):
|
def get_cache(backend_uri):
|
||||||
if backend_uri.find(':') == -1:
|
if backend_uri.find(':') == -1:
|
||||||
raise InvalidCacheBackendError, "Backend URI must start with scheme://"
|
raise InvalidCacheBackendError, "Backend URI must start with scheme://"
|
||||||
scheme, rest = backend_uri.split(':', 1)
|
scheme, rest = backend_uri.split(':', 1)
|
||||||
if not rest.startswith('//'):
|
if not rest.startswith('//'):
|
||||||
raise InvalidCacheBackendError, "Backend URI must start with scheme://"
|
raise InvalidCacheBackendError, "Backend URI must start with scheme://"
|
||||||
|
if scheme in DEPRECATED_BACKENDS:
|
||||||
|
import warnings
|
||||||
|
warnings.warn("'%s' backend is deprecated. Use '%s' instead." %
|
||||||
|
(scheme, DEPRECATED_BACKENDS[scheme]), DeprecationWarning)
|
||||||
|
scheme = DEPRECATED_BACKENDS[scheme]
|
||||||
if scheme not in BACKENDS:
|
if scheme not in BACKENDS:
|
||||||
raise InvalidCacheBackendError, "%r is not a valid cache backend" % scheme
|
raise InvalidCacheBackendError, "%r is not a valid cache backend" % scheme
|
||||||
|
|
||||||
|
23
django/core/cache/backends/filebased.py
vendored
23
django/core/cache/backends/filebased.py
vendored
@ -1,21 +1,32 @@
|
|||||||
"File-based cache backend"
|
"File-based cache backend"
|
||||||
|
|
||||||
from django.core.cache.backends.simple import CacheClass as SimpleCacheClass
|
|
||||||
from django.utils.http import urlquote_plus
|
|
||||||
import os, time
|
import os, time
|
||||||
try:
|
try:
|
||||||
import cPickle as pickle
|
import cPickle as pickle
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import pickle
|
import pickle
|
||||||
|
from django.core.cache.backends.base import BaseCache
|
||||||
|
from django.utils.http import urlquote_plus
|
||||||
|
|
||||||
class CacheClass(SimpleCacheClass):
|
class CacheClass(BaseCache):
|
||||||
def __init__(self, dir, params):
|
def __init__(self, dir, params):
|
||||||
|
BaseCache.__init__(self, params)
|
||||||
|
|
||||||
|
max_entries = params.get('max_entries', 300)
|
||||||
|
try:
|
||||||
|
self._max_entries = int(max_entries)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
self._max_entries = 300
|
||||||
|
|
||||||
|
cull_frequency = params.get('cull_frequency', 3)
|
||||||
|
try:
|
||||||
|
self._cull_frequency = int(cull_frequency)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
self._cull_frequency = 3
|
||||||
|
|
||||||
self._dir = dir
|
self._dir = dir
|
||||||
if not os.path.exists(self._dir):
|
if not os.path.exists(self._dir):
|
||||||
self._createdir()
|
self._createdir()
|
||||||
SimpleCacheClass.__init__(self, dir, params)
|
|
||||||
del self._cache
|
|
||||||
del self._expire_info
|
|
||||||
|
|
||||||
def add(self, key, value, timeout=None):
|
def add(self, key, value, timeout=None):
|
||||||
fname = self._key_to_file(key)
|
fname = self._key_to_file(key)
|
||||||
|
68
django/core/cache/backends/locmem.py
vendored
68
django/core/cache/backends/locmem.py
vendored
@ -6,20 +6,44 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from django.core.cache.backends.simple import CacheClass as SimpleCacheClass
|
from django.core.cache.backends.base import BaseCache
|
||||||
from django.utils.synch import RWLock
|
from django.utils.synch import RWLock
|
||||||
|
|
||||||
class CacheClass(SimpleCacheClass):
|
class CacheClass(BaseCache):
|
||||||
def __init__(self, host, params):
|
def __init__(self, _, params):
|
||||||
SimpleCacheClass.__init__(self, host, params)
|
BaseCache.__init__(self, params)
|
||||||
|
self._cache = {}
|
||||||
|
self._expire_info = {}
|
||||||
|
|
||||||
|
max_entries = params.get('max_entries', 300)
|
||||||
|
try:
|
||||||
|
self._max_entries = int(max_entries)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
self._max_entries = 300
|
||||||
|
|
||||||
|
cull_frequency = params.get('cull_frequency', 3)
|
||||||
|
try:
|
||||||
|
self._cull_frequency = int(cull_frequency)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
self._cull_frequency = 3
|
||||||
|
|
||||||
self._lock = RWLock()
|
self._lock = RWLock()
|
||||||
|
|
||||||
|
def _add(self, key, value, timeout=None):
|
||||||
|
if len(self._cache) >= self._max_entries:
|
||||||
|
self._cull()
|
||||||
|
if timeout is None:
|
||||||
|
timeout = self.default_timeout
|
||||||
|
if key not in self._cache.keys():
|
||||||
|
self._cache[key] = value
|
||||||
|
self._expire_info[key] = time.time() + timeout
|
||||||
|
|
||||||
def add(self, key, value, timeout=None):
|
def add(self, key, value, timeout=None):
|
||||||
self._lock.writer_enters()
|
self._lock.writer_enters()
|
||||||
# Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
|
# Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
super(CacheClass, self).add(key, pickle.dumps(value), timeout)
|
self._add(key, pickle.dumps(value), timeout)
|
||||||
except pickle.PickleError:
|
except pickle.PickleError:
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
@ -51,20 +75,50 @@ class CacheClass(SimpleCacheClass):
|
|||||||
finally:
|
finally:
|
||||||
self._lock.writer_leaves()
|
self._lock.writer_leaves()
|
||||||
|
|
||||||
|
def _set(self, key, value, timeout=None):
|
||||||
|
if len(self._cache) >= self._max_entries:
|
||||||
|
self._cull()
|
||||||
|
if timeout is None:
|
||||||
|
timeout = self.default_timeout
|
||||||
|
self._cache[key] = value
|
||||||
|
self._expire_info[key] = time.time() + timeout
|
||||||
|
|
||||||
def set(self, key, value, timeout=None):
|
def set(self, key, value, timeout=None):
|
||||||
self._lock.writer_enters()
|
self._lock.writer_enters()
|
||||||
# Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
|
# Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
super(CacheClass, self).set(key, pickle.dumps(value), timeout)
|
self._set(key, pickle.dumps(value), timeout)
|
||||||
except pickle.PickleError:
|
except pickle.PickleError:
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
self._lock.writer_leaves()
|
self._lock.writer_leaves()
|
||||||
|
|
||||||
|
def has_key(self, key):
|
||||||
|
return key in self._cache
|
||||||
|
|
||||||
|
def _cull(self):
|
||||||
|
if self._cull_frequency == 0:
|
||||||
|
self._cache.clear()
|
||||||
|
self._expire_info.clear()
|
||||||
|
else:
|
||||||
|
doomed = [k for (i, k) in enumerate(self._cache) if i % self._cull_frequency == 0]
|
||||||
|
for k in doomed:
|
||||||
|
self.delete(k)
|
||||||
|
|
||||||
|
def _delete(self, key):
|
||||||
|
try:
|
||||||
|
del self._cache[key]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
del self._expire_info[key]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
def delete(self, key):
|
def delete(self, key):
|
||||||
self._lock.writer_enters()
|
self._lock.writer_enters()
|
||||||
try:
|
try:
|
||||||
SimpleCacheClass.delete(self, key)
|
self._delete(key)
|
||||||
finally:
|
finally:
|
||||||
self._lock.writer_leaves()
|
self._lock.writer_leaves()
|
||||||
|
73
django/core/cache/backends/simple.py
vendored
73
django/core/cache/backends/simple.py
vendored
@ -1,73 +0,0 @@
|
|||||||
"Single-process in-memory cache backend."
|
|
||||||
|
|
||||||
from django.core.cache.backends.base import BaseCache
|
|
||||||
import time
|
|
||||||
|
|
||||||
class CacheClass(BaseCache):
|
|
||||||
def __init__(self, host, params):
|
|
||||||
BaseCache.__init__(self, params)
|
|
||||||
self._cache = {}
|
|
||||||
self._expire_info = {}
|
|
||||||
|
|
||||||
max_entries = params.get('max_entries', 300)
|
|
||||||
try:
|
|
||||||
self._max_entries = int(max_entries)
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
self._max_entries = 300
|
|
||||||
|
|
||||||
cull_frequency = params.get('cull_frequency', 3)
|
|
||||||
try:
|
|
||||||
self._cull_frequency = int(cull_frequency)
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
self._cull_frequency = 3
|
|
||||||
|
|
||||||
def add(self, key, value, timeout=None):
|
|
||||||
if len(self._cache) >= self._max_entries:
|
|
||||||
self._cull()
|
|
||||||
if timeout is None:
|
|
||||||
timeout = self.default_timeout
|
|
||||||
if key not in self._cache.keys():
|
|
||||||
self._cache[key] = value
|
|
||||||
self._expire_info[key] = time.time() + timeout
|
|
||||||
|
|
||||||
def get(self, key, default=None):
|
|
||||||
now = time.time()
|
|
||||||
exp = self._expire_info.get(key)
|
|
||||||
if exp is None:
|
|
||||||
return default
|
|
||||||
elif exp < now:
|
|
||||||
del self._cache[key]
|
|
||||||
del self._expire_info[key]
|
|
||||||
return default
|
|
||||||
else:
|
|
||||||
return self._cache[key]
|
|
||||||
|
|
||||||
def set(self, key, value, timeout=None):
|
|
||||||
if len(self._cache) >= self._max_entries:
|
|
||||||
self._cull()
|
|
||||||
if timeout is None:
|
|
||||||
timeout = self.default_timeout
|
|
||||||
self._cache[key] = value
|
|
||||||
self._expire_info[key] = time.time() + timeout
|
|
||||||
|
|
||||||
def delete(self, key):
|
|
||||||
try:
|
|
||||||
del self._cache[key]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
del self._expire_info[key]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def has_key(self, key):
|
|
||||||
return key in self._cache
|
|
||||||
|
|
||||||
def _cull(self):
|
|
||||||
if self._cull_frequency == 0:
|
|
||||||
self._cache.clear()
|
|
||||||
self._expire_info.clear()
|
|
||||||
else:
|
|
||||||
doomed = [k for (i, k) in enumerate(self._cache) if i % self._cull_frequency == 0]
|
|
||||||
for k in doomed:
|
|
||||||
self.delete(k)
|
|
@ -4,6 +4,10 @@ class ObjectDoesNotExist(Exception):
|
|||||||
"The requested object does not exist"
|
"The requested object does not exist"
|
||||||
silent_variable_failure = True
|
silent_variable_failure = True
|
||||||
|
|
||||||
|
class MultipleObjectsReturned(Exception):
|
||||||
|
"The query returned multiple objects when only one was expected."
|
||||||
|
pass
|
||||||
|
|
||||||
class SuspiciousOperation(Exception):
|
class SuspiciousOperation(Exception):
|
||||||
"The user did something suspicious"
|
"The user did something suspicious"
|
||||||
pass
|
pass
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from django import http
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django import http
|
|
||||||
import sys
|
|
||||||
|
|
||||||
class BaseHandler(object):
|
class BaseHandler(object):
|
||||||
# Changes that are always applied to a response (in this order).
|
# Changes that are always applied to a response (in this order).
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
from django.core.handlers.base import BaseHandler
|
import os
|
||||||
|
from pprint import pformat
|
||||||
|
|
||||||
|
from django import http
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
|
from django.core.handlers.base import BaseHandler
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django.utils import datastructures
|
from django.utils import datastructures
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_unicode
|
||||||
from django import http
|
|
||||||
from pprint import pformat
|
|
||||||
import os
|
|
||||||
|
|
||||||
# NOTE: do *not* import settings (or any module which eventually imports
|
# NOTE: do *not* import settings (or any module which eventually imports
|
||||||
# settings) until after ModPythonHandler has been called; otherwise os.environ
|
# settings) until after ModPythonHandler has been called; otherwise os.environ
|
||||||
|
@ -5,12 +5,12 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
from django.core.handlers.base import BaseHandler
|
from django import http
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
|
from django.core.handlers.base import BaseHandler
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django.utils import datastructures
|
from django.utils import datastructures
|
||||||
from django.utils.encoding import force_unicode
|
from django.utils.encoding import force_unicode
|
||||||
from django import http
|
|
||||||
|
|
||||||
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||||
STATUS_CODE_TEXT = {
|
STATUS_CODE_TEXT = {
|
||||||
|
@ -84,7 +84,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):
|
except (AttributeError, ImportError):
|
||||||
apps = []
|
apps = []
|
||||||
|
|
||||||
for app_name in apps:
|
for app_name in apps:
|
||||||
@ -99,7 +99,7 @@ def get_commands():
|
|||||||
try:
|
try:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
project_directory = setup_environ(__import__(settings.SETTINGS_MODULE))
|
project_directory = setup_environ(__import__(settings.SETTINGS_MODULE))
|
||||||
except (AttributeError, EnvironmentError, ImportError):
|
except (AttributeError, ImportError):
|
||||||
project_directory = None
|
project_directory = None
|
||||||
|
|
||||||
if project_directory:
|
if project_directory:
|
||||||
@ -254,6 +254,8 @@ def setup_environ(settings_mod):
|
|||||||
# way. For example, if this file (manage.py) lives in a directory
|
# way. For example, if this file (manage.py) lives in a directory
|
||||||
# "myproject", this code would add "/path/to/myproject" to sys.path.
|
# "myproject", this code would add "/path/to/myproject" to sys.path.
|
||||||
project_directory, settings_filename = os.path.split(settings_mod.__file__)
|
project_directory, settings_filename = os.path.split(settings_mod.__file__)
|
||||||
|
if not project_directory:
|
||||||
|
project_directory = os.getcwd()
|
||||||
project_name = os.path.basename(project_directory)
|
project_name = os.path.basename(project_directory)
|
||||||
settings_name = os.path.splitext(settings_filename)[0]
|
settings_name = os.path.splitext(settings_filename)[0]
|
||||||
sys.path.append(os.path.join(project_directory, os.pardir))
|
sys.path.append(os.path.join(project_directory, os.pardir))
|
||||||
|
@ -33,8 +33,9 @@ class Command(NoArgsCommand):
|
|||||||
for app_name in settings.INSTALLED_APPS:
|
for app_name in settings.INSTALLED_APPS:
|
||||||
try:
|
try:
|
||||||
__import__(app_name + '.management', {}, {}, [''])
|
__import__(app_name + '.management', {}, {}, [''])
|
||||||
except ImportError:
|
except ImportError, exc:
|
||||||
pass
|
if not exc.args[0].startswith('No module named management'):
|
||||||
|
raise
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
@ -70,10 +70,14 @@ def get_validation_errors(outfile, app=None):
|
|||||||
# Check to see if the related field will clash with any
|
# Check to see if the related field will clash with any
|
||||||
# existing fields, m2m fields, m2m related objects or related objects
|
# existing fields, m2m fields, m2m related objects or related objects
|
||||||
if f.rel:
|
if f.rel:
|
||||||
rel_opts = f.rel.to._meta
|
|
||||||
if f.rel.to not in models.get_models():
|
if f.rel.to not in models.get_models():
|
||||||
e.add(opts, "'%s' has relation with model %s, which has not been installed" % (f.name, rel_opts.object_name))
|
e.add(opts, "'%s' has relation with model %s, which has not been installed" % (f.name, f.rel.to))
|
||||||
|
# it is a string and we could not find the model it refers to
|
||||||
|
# so skip the next section
|
||||||
|
if isinstance(f.rel.to, (str, unicode)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
rel_opts = f.rel.to._meta
|
||||||
rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
|
rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
|
||||||
rel_query_name = f.related_query_name()
|
rel_query_name = f.related_query_name()
|
||||||
for r in rel_opts.fields:
|
for r in rel_opts.fields:
|
||||||
@ -101,10 +105,14 @@ def get_validation_errors(outfile, app=None):
|
|||||||
for i, f in enumerate(opts.many_to_many):
|
for i, f in enumerate(opts.many_to_many):
|
||||||
# Check to see if the related m2m field will clash with any
|
# Check to see if the related m2m field will clash with any
|
||||||
# existing fields, m2m fields, m2m related objects or related objects
|
# existing fields, m2m fields, m2m related objects or related objects
|
||||||
rel_opts = f.rel.to._meta
|
|
||||||
if f.rel.to not in models.get_models():
|
if f.rel.to not in models.get_models():
|
||||||
e.add(opts, "'%s' has m2m relation with model %s, which has not been installed" % (f.name, rel_opts.object_name))
|
e.add(opts, "'%s' has m2m relation with model %s, which has not been installed" % (f.name, f.rel.to))
|
||||||
|
# it is a string and we could not find the model it refers to
|
||||||
|
# so skip the next section
|
||||||
|
if isinstance(f.rel.to, (str, unicode)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
rel_opts = f.rel.to._meta
|
||||||
rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
|
rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
|
||||||
rel_query_name = f.related_query_name()
|
rel_query_name = f.related_query_name()
|
||||||
# If rel_name is none, there is no reverse accessor.
|
# If rel_name is none, there is no reverse accessor.
|
||||||
|
@ -47,7 +47,8 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||||||
BEGIN
|
BEGIN
|
||||||
SELECT %(sq_name)s.nextval
|
SELECT %(sq_name)s.nextval
|
||||||
INTO :new.%(col_name)s FROM dual;
|
INTO :new.%(col_name)s FROM dual;
|
||||||
END;/""" % locals()
|
END;
|
||||||
|
/""" % locals()
|
||||||
return sequence_sql, trigger_sql
|
return sequence_sql, trigger_sql
|
||||||
|
|
||||||
def date_extract_sql(self, lookup_type, field_name):
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
|
@ -6,6 +6,7 @@ Requires psycopg 2: http://initd.org/projects/psycopg2
|
|||||||
|
|
||||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
|
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
|
||||||
from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
|
from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
|
||||||
|
from django.utils.safestring import SafeUnicode
|
||||||
try:
|
try:
|
||||||
import psycopg2 as Database
|
import psycopg2 as Database
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
@ -17,6 +18,7 @@ DatabaseError = Database.DatabaseError
|
|||||||
IntegrityError = Database.IntegrityError
|
IntegrityError = Database.IntegrityError
|
||||||
|
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
|
psycopg2.extensions.register_adapter(SafeUnicode, psycopg2.extensions.QuotedString)
|
||||||
|
|
||||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
needs_datetime_string_cast = False
|
needs_datetime_string_cast = False
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import django.db.models.manipulators
|
import django.db.models.manipulators
|
||||||
import django.db.models.manager
|
import django.db.models.manager
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||||
from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
|
from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
|
||||||
from django.db.models.fields.related import OneToOneRel, ManyToOneRel
|
from django.db.models.fields.related import OneToOneRel, ManyToOneRel
|
||||||
from django.db.models.query import delete_objects
|
from django.db.models.query import delete_objects
|
||||||
@ -35,6 +35,8 @@ class ModelBase(type):
|
|||||||
new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')})
|
new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')})
|
||||||
new_class.add_to_class('_meta', Options(attrs.pop('Meta', None)))
|
new_class.add_to_class('_meta', Options(attrs.pop('Meta', None)))
|
||||||
new_class.add_to_class('DoesNotExist', types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}))
|
new_class.add_to_class('DoesNotExist', types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}))
|
||||||
|
new_class.add_to_class('MultipleObjectsReturned',
|
||||||
|
types.ClassType('MultipleObjectsReturned', (MultipleObjectsReturned, ), {}))
|
||||||
|
|
||||||
# Build complete list of parents
|
# Build complete list of parents
|
||||||
for base in bases:
|
for base in bases:
|
||||||
|
@ -102,7 +102,7 @@ class Field(object):
|
|||||||
self.radio_admin = radio_admin
|
self.radio_admin = radio_admin
|
||||||
self.help_text = help_text
|
self.help_text = help_text
|
||||||
self.db_column = db_column
|
self.db_column = db_column
|
||||||
self.db_tablespace = db_tablespace
|
self.db_tablespace = db_tablespace or settings.DEFAULT_INDEX_TABLESPACE
|
||||||
|
|
||||||
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
||||||
self.db_index = db_index
|
self.db_index = db_index
|
||||||
|
@ -28,7 +28,7 @@ class Options(object):
|
|||||||
self.object_name, self.app_label = None, None
|
self.object_name, self.app_label = None, None
|
||||||
self.get_latest_by = None
|
self.get_latest_by = None
|
||||||
self.order_with_respect_to = None
|
self.order_with_respect_to = None
|
||||||
self.db_tablespace = None
|
self.db_tablespace = settings.DEFAULT_TABLESPACE
|
||||||
self.admin = None
|
self.admin = None
|
||||||
self.meta = meta
|
self.meta = meta
|
||||||
self.pk = None
|
self.pk = None
|
||||||
@ -151,7 +151,7 @@ class Options(object):
|
|||||||
rel_objs = []
|
rel_objs = []
|
||||||
for klass in get_models():
|
for klass in get_models():
|
||||||
for f in klass._meta.fields:
|
for f in klass._meta.fields:
|
||||||
if f.rel and self == f.rel.to._meta:
|
if f.rel and not isinstance(f.rel.to, str) and self == f.rel.to._meta:
|
||||||
rel_objs.append(RelatedObject(f.rel.to, klass, f))
|
rel_objs.append(RelatedObject(f.rel.to, klass, f))
|
||||||
self._all_related_objects = rel_objs
|
self._all_related_objects = rel_objs
|
||||||
return rel_objs
|
return rel_objs
|
||||||
@ -185,7 +185,7 @@ class Options(object):
|
|||||||
rel_objs = []
|
rel_objs = []
|
||||||
for klass in get_models():
|
for klass in get_models():
|
||||||
for f in klass._meta.many_to_many:
|
for f in klass._meta.many_to_many:
|
||||||
if f.rel and self == f.rel.to._meta:
|
if f.rel and not isinstance(f.rel.to, str) and self == f.rel.to._meta:
|
||||||
rel_objs.append(RelatedObject(f.rel.to, klass, f))
|
rel_objs.append(RelatedObject(f.rel.to, klass, f))
|
||||||
if app_cache_ready():
|
if app_cache_ready():
|
||||||
self._all_related_many_to_many_objects = rel_objs
|
self._all_related_many_to_many_objects = rel_objs
|
||||||
|
@ -261,7 +261,8 @@ class _QuerySet(object):
|
|||||||
obj_list = list(clone)
|
obj_list = list(clone)
|
||||||
if len(obj_list) < 1:
|
if len(obj_list) < 1:
|
||||||
raise self.model.DoesNotExist, "%s matching query does not exist." % self.model._meta.object_name
|
raise self.model.DoesNotExist, "%s matching query does not exist." % self.model._meta.object_name
|
||||||
assert len(obj_list) == 1, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs)
|
elif len(obj_list) > 1:
|
||||||
|
raise self.model.MultipleObjectsReturned, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs)
|
||||||
return obj_list[0]
|
return obj_list[0]
|
||||||
|
|
||||||
def create(self, **kwargs):
|
def create(self, **kwargs):
|
||||||
|
@ -5,7 +5,7 @@ Functions that modify an HTTP request or response in some way.
|
|||||||
# This group of functions are run as part of the response handling, after
|
# This group of functions are run as part of the response handling, after
|
||||||
# everything else, including all response middleware. Think of them as
|
# everything else, including all response middleware. Think of them as
|
||||||
# "compulsory response middleware". Be careful about what goes here, because
|
# "compulsory response middleware". Be careful about what goes here, because
|
||||||
# it's a little fiddly to override this behaviour, so they should be truly
|
# it's a little fiddly to override this behavior, so they should be truly
|
||||||
# universally applicable.
|
# universally applicable.
|
||||||
|
|
||||||
def fix_location_header(request, response):
|
def fix_location_header(request, response):
|
||||||
@ -13,7 +13,7 @@ def fix_location_header(request, response):
|
|||||||
Ensures that we always use an absolute URI in any location header in the
|
Ensures that we always use an absolute URI in any location header in the
|
||||||
response. This is required by RFC 2616, section 14.30.
|
response. This is required by RFC 2616, section 14.30.
|
||||||
|
|
||||||
Code constructing response objects is free to insert relative paths and
|
Code constructing response objects is free to insert relative paths, as
|
||||||
this function converts them to absolute paths.
|
this function converts them to absolute paths.
|
||||||
"""
|
"""
|
||||||
if 'Location' in response and request.get_host():
|
if 'Location' in response and request.get_host():
|
||||||
@ -31,4 +31,3 @@ def conditional_content_removal(request, response):
|
|||||||
if request.method == 'HEAD':
|
if request.method == 'HEAD':
|
||||||
response.content = ''
|
response.content = ''
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ from django.conf import settings
|
|||||||
from django import http
|
from django import http
|
||||||
from django.core.mail import mail_managers
|
from django.core.mail import mail_managers
|
||||||
from django.utils.http import urlquote
|
from django.utils.http import urlquote
|
||||||
|
from django.core import urlresolvers
|
||||||
|
|
||||||
class CommonMiddleware(object):
|
class CommonMiddleware(object):
|
||||||
"""
|
"""
|
||||||
@ -16,6 +17,12 @@ class CommonMiddleware(object):
|
|||||||
this middleware appends missing slashes and/or prepends missing
|
this middleware appends missing slashes and/or prepends missing
|
||||||
"www."s.
|
"www."s.
|
||||||
|
|
||||||
|
- If APPEND_SLASH is set and the initial URL doesn't end with a
|
||||||
|
slash, and it is not found in urlpatterns, a new URL is formed by
|
||||||
|
appending a slash at the end. If this new URL is found in
|
||||||
|
urlpatterns, then an HTTP-redirect is returned to this new URL;
|
||||||
|
otherwise the initial URL is processed as usual.
|
||||||
|
|
||||||
- ETags: If the USE_ETAGS setting is set, ETags will be calculated from
|
- ETags: If the USE_ETAGS setting is set, ETags will be calculated from
|
||||||
the entire page content and Not Modified responses will be returned
|
the entire page content and Not Modified responses will be returned
|
||||||
appropriately.
|
appropriately.
|
||||||
@ -33,27 +40,48 @@ class CommonMiddleware(object):
|
|||||||
if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
|
if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
|
||||||
return http.HttpResponseForbidden('<h1>Forbidden</h1>')
|
return http.HttpResponseForbidden('<h1>Forbidden</h1>')
|
||||||
|
|
||||||
# Check for a redirect based on settings.APPEND_SLASH and settings.PREPEND_WWW
|
# Check for a redirect based on settings.APPEND_SLASH
|
||||||
|
# and settings.PREPEND_WWW
|
||||||
host = request.get_host()
|
host = request.get_host()
|
||||||
old_url = [host, request.path]
|
old_url = [host, request.path]
|
||||||
new_url = old_url[:]
|
new_url = old_url[:]
|
||||||
if settings.PREPEND_WWW and old_url[0] and not old_url[0].startswith('www.'):
|
|
||||||
|
if (settings.PREPEND_WWW and old_url[0] and
|
||||||
|
not old_url[0].startswith('www.')):
|
||||||
new_url[0] = 'www.' + old_url[0]
|
new_url[0] = 'www.' + old_url[0]
|
||||||
# Append a slash if append_slash is set and the URL doesn't have a
|
|
||||||
# trailing slash or a file extension.
|
# Append a slash if APPEND_SLASH is set and the URL doesn't have a
|
||||||
if settings.APPEND_SLASH and (not old_url[1].endswith('/')) and ('.' not in old_url[1].split('/')[-1]):
|
# trailing slash and there is no pattern for the current path
|
||||||
new_url[1] = new_url[1] + '/'
|
if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
|
||||||
if settings.DEBUG and request.method == 'POST':
|
try:
|
||||||
raise RuntimeError, "You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to %s%s (note the trailing slash), or set APPEND_SLASH=False in your Django settings." % (new_url[0], new_url[1])
|
urlresolvers.resolve(request.path)
|
||||||
|
except urlresolvers.Resolver404:
|
||||||
|
new_url[1] = new_url[1] + '/'
|
||||||
|
if settings.DEBUG and request.method == 'POST':
|
||||||
|
raise RuntimeError, (""
|
||||||
|
"You called this URL via POST, but the URL doesn't end "
|
||||||
|
"in a slash and you have APPEND_SLASH set. Django can't "
|
||||||
|
"redirect to the slash URL while maintaining POST data. "
|
||||||
|
"Change your form to point to %s%s (note the trailing "
|
||||||
|
"slash), or set APPEND_SLASH=False in your Django "
|
||||||
|
"settings.") % (new_url[0], new_url[1])
|
||||||
|
|
||||||
if new_url != old_url:
|
if new_url != old_url:
|
||||||
# Redirect
|
# Redirect if the target url exists
|
||||||
if new_url[0]:
|
try:
|
||||||
newurl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', new_url[0], urlquote(new_url[1]))
|
urlresolvers.resolve(new_url[1])
|
||||||
|
except urlresolvers.Resolver404:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
newurl = urlquote(new_url[1])
|
if new_url[0]:
|
||||||
if request.GET:
|
newurl = "%s://%s%s" % (
|
||||||
newurl += '?' + request.GET.urlencode()
|
request.is_secure() and 'https' or 'http',
|
||||||
return http.HttpResponsePermanentRedirect(newurl)
|
new_url[0], urlquote(new_url[1]))
|
||||||
|
else:
|
||||||
|
newurl = urlquote(new_url[1])
|
||||||
|
if request.GET:
|
||||||
|
newurl += '?' + request.GET.urlencode()
|
||||||
|
return http.HttpResponsePermanentRedirect(newurl)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -83,21 +83,15 @@ class Field(object):
|
|||||||
self.creation_counter = Field.creation_counter
|
self.creation_counter = Field.creation_counter
|
||||||
Field.creation_counter += 1
|
Field.creation_counter += 1
|
||||||
|
|
||||||
self.error_messages = self._build_error_messages(error_messages)
|
def set_class_error_messages(messages, klass):
|
||||||
|
|
||||||
def _build_error_messages(self, extra_error_messages):
|
|
||||||
error_messages = {}
|
|
||||||
|
|
||||||
def get_default_error_messages(klass):
|
|
||||||
for base_class in klass.__bases__:
|
for base_class in klass.__bases__:
|
||||||
get_default_error_messages(base_class)
|
set_class_error_messages(messages, base_class)
|
||||||
if hasattr(klass, 'default_error_messages'):
|
messages.update(getattr(klass, 'default_error_messages', {}))
|
||||||
error_messages.update(klass.default_error_messages)
|
|
||||||
|
|
||||||
get_default_error_messages(self.__class__)
|
messages = {}
|
||||||
if extra_error_messages:
|
set_class_error_messages(messages, self.__class__)
|
||||||
error_messages.update(extra_error_messages)
|
messages.update(error_messages or {})
|
||||||
return error_messages
|
self.error_messages = messages
|
||||||
|
|
||||||
def clean(self, value):
|
def clean(self, value):
|
||||||
"""
|
"""
|
||||||
@ -415,7 +409,7 @@ class EmailField(RegexField):
|
|||||||
try:
|
try:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
|
URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
|
||||||
except (ImportError, EnvironmentError):
|
except ImportError:
|
||||||
# It's OK if Django settings aren't configured.
|
# It's OK if Django settings aren't configured.
|
||||||
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||||
|
|
||||||
|
@ -3,17 +3,21 @@ Helper functions for creating Form classes from Django models
|
|||||||
and database field objects.
|
and database field objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_unicode
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from util import ValidationError
|
from util import ValidationError, ErrorList
|
||||||
from forms import BaseForm
|
from forms import BaseForm
|
||||||
from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
|
from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
|
||||||
from formsets import BaseFormSet, formset_for_form, DELETION_FIELD_NAME
|
from formsets import BaseFormSet, formset_for_form, DELETION_FIELD_NAME
|
||||||
from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
|
from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
|
||||||
'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
||||||
'formset_for_model', 'inline_formset',
|
'formset_for_model', 'inline_formset',
|
||||||
'ModelChoiceField', 'ModelMultipleChoiceField',
|
'ModelChoiceField', 'ModelMultipleChoiceField',
|
||||||
@ -82,6 +86,9 @@ def form_for_model(model, form=BaseForm, fields=None,
|
|||||||
determining the formfield for a given database field. It's a callable that
|
determining the formfield for a given database field. It's a callable that
|
||||||
takes a database Field instance and returns a form Field instance.
|
takes a database Field instance and returns a form Field instance.
|
||||||
"""
|
"""
|
||||||
|
warn("form_for_model is deprecated, use ModelForm instead.",
|
||||||
|
PendingDeprecationWarning,
|
||||||
|
stacklevel=3)
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
field_list = []
|
field_list = []
|
||||||
for f in opts.fields + opts.many_to_many:
|
for f in opts.fields + opts.many_to_many:
|
||||||
@ -109,6 +116,9 @@ def form_for_instance(instance, form=BaseForm, fields=None,
|
|||||||
takes a database Field instance, plus **kwargs, and returns a form Field
|
takes a database Field instance, plus **kwargs, and returns a form Field
|
||||||
instance with the given kwargs (i.e. 'initial').
|
instance with the given kwargs (i.e. 'initial').
|
||||||
"""
|
"""
|
||||||
|
warn("form_for_instance is deprecated, use ModelForm instead.",
|
||||||
|
PendingDeprecationWarning,
|
||||||
|
stacklevel=3)
|
||||||
model = instance.__class__
|
model = instance.__class__
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
field_list = []
|
field_list = []
|
||||||
@ -134,6 +144,155 @@ def form_for_fields(field_list):
|
|||||||
for f in field_list if f.editable])
|
for f in field_list if f.editable])
|
||||||
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
||||||
|
|
||||||
|
|
||||||
|
# ModelForms #################################################################
|
||||||
|
|
||||||
|
def model_to_dict(instance, fields=None, exclude=None):
|
||||||
|
"""
|
||||||
|
Returns a dict containing the data in ``instance`` suitable for passing as
|
||||||
|
a Form's ``initial`` keyword argument.
|
||||||
|
|
||||||
|
``fields`` is an optional list of field names. If provided, only the named
|
||||||
|
fields will be included in the returned dict.
|
||||||
|
|
||||||
|
``exclude`` is an optional list of field names. If provided, the named
|
||||||
|
fields will be excluded from the returned dict, even if they are listed in
|
||||||
|
the ``fields`` argument.
|
||||||
|
"""
|
||||||
|
# avoid a circular import
|
||||||
|
from django.db.models.fields.related import ManyToManyField
|
||||||
|
opts = instance._meta
|
||||||
|
data = {}
|
||||||
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
if not f.editable:
|
||||||
|
continue
|
||||||
|
if fields and not f.name in fields:
|
||||||
|
continue
|
||||||
|
if exclude and f.name in exclude:
|
||||||
|
continue
|
||||||
|
if isinstance(f, ManyToManyField):
|
||||||
|
# If the object doesn't have a primry key yet, just use an empty
|
||||||
|
# list for its m2m fields. Calling f.value_from_object will raise
|
||||||
|
# an exception.
|
||||||
|
if instance.pk is None:
|
||||||
|
data[f.name] = []
|
||||||
|
else:
|
||||||
|
# MultipleChoiceWidget needs a list of pks, not object instances.
|
||||||
|
data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
|
||||||
|
else:
|
||||||
|
data[f.name] = f.value_from_object(instance)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()):
|
||||||
|
"""
|
||||||
|
Returns a ``SortedDict`` containing form fields for the given model.
|
||||||
|
|
||||||
|
``fields`` is an optional list of field names. If provided, only the named
|
||||||
|
fields will be included in the returned fields.
|
||||||
|
|
||||||
|
``exclude`` is an optional list of field names. If provided, the named
|
||||||
|
fields will be excluded from the returned fields, even if they are listed
|
||||||
|
in the ``fields`` argument.
|
||||||
|
"""
|
||||||
|
# TODO: if fields is provided, it would be nice to return fields in that order
|
||||||
|
field_list = []
|
||||||
|
opts = model._meta
|
||||||
|
for f in opts.fields + opts.many_to_many:
|
||||||
|
if not f.editable:
|
||||||
|
continue
|
||||||
|
if fields and not f.name in fields:
|
||||||
|
continue
|
||||||
|
if exclude and f.name in exclude:
|
||||||
|
continue
|
||||||
|
formfield = formfield_callback(f)
|
||||||
|
if formfield:
|
||||||
|
field_list.append((f.name, formfield))
|
||||||
|
return SortedDict(field_list)
|
||||||
|
|
||||||
|
class ModelFormOptions(object):
|
||||||
|
def __init__(self, options=None):
|
||||||
|
self.model = getattr(options, 'model', None)
|
||||||
|
self.fields = getattr(options, 'fields', None)
|
||||||
|
self.exclude = getattr(options, 'exclude', None)
|
||||||
|
|
||||||
|
class ModelFormMetaclass(type):
|
||||||
|
def __new__(cls, name, bases, attrs):
|
||||||
|
# TODO: no way to specify formfield_callback yet, do we need one, or
|
||||||
|
# should it be a special case for the admin?
|
||||||
|
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
|
||||||
|
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
|
||||||
|
|
||||||
|
# If this class is subclassing another Form, add that Form's fields.
|
||||||
|
# Note that we loop over the bases in *reverse*. This is necessary in
|
||||||
|
# order to preserve the correct order of fields.
|
||||||
|
for base in bases[::-1]:
|
||||||
|
if hasattr(base, 'base_fields'):
|
||||||
|
fields = base.base_fields.items() + fields
|
||||||
|
declared_fields = SortedDict(fields)
|
||||||
|
|
||||||
|
opts = ModelFormOptions(attrs.get('Meta', None))
|
||||||
|
attrs['_meta'] = opts
|
||||||
|
|
||||||
|
# Don't allow more than one Meta model defenition in bases. The fields
|
||||||
|
# would be generated correctly, but the save method won't deal with
|
||||||
|
# more than one object.
|
||||||
|
base_models = []
|
||||||
|
for base in bases:
|
||||||
|
base_opts = getattr(base, '_meta', None)
|
||||||
|
base_model = getattr(base_opts, 'model', None)
|
||||||
|
if base_model is not None:
|
||||||
|
base_models.append(base_model)
|
||||||
|
if len(base_models) > 1:
|
||||||
|
raise ImproperlyConfigured("%s's base classes define more than one model." % name)
|
||||||
|
|
||||||
|
# If a model is defined, extract form fields from it and add them to base_fields
|
||||||
|
if attrs['_meta'].model is not None:
|
||||||
|
# Don't allow a subclass to define a Meta model if a parent class has.
|
||||||
|
# Technically the right fields would be generated, but the save
|
||||||
|
# method will not deal with more than one model.
|
||||||
|
for base in bases:
|
||||||
|
base_opts = getattr(base, '_meta', None)
|
||||||
|
base_model = getattr(base_opts, 'model', None)
|
||||||
|
if base_model is not None:
|
||||||
|
raise ImproperlyConfigured('%s defines more than one model.' % name)
|
||||||
|
model_fields = fields_for_model(opts.model, opts.fields, opts.exclude)
|
||||||
|
# fields declared in base classes override fields from the model
|
||||||
|
model_fields.update(declared_fields)
|
||||||
|
attrs['base_fields'] = model_fields
|
||||||
|
else:
|
||||||
|
attrs['base_fields'] = declared_fields
|
||||||
|
return type.__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
|
class BaseModelForm(BaseForm):
|
||||||
|
def __init__(self, instance, data=None, files=None, auto_id='id_%s', prefix=None,
|
||||||
|
initial=None, error_class=ErrorList, label_suffix=':'):
|
||||||
|
self.instance = instance
|
||||||
|
opts = self._meta
|
||||||
|
object_data = model_to_dict(instance, opts.fields, opts.exclude)
|
||||||
|
# if initial was provided, it should override the values from instance
|
||||||
|
if initial is not None:
|
||||||
|
object_data.update(initial)
|
||||||
|
BaseForm.__init__(self, data, files, auto_id, prefix, object_data, error_class, label_suffix)
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
"""
|
||||||
|
Saves this ``form``'s cleaned_data into model instance ``self.instance``.
|
||||||
|
|
||||||
|
If commit=True, then the changes to ``instance`` will be saved to the
|
||||||
|
database. Returns ``instance``.
|
||||||
|
"""
|
||||||
|
if self.instance.pk is None:
|
||||||
|
fail_message = 'created'
|
||||||
|
else:
|
||||||
|
fail_message = 'changed'
|
||||||
|
return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
|
||||||
|
|
||||||
|
class ModelForm(BaseModelForm):
|
||||||
|
__metaclass__ = ModelFormMetaclass
|
||||||
|
|
||||||
|
|
||||||
|
# Fields #####################################################################
|
||||||
|
|
||||||
class QuerySetIterator(object):
|
class QuerySetIterator(object):
|
||||||
def __init__(self, queryset, empty_label, cache_choices):
|
def __init__(self, queryset, empty_label, cache_choices):
|
||||||
self.queryset = queryset
|
self.queryset = queryset
|
||||||
|
@ -38,7 +38,7 @@ def get_object_or_404(klass, *args, **kwargs):
|
|||||||
klass may be a Model, Manager, or QuerySet object. All other passed
|
klass may be a Model, Manager, or QuerySet object. All other passed
|
||||||
arguments and keyword arguments are used in the get() query.
|
arguments and keyword arguments are used in the get() query.
|
||||||
|
|
||||||
Note: Like with get(), an AssertionError will be raised if more than one
|
Note: Like with get(), an MultipleObjectsReturned will be raised if more than one
|
||||||
object is found.
|
object is found.
|
||||||
"""
|
"""
|
||||||
queryset = _get_queryset(klass)
|
queryset = _get_queryset(klass)
|
||||||
|
@ -23,12 +23,14 @@ class Context(object):
|
|||||||
yield d
|
yield d
|
||||||
|
|
||||||
def push(self):
|
def push(self):
|
||||||
self.dicts = [{}] + self.dicts
|
d = {}
|
||||||
|
self.dicts = [d] + self.dicts
|
||||||
|
return d
|
||||||
|
|
||||||
def pop(self):
|
def pop(self):
|
||||||
if len(self.dicts) == 1:
|
if len(self.dicts) == 1:
|
||||||
raise ContextPopException
|
raise ContextPopException
|
||||||
del self.dicts[0]
|
return self.dicts.pop(0)
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
"Set a variable in the current context"
|
"Set a variable in the current context"
|
||||||
@ -62,6 +64,7 @@ class Context(object):
|
|||||||
def update(self, other_dict):
|
def update(self, other_dict):
|
||||||
"Like dict.update(). Pushes an entire dictionary's keys and values onto the context."
|
"Like dict.update(). Pushes an entire dictionary's keys and values onto the context."
|
||||||
self.dicts = [other_dict] + self.dicts
|
self.dicts = [other_dict] + self.dicts
|
||||||
|
return other_dict
|
||||||
|
|
||||||
# This is a function rather than module-level procedural code because we only
|
# This is a function rather than module-level procedural code because we only
|
||||||
# want it to execute if somebody uses RequestContext.
|
# want it to execute if somebody uses RequestContext.
|
||||||
|
@ -696,7 +696,7 @@ filesizeformat.is_safe = True
|
|||||||
|
|
||||||
def pluralize(value, arg=u's'):
|
def pluralize(value, arg=u's'):
|
||||||
"""
|
"""
|
||||||
Returns a plural suffix if the value is not 1. By default, 's' is used as
|
Returns a plural suffix if the value is not 1. By default, 's' is used as
|
||||||
the suffix:
|
the suffix:
|
||||||
|
|
||||||
* If value is 0, vote{{ value|pluralize }} displays "0 votes".
|
* If value is 0, vote{{ value|pluralize }} displays "0 votes".
|
||||||
|
@ -354,8 +354,19 @@ class _OutputRedirectingPdb(pdb.Pdb):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, out):
|
def __init__(self, out):
|
||||||
self.__out = out
|
self.__out = out
|
||||||
|
self.__debugger_used = False
|
||||||
pdb.Pdb.__init__(self)
|
pdb.Pdb.__init__(self)
|
||||||
|
|
||||||
|
def set_trace(self):
|
||||||
|
self.__debugger_used = True
|
||||||
|
pdb.Pdb.set_trace(self)
|
||||||
|
|
||||||
|
def set_continue(self):
|
||||||
|
# Calling set_continue unconditionally would break unit test coverage
|
||||||
|
# reporting, as Bdb.set_continue calls sys.settrace(None).
|
||||||
|
if self.__debugger_used:
|
||||||
|
pdb.Pdb.set_continue(self)
|
||||||
|
|
||||||
def trace_dispatch(self, *args):
|
def trace_dispatch(self, *args):
|
||||||
# Redirect stdout to the given stream.
|
# Redirect stdout to the given stream.
|
||||||
save_stdout = sys.stdout
|
save_stdout = sys.stdout
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
class MergeDict(object):
|
class MergeDict(object):
|
||||||
"""
|
"""
|
||||||
A simple class for creating new "virtual" dictionaries that actualy look
|
A simple class for creating new "virtual" dictionaries that actually look
|
||||||
up values in more than one dictionary, passed in the constructor.
|
up values in more than one dictionary, passed in the constructor.
|
||||||
"""
|
"""
|
||||||
def __init__(self, *dicts):
|
def __init__(self, *dicts):
|
||||||
@ -215,7 +215,7 @@ class MultiValueDict(dict):
|
|||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
"""
|
"""
|
||||||
Returns the last data value for the passed key. If key doesn't exist
|
Returns the last data value for the passed key. If key doesn't exist
|
||||||
or value is an empty list, then default is returned.
|
or value is an empty list, then default is returned.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
@ -228,7 +228,7 @@ class MultiValueDict(dict):
|
|||||||
|
|
||||||
def getlist(self, key):
|
def getlist(self, key):
|
||||||
"""
|
"""
|
||||||
Returns the list of values for the passed key. If key doesn't exist,
|
Returns the list of values for the passed key. If key doesn't exist,
|
||||||
then an empty list is returned.
|
then an empty list is returned.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -100,7 +100,7 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
|
|||||||
if safe_input:
|
if safe_input:
|
||||||
middle = mark_safe(middle)
|
middle = mark_safe(middle)
|
||||||
if middle.startswith('www.') or ('@' not in middle and not middle.startswith('http://') and \
|
if middle.startswith('www.') or ('@' not in middle and not middle.startswith('http://') and \
|
||||||
len(middle) > 0 and middle[0] in string.letters + string.digits and \
|
len(middle) > 0 and middle[0] in string.ascii_letters + string.digits and \
|
||||||
(middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))):
|
(middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))):
|
||||||
middle = '<a href="http://%s"%s>%s</a>' % (
|
middle = '<a href="http://%s"%s>%s</a>' % (
|
||||||
urlquote(middle, safe='/&=:;#?+'), nofollow_attr,
|
urlquote(middle, safe='/&=:;#?+'), nofollow_attr,
|
||||||
|
@ -21,7 +21,7 @@ def legacy_maxlength(max_length, maxlength):
|
|||||||
"""
|
"""
|
||||||
if maxlength is not None:
|
if maxlength is not None:
|
||||||
warn("maxlength is deprecated, use max_length instead.",
|
warn("maxlength is deprecated, use max_length instead.",
|
||||||
PendingDeprecationWarning,
|
DeprecationWarning,
|
||||||
stacklevel=3)
|
stacklevel=3)
|
||||||
if max_length is not None:
|
if max_length is not None:
|
||||||
raise TypeError("field can not take both the max_length"
|
raise TypeError("field can not take both the max_length"
|
||||||
|
@ -34,15 +34,12 @@ class SafeString(str, SafeData):
|
|||||||
Concatenating a safe string with another safe string or safe unicode
|
Concatenating a safe string with another safe string or safe unicode
|
||||||
object is safe. Otherwise, the result is no longer safe.
|
object is safe. Otherwise, the result is no longer safe.
|
||||||
"""
|
"""
|
||||||
|
t = super(SafeString, self).__add__(rhs)
|
||||||
if isinstance(rhs, SafeUnicode):
|
if isinstance(rhs, SafeUnicode):
|
||||||
return SafeUnicode(self + rhs)
|
return SafeUnicode(t)
|
||||||
elif isinstance(rhs, SafeString):
|
elif isinstance(rhs, SafeString):
|
||||||
return SafeString(self + rhs)
|
return SafeString(t)
|
||||||
else:
|
return t
|
||||||
return super(SafeString, self).__add__(rhs)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _proxy_method(self, *args, **kwargs):
|
def _proxy_method(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -69,10 +66,10 @@ class SafeUnicode(unicode, SafeData):
|
|||||||
Concatenating a safe unicode object with another safe string or safe
|
Concatenating a safe unicode object with another safe string or safe
|
||||||
unicode object is safe. Otherwise, the result is no longer safe.
|
unicode object is safe. Otherwise, the result is no longer safe.
|
||||||
"""
|
"""
|
||||||
|
t = super(SafeUnicode, self).__add__(rhs)
|
||||||
if isinstance(rhs, SafeData):
|
if isinstance(rhs, SafeData):
|
||||||
return SafeUnicode(self + rhs)
|
return SafeUnicode(t)
|
||||||
else:
|
return t
|
||||||
return super(SafeUnicode, self).__add__(rhs)
|
|
||||||
|
|
||||||
def _proxy_method(self, *args, **kwargs):
|
def _proxy_method(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -131,7 +131,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
|
|||||||
if start is not None and end is not None:
|
if start is not None and end is not None:
|
||||||
unicode_str = exc_value.args[1]
|
unicode_str = exc_value.args[1]
|
||||||
unicode_hint = smart_unicode(unicode_str[max(start-5, 0):min(end+5, len(unicode_str))], 'ascii', errors='replace')
|
unicode_hint = smart_unicode(unicode_str[max(start-5, 0):min(end+5, len(unicode_str))], 'ascii', errors='replace')
|
||||||
|
from django import get_version
|
||||||
t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
|
t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
|
||||||
c = Context({
|
c = Context({
|
||||||
'exception_type': exc_type.__name__,
|
'exception_type': exc_type.__name__,
|
||||||
@ -144,6 +144,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
|
|||||||
'settings': get_safe_settings(),
|
'settings': get_safe_settings(),
|
||||||
'sys_executable' : sys.executable,
|
'sys_executable' : sys.executable,
|
||||||
'sys_version_info' : '%d.%d.%d' % sys.version_info[0:3],
|
'sys_version_info' : '%d.%d.%d' % sys.version_info[0:3],
|
||||||
|
'django_version_info' : get_version(),
|
||||||
'template_info': template_info,
|
'template_info': template_info,
|
||||||
'template_does_not_exist': template_does_not_exist,
|
'template_does_not_exist': template_does_not_exist,
|
||||||
'loader_debug_info': loader_debug_info,
|
'loader_debug_info': loader_debug_info,
|
||||||
@ -275,6 +276,8 @@ TECHNICAL_500_TEMPLATE = """
|
|||||||
#requestinfo h3 { margin-bottom:-1em; }
|
#requestinfo h3 { margin-bottom:-1em; }
|
||||||
.error { background: #ffc; }
|
.error { background: #ffc; }
|
||||||
.specific { color:#cc3300; font-weight:bold; }
|
.specific { color:#cc3300; font-weight:bold; }
|
||||||
|
h2 span.commands { font-size:.7em;}
|
||||||
|
span.commands a:link {color:#5E5694;}
|
||||||
</style>
|
</style>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
//<!--
|
//<!--
|
||||||
@ -409,9 +412,7 @@ TECHNICAL_500_TEMPLATE = """
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="traceback">
|
<div id="traceback">
|
||||||
<h2>Traceback <span>(innermost last)</span></h2>
|
<h2>Traceback <span class="commands"><a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></span></h2>
|
||||||
<div class="commands"><a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></div>
|
|
||||||
<br/>
|
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
<div id="browserTraceback">
|
<div id="browserTraceback">
|
||||||
<ul class="traceback">
|
<ul class="traceback">
|
||||||
@ -456,27 +457,51 @@ TECHNICAL_500_TEMPLATE = """
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="pastebinTraceback" class="pastebin">
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>
|
|
||||||
Traceback (most recent call last):<br/>
|
|
||||||
{% for frame in frames %}
|
|
||||||
File "{{ frame.filename }}" in {{ frame.function }}<br/>
|
|
||||||
{% if frame.context_line %}
|
|
||||||
{{ frame.lineno }}. {{ frame.context_line|escape }}<br/>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}<br/>
|
|
||||||
{{ exception_type }} at {{ request.path|escape }}<br/>
|
|
||||||
{{ exception_value|escape }}</code>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
<form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
|
||||||
|
<div id="pastebinTraceback" class="pastebin">
|
||||||
|
<input type="hidden" name="language" value="PythonConsole" />
|
||||||
|
<input type="hidden" name="title" value="{{ exception_type|escape }} at {{ request.path|escape }}" />
|
||||||
|
<input type="hidden" name="source" value="Django Dpaste Agent" />
|
||||||
|
<input type="hidden" name="poster" value="Django" />
|
||||||
|
<textarea name="content" id="traceback_area" cols="140" rows="25">
|
||||||
|
Environment:
|
||||||
|
|
||||||
|
Request Method: {{ request.META.REQUEST_METHOD }}
|
||||||
|
Request URL: {{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }}
|
||||||
|
Django Version: {{ django_version_info }}
|
||||||
|
Python Version: {{ sys_version_info }}
|
||||||
|
Installed Applications:
|
||||||
|
{{ settings.INSTALLED_APPS|pprint }}
|
||||||
|
Installed Middleware:
|
||||||
|
{{ settings.MIDDLEWARE_CLASSES|pprint }}
|
||||||
|
|
||||||
|
{% if template_does_not_exist %}Template Loader Error:
|
||||||
|
{% if loader_debug_info %}Django tried loading these templates, in this order:
|
||||||
|
{% for loader in loader_debug_info %}Using loader {{ loader.loader }}:
|
||||||
|
{% for t in loader.templates %}{{ t.name }} (File {% if t.exists %}exists{% else %}does not exist{% endif %})
|
||||||
|
{% endfor %}{% endfor %}
|
||||||
|
{% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty!
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}{% if template_info %}
|
||||||
|
Template error:
|
||||||
|
In template {{ template_info.name }}, error at line {{ template_info.line }}
|
||||||
|
{{ template_info.message }}{% for source_line in template_info.source_lines %}{% ifequal source_line.0 template_info.line %}
|
||||||
|
{{ source_line.0 }} : {{ template_info.before }} {{ template_info.during }} {{ template_info.after }}
|
||||||
|
{% else %}
|
||||||
|
{{ source_line.0 }} : {{ source_line.1 }}
|
||||||
|
{% endifequal %}{% endfor %}{% endif %}
|
||||||
|
Traceback:
|
||||||
|
{% for frame in frames %}File "{{ frame.filename|escape }}" in {{ frame.function|escape }}
|
||||||
|
{% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
Exception Type: {{ exception_type|escape }} at {{ request.path|escape }}
|
||||||
|
Exception Value: {{ exception_value|escape }}
|
||||||
|
</textarea>
|
||||||
|
<br/><br/>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Send to DPaste">
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="requestinfo">
|
<div id="requestinfo">
|
||||||
|
@ -9,7 +9,7 @@ from django.http import Http404, HttpResponse
|
|||||||
|
|
||||||
def archive_index(request, queryset, date_field, num_latest=15,
|
def archive_index(request, queryset, date_field, num_latest=15,
|
||||||
template_name=None, template_loader=loader,
|
template_name=None, template_loader=loader,
|
||||||
extra_context=None, allow_empty=False, context_processors=None,
|
extra_context=None, allow_empty=True, context_processors=None,
|
||||||
mimetype=None, allow_future=False, template_object_name='latest'):
|
mimetype=None, allow_future=False, template_object_name='latest'):
|
||||||
"""
|
"""
|
||||||
Generic top-level archive of date-based objects.
|
Generic top-level archive of date-based objects.
|
||||||
|
@ -5,7 +5,7 @@ from django.core.paginator import ObjectPaginator, InvalidPage
|
|||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
def object_list(request, queryset, paginate_by=None, page=None,
|
def object_list(request, queryset, paginate_by=None, page=None,
|
||||||
allow_empty=False, template_name=None, template_loader=loader,
|
allow_empty=True, template_name=None, template_loader=loader,
|
||||||
extra_context=None, context_processors=None, template_object_name='object',
|
extra_context=None, context_processors=None, template_object_name='object',
|
||||||
mimetype=None):
|
mimetype=None):
|
||||||
"""
|
"""
|
||||||
|
@ -58,6 +58,17 @@ See the `csrf documentation`_.
|
|||||||
|
|
||||||
.. _csrf documentation: ../csrf/
|
.. _csrf documentation: ../csrf/
|
||||||
|
|
||||||
|
flatpages
|
||||||
|
=========
|
||||||
|
|
||||||
|
A framework for managing simple "flat" HTML content in a database.
|
||||||
|
|
||||||
|
See the `flatpages documentation`_.
|
||||||
|
|
||||||
|
.. _flatpages documentation: ../flatpages/
|
||||||
|
|
||||||
|
Requires the sites_ contrib package to be installed as well.
|
||||||
|
|
||||||
formtools
|
formtools
|
||||||
=========
|
=========
|
||||||
|
|
||||||
@ -162,17 +173,6 @@ Examples (when 'today' is 17 Feb 2007):
|
|||||||
|
|
||||||
.. _DATE_FORMAT: ../settings/#date_format
|
.. _DATE_FORMAT: ../settings/#date_format
|
||||||
|
|
||||||
flatpages
|
|
||||||
=========
|
|
||||||
|
|
||||||
A framework for managing simple "flat" HTML content in a database.
|
|
||||||
|
|
||||||
See the `flatpages documentation`_.
|
|
||||||
|
|
||||||
.. _flatpages documentation: ../flatpages/
|
|
||||||
|
|
||||||
Requires the sites_ contrib package to be installed as well.
|
|
||||||
|
|
||||||
localflavor
|
localflavor
|
||||||
===========
|
===========
|
||||||
|
|
||||||
@ -211,6 +211,15 @@ See the `redirects documentation`_.
|
|||||||
|
|
||||||
.. _redirects documentation: ../redirects/
|
.. _redirects documentation: ../redirects/
|
||||||
|
|
||||||
|
sessions
|
||||||
|
========
|
||||||
|
|
||||||
|
A framework for storing data in anonymous sessions.
|
||||||
|
|
||||||
|
See the `sessions documentation`_.
|
||||||
|
|
||||||
|
.. _sessions documentation: ../sessions/
|
||||||
|
|
||||||
sites
|
sites
|
||||||
=====
|
=====
|
||||||
|
|
||||||
@ -240,6 +249,16 @@ See the `syndication documentation`_.
|
|||||||
|
|
||||||
.. _syndication documentation: ../syndication_feeds/
|
.. _syndication documentation: ../syndication_feeds/
|
||||||
|
|
||||||
|
webdesign
|
||||||
|
=========
|
||||||
|
|
||||||
|
Helpers and utilties targeted primarily at web designers rather than
|
||||||
|
web developers.
|
||||||
|
|
||||||
|
See the `web design helpers documentation`_.
|
||||||
|
|
||||||
|
.. _web design helpers documentation: ../webdesign/
|
||||||
|
|
||||||
Other add-ons
|
Other add-ons
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
@ -154,10 +154,13 @@ custom methods:
|
|||||||
|
|
||||||
* ``get_profile()`` -- Returns a site-specific profile for this user.
|
* ``get_profile()`` -- Returns a site-specific profile for this user.
|
||||||
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
|
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
|
||||||
doesn't allow profiles.
|
doesn't allow profiles. For information on how to define a
|
||||||
|
site-specific user profile, see the section on `storing additional
|
||||||
|
user information`_ below.
|
||||||
|
|
||||||
.. _Django model: ../model-api/
|
.. _Django model: ../model-api/
|
||||||
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
|
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
|
||||||
|
.. _storing additional user information: #storing-additional-information-about-users
|
||||||
|
|
||||||
Manager functions
|
Manager functions
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
@ -269,6 +272,45 @@ you need to create a superuser after that via the command line, you can use the
|
|||||||
Make sure to substitute ``/path/to/`` with the path to the Django codebase on
|
Make sure to substitute ``/path/to/`` with the path to the Django codebase on
|
||||||
your filesystem.
|
your filesystem.
|
||||||
|
|
||||||
|
Storing additional information about users
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
If you'd like to store additional information related to your users,
|
||||||
|
Django provides a method to specify a site-specific related model --
|
||||||
|
termed a "user profile" -- for this purpose.
|
||||||
|
|
||||||
|
To make use of this feature, define a model with fields for the
|
||||||
|
additional information you'd like to store, or additional methods
|
||||||
|
you'd like to have available, and also add a ``ForeignKey`` from your
|
||||||
|
model to the ``User`` model, specified with ``unique=True`` to ensure
|
||||||
|
only one instance of your model can be created for each ``User``.
|
||||||
|
|
||||||
|
To indicate that this model is the user profile model for a given
|
||||||
|
site, fill in the setting ``AUTH_PROFILE_MODULE`` with a string
|
||||||
|
consisting of the following items, separated by a dot:
|
||||||
|
|
||||||
|
1. The (normalized to lower-case) name of the application in which the
|
||||||
|
user profile model is defined (in other words, an all-lowercase
|
||||||
|
version of the name which was passed to ``manage.py startapp`` to
|
||||||
|
create the application).
|
||||||
|
|
||||||
|
2. The (normalized to lower-case) name of the model class.
|
||||||
|
|
||||||
|
For example, if the profile model was a class named ``UserProfile``
|
||||||
|
and was defined inside an application named ``accounts``, the
|
||||||
|
appropriate setting would be::
|
||||||
|
|
||||||
|
AUTH_PROFILE_MODULE = 'accounts.userprofile'
|
||||||
|
|
||||||
|
When a user profile model has been defined and specified in this
|
||||||
|
manner, each ``User`` object will have a method -- ``get_profile()``
|
||||||
|
-- which returns the instance of the user profile model associated
|
||||||
|
with that ``User``.
|
||||||
|
|
||||||
|
For more information, see `Chapter 12 of the Django book`_.
|
||||||
|
|
||||||
|
.. _Chapter 12 of the Django book: http://www.djangobook.com/en/beta/chapter12/#cn226
|
||||||
|
|
||||||
Authentication in Web requests
|
Authentication in Web requests
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
@ -337,6 +379,17 @@ This example shows how you might use both ``authenticate()`` and ``login()``::
|
|||||||
else:
|
else:
|
||||||
# Return an 'invalid login' error message.
|
# Return an 'invalid login' error message.
|
||||||
|
|
||||||
|
.. admonition:: Calling ``authenticate()`` first
|
||||||
|
|
||||||
|
When you're manually logging a user in, you *must* call
|
||||||
|
``authenticate()`` before you call ``login()``; ``authenticate()``
|
||||||
|
sets an attribute on the ``User`` noting which authentication
|
||||||
|
backend successfully authenticated that user (see the `backends
|
||||||
|
documentation`_ for details), and this information is needed later
|
||||||
|
during the login process.
|
||||||
|
|
||||||
|
.. _backends documentation: #other-authentication-sources
|
||||||
|
|
||||||
Manually checking a user's password
|
Manually checking a user's password
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
@ -168,6 +168,10 @@ development or testing environments. For example::
|
|||||||
|
|
||||||
CACHE_BACKEND = 'simple:///'
|
CACHE_BACKEND = 'simple:///'
|
||||||
|
|
||||||
|
**New in Django development version:** This cache backend is deprecated and
|
||||||
|
will be removed in a future release. New code should use the ``locmem`` backend
|
||||||
|
instead.
|
||||||
|
|
||||||
Dummy caching (for development)
|
Dummy caching (for development)
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ We've got two roles here:
|
|||||||
* Ticket triagers: community members who keep track of tickets, making
|
* Ticket triagers: community members who keep track of tickets, making
|
||||||
sure the tickets are always categorized correctly.
|
sure the tickets are always categorized correctly.
|
||||||
|
|
||||||
Second, note the four triage stages:
|
Second, note the five triage stages:
|
||||||
|
|
||||||
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
|
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
|
||||||
examine the ticket and move it along.
|
examine the ticket and move it along.
|
||||||
@ -254,9 +254,14 @@ Second, note the four triage stages:
|
|||||||
3. Once a ticket is ruled to be approved for fixing, it's moved into the
|
3. Once a ticket is ruled to be approved for fixing, it's moved into the
|
||||||
"Accepted" stage. This stage is where all the real work gets done.
|
"Accepted" stage. This stage is where all the real work gets done.
|
||||||
|
|
||||||
4. If a ticket has an associated patch (see below), a triager will review the
|
4. A ticket might be moved to the "Someday/Maybe" state if it's an
|
||||||
patch. If the patch is complete, it'll be marked as "ready for checkin" so
|
enhancement request we are willing to consider if a good patch is
|
||||||
that a core developer knows to review and check in the patches.
|
written. Such tickets are not high priority.
|
||||||
|
|
||||||
|
5. If a ticket has an associated patch (see below), a triager will review
|
||||||
|
the patch. If the patch is complete, it'll be marked as "ready for
|
||||||
|
checkin" so that a core developer knows to review and check in the
|
||||||
|
patches.
|
||||||
|
|
||||||
The second part of this workflow involves a set of flags the describe what the
|
The second part of this workflow involves a set of flags the describe what the
|
||||||
ticket has or needs in order to be "ready for checkin":
|
ticket has or needs in order to be "ready for checkin":
|
||||||
@ -654,9 +659,8 @@ To run the tests, ``cd`` to the ``tests/`` directory and type::
|
|||||||
./runtests.py --settings=path.to.django.settings
|
./runtests.py --settings=path.to.django.settings
|
||||||
|
|
||||||
Yes, the unit tests need a settings module, but only for database connection
|
Yes, the unit tests need a settings module, but only for database connection
|
||||||
info, with the ``DATABASE_ENGINE`` setting. You will also need a ``ROOT_URLCONF``
|
info, with the ``DATABASE_ENGINE`` setting. You'll also need a ``ROOT_URLCONF``
|
||||||
setting (its value is ignored; it just needs to be present) and a ``SITE_ID``
|
setting (its value is ignored; it just needs to be present).
|
||||||
setting (any non-zero integer value will do) in order for all the tests to pass.
|
|
||||||
|
|
||||||
If you're using the ``sqlite3`` database backend, no further settings are
|
If you're using the ``sqlite3`` database backend, no further settings are
|
||||||
needed. A temporary database will be created in memory when running the tests.
|
needed. A temporary database will be created in memory when running the tests.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
===================
|
===================
|
||||||
Custom Model Fields
|
Custom model fields
|
||||||
===================
|
===================
|
||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
@ -8,9 +8,10 @@ Introduction
|
|||||||
============
|
============
|
||||||
|
|
||||||
The `model reference`_ documentation explains how to use Django's standard
|
The `model reference`_ documentation explains how to use Django's standard
|
||||||
field classes. For many purposes, those classes are all you'll need. Sometimes,
|
field classes -- ``CharField``, ``DateField``, etc. For many purposes, those
|
||||||
though, the Django version won't meet your precise requirements, or you'll want
|
classes are all you'll need. Sometimes, though, the Django version won't meet
|
||||||
to use a field that is entirely different from those shipped with Django.
|
your precise requirements, or you'll want to use a field that is entirely
|
||||||
|
different from those shipped with Django.
|
||||||
|
|
||||||
Django's built-in field types don't cover every possible database column type --
|
Django's built-in field types don't cover every possible database column type --
|
||||||
only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure
|
only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure
|
||||||
@ -27,10 +28,10 @@ Our example object
|
|||||||
Creating custom fields requires a bit of attention to detail. To make things
|
Creating custom fields requires a bit of attention to detail. To make things
|
||||||
easier to follow, we'll use a consistent example throughout this document.
|
easier to follow, we'll use a consistent example throughout this document.
|
||||||
Suppose you have a Python object representing the deal of cards in a hand of
|
Suppose you have a Python object representing the deal of cards in a hand of
|
||||||
Bridge_. It doesn't matter if you don't know how to play Bridge. You only need
|
Bridge_. (Don't worry, you don't know how to play Bridge to follow this
|
||||||
to know that 52 cards are dealt out equally to four players, who are
|
example. You only need to know that 52 cards are dealt out equally to four
|
||||||
traditionally called *north*, *east*, *south* and *west*. Our class looks
|
players, who are traditionally called *north*, *east*, *south* and *west*.)
|
||||||
something like this::
|
Our class looks something like this::
|
||||||
|
|
||||||
class Hand(object):
|
class Hand(object):
|
||||||
def __init__(self, north, east, south, west):
|
def __init__(self, north, east, south, west):
|
||||||
@ -42,10 +43,9 @@ something like this::
|
|||||||
|
|
||||||
# ... (other possibly useful methods omitted) ...
|
# ... (other possibly useful methods omitted) ...
|
||||||
|
|
||||||
This is just an ordinary Python class, nothing Django-specific about it. We
|
This is just an ordinary Python class, with nothing Django-specific about it.
|
||||||
would like to be able to things like this in our models (we assume the
|
We'd like to be able to things like this in our models (we assume the ``hand``
|
||||||
``hand`` attribute on the model is an instance of ``Hand``)::
|
attribute on the model is an instance of ``Hand``)::
|
||||||
|
|
||||||
|
|
||||||
example = MyModel.objects.get(pk=1)
|
example = MyModel.objects.get(pk=1)
|
||||||
print example.hand.north
|
print example.hand.north
|
||||||
@ -72,7 +72,7 @@ model support for existing classes where you cannot change the source code.
|
|||||||
.. _PostgreSQL custom types: http://www.postgresql.org/docs/8.2/interactive/sql-createtype.html
|
.. _PostgreSQL custom types: http://www.postgresql.org/docs/8.2/interactive/sql-createtype.html
|
||||||
.. _Bridge: http://en.wikipedia.org/wiki/Contract_bridge
|
.. _Bridge: http://en.wikipedia.org/wiki/Contract_bridge
|
||||||
|
|
||||||
Background Theory
|
Background theory
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Database storage
|
Database storage
|
||||||
@ -87,7 +87,7 @@ that falls out fairly naturally once you have the database side under control).
|
|||||||
Fields in a model must somehow be converted to fit into an existing database
|
Fields in a model must somehow be converted to fit into an existing database
|
||||||
column type. Different databases provide different sets of valid column types,
|
column type. Different databases provide different sets of valid column types,
|
||||||
but the rule is still the same: those are the only types you have to work
|
but the rule is still the same: those are the only types you have to work
|
||||||
with. Anything you want to store in the database must fit into one of
|
with. Anything you want to store in the database must fit into one of
|
||||||
those types.
|
those types.
|
||||||
|
|
||||||
Normally, you're either writing a Django field to match a particular database
|
Normally, you're either writing a Django field to match a particular database
|
||||||
@ -95,10 +95,9 @@ column type, or there's a fairly straightforward way to convert your data to,
|
|||||||
say, a string.
|
say, a string.
|
||||||
|
|
||||||
For our ``Hand`` example, we could convert the card data to a string of 104
|
For our ``Hand`` example, we could convert the card data to a string of 104
|
||||||
characters by concatenating all the cards together in a pre-determined order.
|
characters by concatenating all the cards together in a pre-determined order --
|
||||||
Say, all the *north* cards first, then the *east*, *south* and *west* cards, in
|
say, all the *north* cards first, then the *east*, *south* and *west* cards. So
|
||||||
that order. So ``Hand`` objects can be saved to text or character columns in
|
``Hand`` objects can be saved to text or character columns in the database.
|
||||||
the database.
|
|
||||||
|
|
||||||
What does a field class do?
|
What does a field class do?
|
||||||
---------------------------
|
---------------------------
|
||||||
@ -109,12 +108,12 @@ mean model fields and not `form fields`_) are subclasses of
|
|||||||
field is common to all fields -- name, help text, validator lists, uniqueness
|
field is common to all fields -- name, help text, validator lists, uniqueness
|
||||||
and so forth. Storing all that information is handled by ``Field``. We'll get
|
and so forth. Storing all that information is handled by ``Field``. We'll get
|
||||||
into the precise details of what ``Field`` can do later on; for now, suffice it
|
into the precise details of what ``Field`` can do later on; for now, suffice it
|
||||||
to say that everything descends from ``Field`` and then customises key pieces
|
to say that everything descends from ``Field`` and then customizes key pieces
|
||||||
of the class behaviour.
|
of the class behavior.
|
||||||
|
|
||||||
.. _form fields: ../newforms/#fields
|
.. _form fields: ../newforms/#fields
|
||||||
|
|
||||||
It's important to realise that a Django field class is not what is stored in
|
It's important to realize that a Django field class is not what is stored in
|
||||||
your model attributes. The model attributes contain normal Python objects. The
|
your model attributes. The model attributes contain normal Python objects. The
|
||||||
field classes you define in a model are actually stored in the ``Meta`` class
|
field classes you define in a model are actually stored in the ``Meta`` class
|
||||||
when the model class is created (the precise details of how this is done are
|
when the model class is created (the precise details of how this is done are
|
||||||
@ -127,31 +126,35 @@ Keep this in mind when creating your own custom fields. The Django ``Field``
|
|||||||
subclass you write provides the machinery for converting between your Python
|
subclass you write provides the machinery for converting between your Python
|
||||||
instances and the database/serializer values in various ways (there are
|
instances and the database/serializer values in various ways (there are
|
||||||
differences between storing a value and using a value for lookups, for
|
differences between storing a value and using a value for lookups, for
|
||||||
example). If this sounds a bit tricky, don't worry. It will hopefully become
|
example). If this sounds a bit tricky, don't worry -- it will become clearer in
|
||||||
clearer in the examples below. Just remember that you will often end up
|
the examples below. Just remember that you will often end up creating two
|
||||||
creating two classes when you want a custom field. The first class is the
|
classes when you want a custom field:
|
||||||
Python object that your users will manipulate. They will assign it to the model
|
|
||||||
attribute, they will read from it for displaying purposes, things like that.
|
* The first class is the Python object that your users will manipulate.
|
||||||
This is the ``Hand`` class in our example. The second class is the ``Field``
|
They will assign it to the model attribute, they will read from it for
|
||||||
subclass. This is the class that knows how to convert your first class back and
|
displaying purposes, things like that. This is the ``Hand`` class in our
|
||||||
forth between its permanent storage form and the Python form.
|
example.
|
||||||
|
|
||||||
|
* The second class is the ``Field`` subclass. This is the class that knows
|
||||||
|
how to convert your first class back and forth between its permanent
|
||||||
|
storage form and the Python form.
|
||||||
|
|
||||||
Writing a ``Field`` subclass
|
Writing a ``Field`` subclass
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
When you are planning your ``Field`` subclass, first give some thought to
|
When planning your ``Field`` subclass, first give some thought to which
|
||||||
which existing field your new field is most similar to. Can you subclass an
|
existing ``Field`` class your new field is most similar to. Can you subclass an
|
||||||
existing Django field and save yourself some work? If not, you should subclass the ``Field`` class, from which everything is descended.
|
existing Django field and save yourself some work? If not, you should subclass
|
||||||
|
the ``Field`` class, from which everything is descended.
|
||||||
|
|
||||||
Initialising your new field is a matter of separating out any arguments that
|
Initializing your new field is a matter of separating out any arguments that
|
||||||
are specific to your case from the common arguments and passing the latter to
|
are specific to your case from the common arguments and passing the latter to
|
||||||
the ``__init__()`` method of ``Field`` (or your parent class).
|
the ``__init__()`` method of ``Field`` (or your parent class).
|
||||||
|
|
||||||
In our example, the Django field we create is going to be called
|
In our example, we'll call our field ``HandField``. (It's a good idea to call
|
||||||
``HandField``. It's not a bad idea to use a similar naming scheme to Django's
|
your ``Field`` subclass ``(Something)Field``, so it's easily identifiable as a
|
||||||
fields so that our new class is identifiable and yet clearly related to the
|
``Field`` subclass.) It doesn't behave like any existing field, so we'll
|
||||||
``Hand`` class it is wrapping. It doesn't behave like any existing field, so
|
subclass directly from ``Field``::
|
||||||
we'll subclass directly from ``Field``::
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
@ -160,7 +163,7 @@ we'll subclass directly from ``Field``::
|
|||||||
kwargs['max_length'] = 104
|
kwargs['max_length'] = 104
|
||||||
super(HandField, self).__init__(*args, **kwargs)
|
super(HandField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
Our ``HandField`` will accept most of the standard field options (see the list
|
Our ``HandField`` accept most of the standard field options (see the list
|
||||||
below), but we ensure it has a fixed length, since it only needs to hold 52
|
below), but we ensure it has a fixed length, since it only needs to hold 52
|
||||||
card values plus their suits; 104 characters in total.
|
card values plus their suits; 104 characters in total.
|
||||||
|
|
||||||
@ -171,40 +174,40 @@ card values plus their suits; 104 characters in total.
|
|||||||
(``auto_now`` being set implies ``editable=False``). No error is raised in
|
(``auto_now`` being set implies ``editable=False``). No error is raised in
|
||||||
this case.
|
this case.
|
||||||
|
|
||||||
This behaviour simplifies the field classes, because they don't need to
|
This behavior simplifies the field classes, because they don't need to
|
||||||
check for options that aren't necessary. They just pass all the options to
|
check for options that aren't necessary. They just pass all the options to
|
||||||
the parent class and then don't use them later on. It is up to you whether
|
the parent class and then don't use them later on. It's up to you whether
|
||||||
you want your fields to be more strict about the options they select, or
|
you want your fields to be more strict about the options they select, or
|
||||||
to use the simpler, more permissive behaviour of the current fields.
|
to use the simpler, more permissive behavior of the current fields.
|
||||||
|
|
||||||
The ``Field.__init__()`` method takes the following parameters, in this
|
The ``Field.__init__()`` method takes the following parameters, in this
|
||||||
order:
|
order:
|
||||||
|
|
||||||
- ``verbose_name``
|
* ``verbose_name``
|
||||||
- ``name``
|
* ``name``
|
||||||
- ``primary_key``
|
* ``primary_key``
|
||||||
- ``max_length``
|
* ``max_length``
|
||||||
- ``unique``
|
* ``unique``
|
||||||
- ``blank``
|
* ``blank``
|
||||||
- ``null``
|
* ``null``
|
||||||
- ``db_index``
|
* ``db_index``
|
||||||
- ``core``
|
* ``core``
|
||||||
- ``rel``: Used for related fields (like ``ForeignKey``). For advanced use
|
* ``rel``: Used for related fields (like ``ForeignKey``). For advanced use
|
||||||
only.
|
only.
|
||||||
- ``default``
|
* ``default``
|
||||||
- ``editable``
|
* ``editable``
|
||||||
- ``serialize``: If ``False``, the field will not be serialized when the
|
* ``serialize``: If ``False``, the field will not be serialized when the
|
||||||
model is passed to Django's serializers_. Defaults to ``True``.
|
model is passed to Django's serializers_. Defaults to ``True``.
|
||||||
- ``prepopulate_from``
|
* ``prepopulate_from``
|
||||||
- ``unique_for_date``
|
* ``unique_for_date``
|
||||||
- ``unique_for_month``
|
* ``unique_for_month``
|
||||||
- ``unique_for_year``
|
* ``unique_for_year``
|
||||||
- ``validator_list``
|
* ``validator_list``
|
||||||
- ``choices``
|
* ``choices``
|
||||||
- ``radio_admin``
|
* ``radio_admin``
|
||||||
- ``help_text``
|
* ``help_text``
|
||||||
- ``db_column``
|
* ``db_column``
|
||||||
- ``db_tablespace``: Currently only used with the Oracle backend and only
|
* ``db_tablespace``: Currently only used with the Oracle backend and only
|
||||||
for index creation. You can usually ignore this option.
|
for index creation. You can usually ignore this option.
|
||||||
|
|
||||||
All of the options without an explanation in the above list have the same
|
All of the options without an explanation in the above list have the same
|
||||||
@ -218,22 +221,19 @@ The ``SubfieldBase`` metaclass
|
|||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
As we indicated in the introduction_, field subclasses are often needed for
|
As we indicated in the introduction_, field subclasses are often needed for
|
||||||
two reasons. Either to take advantage of a custom database column type, or to
|
two reasons: either to take advantage of a custom database column type, or to
|
||||||
handle complex Python types. A combination of the two is obviously also
|
handle complex Python types. Obviously, a combination of the two is also
|
||||||
possible. If you are only working with custom database column types and your
|
possible. If you're only working with custom database column types and your
|
||||||
model fields appear in Python as standard Python types direct from the
|
model fields appear in Python as standard Python types direct from the
|
||||||
database backend, you don't need to worry about this section.
|
database backend, you don't need to worry about this section.
|
||||||
|
|
||||||
If you are handling custom Python types, such as our ``Hand`` class, we need
|
If you're handling custom Python types, such as our ``Hand`` class, we need
|
||||||
to make sure that when Django initialises an instance of our model and assigns
|
to make sure that when Django initializes an instance of our model and assigns
|
||||||
a database value to our custom field attribute we convert that value into the
|
a database value to our custom field attribute, we convert that value into the
|
||||||
appropriate Python object. The details of how this happens internally are a
|
appropriate Python object. The details of how this happens internally are a
|
||||||
little complex. For the field writer, though, things are fairly simple. Make
|
little complex, but the code you need to write in your ``Field`` class is
|
||||||
sure your field subclass uses ``django.db.models.SubfieldBase`` as its
|
simple: make sure your field subclass uses ``django.db.models.SubfieldBase`` as
|
||||||
metaclass. This ensures that the ``to_python()`` method, documented below_,
|
its metaclass::
|
||||||
will always be called when the attribute is initialised.
|
|
||||||
|
|
||||||
Our ``HandField`` class now looks like this::
|
|
||||||
|
|
||||||
class HandField(models.Field):
|
class HandField(models.Field):
|
||||||
__metaclass__ = models.SubfieldBase
|
__metaclass__ = models.SubfieldBase
|
||||||
@ -241,16 +241,18 @@ Our ``HandField`` class now looks like this::
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
|
This ensures that the ``to_python()`` method, documented below_, will always be
|
||||||
|
called when the attribute is initialized.
|
||||||
|
|
||||||
.. _below: #to-python-self-value
|
.. _below: #to-python-self-value
|
||||||
|
|
||||||
Useful methods
|
Useful methods
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Once you've created your ``Field`` subclass and setup up the
|
Once you've created your ``Field`` subclass and set up up the
|
||||||
``__metaclass__``, if necessary, there are a few standard methods you need to
|
``__metaclass__``, you might consider overriding a few standard methods,
|
||||||
consider overriding. Which of these you need to implement will depend on you
|
depending on your field's behavior. The list of methods below is in
|
||||||
particular field behaviour. The list below is in approximately decreasing
|
approximately decreasing order of importance, so start from the top.
|
||||||
order of importance, so start from the top.
|
|
||||||
|
|
||||||
``db_type(self)``
|
``db_type(self)``
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
@ -337,23 +339,32 @@ field. You are then responsible for creating the column in the right table in
|
|||||||
some other way, of course, but this gives you a way to tell Django to get out
|
some other way, of course, but this gives you a way to tell Django to get out
|
||||||
of the way.
|
of the way.
|
||||||
|
|
||||||
|
|
||||||
``to_python(self, value)``
|
``to_python(self, value)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Converts between all the ways your field can receive its initial value and the
|
Converts a value as returned by your database (or a serializer) to a Python
|
||||||
Python object you want to end up with. The default version just returns
|
object.
|
||||||
``value``, so is useful is the database backend returns the data already in
|
|
||||||
the correct form (a Python string, for example).
|
|
||||||
|
|
||||||
Normally, you will need to override this method. As a general rule, be
|
The default implementation simply returns ``value``, for the common case in
|
||||||
prepared to accept an instance of the right type (e.g. ``Hand`` in our ongoing
|
which the database backend already returns data in the correct format (as a
|
||||||
example), a string (from a deserializer, for example), and whatever the
|
Python string, for example).
|
||||||
database wrapper returns for the column type you are using.
|
|
||||||
|
|
||||||
In our ``HandField`` class, we are storing the data in a character field in
|
If your custom ``Field`` class deals with data structures that are more complex
|
||||||
the database, so we need to be able to process strings and ``Hand`` instances
|
than strings, dates, integers or floats, then you'll need to override this
|
||||||
in ``to_python()``::
|
method. As a general rule, the method should deal gracefully with any of the
|
||||||
|
following arguments:
|
||||||
|
|
||||||
|
* An instance of the correct type (e.g., ``Hand`` in our ongoing example).
|
||||||
|
|
||||||
|
* A string (e.g., from a deserializer).
|
||||||
|
|
||||||
|
* Whatever the database returns for the column type you're using.
|
||||||
|
|
||||||
|
In our ``HandField`` class, we're storing the data as a VARCHAR field in the
|
||||||
|
database, so we need to be able to process strings and ``Hand`` instances in
|
||||||
|
``to_python()``::
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
class HandField(models.Field):
|
class HandField(models.Field):
|
||||||
# ...
|
# ...
|
||||||
@ -362,14 +373,14 @@ in ``to_python()``::
|
|||||||
if isinstance(value, Hand):
|
if isinstance(value, Hand):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# The string case
|
# The string case.
|
||||||
p1 = re.compile('.{26}')
|
p1 = re.compile('.{26}')
|
||||||
p2 = re.compile('..')
|
p2 = re.compile('..')
|
||||||
args = [p2.findall(x) for x in p1.findall(value)]
|
args = [p2.findall(x) for x in p1.findall(value)]
|
||||||
return Hand(*args)
|
return Hand(*args)
|
||||||
|
|
||||||
Notice that we always return a ``Hand`` instance from this method. That is the
|
Notice that we always return a ``Hand`` instance from this method. That's the
|
||||||
Python object we want to store in the model's attribute.
|
Python object type we want to store in the model's attribute.
|
||||||
|
|
||||||
``get_db_prep_save(self, value)``
|
``get_db_prep_save(self, value)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -377,7 +388,7 @@ Python object we want to store in the model's attribute.
|
|||||||
This is the reverse of ``to_python()`` when working with the database backends
|
This is the reverse of ``to_python()`` when working with the database backends
|
||||||
(as opposed to serialization). The ``value`` parameter is the current value of
|
(as opposed to serialization). The ``value`` parameter is the current value of
|
||||||
the model's attribute (a field has no reference to its containing model, so it
|
the model's attribute (a field has no reference to its containing model, so it
|
||||||
cannot retrieve the value itself) and the method should return data in a
|
cannot retrieve the value itself), and the method should return data in a
|
||||||
format that can be used as a parameter in a query for the database backend.
|
format that can be used as a parameter in a query for the database backend.
|
||||||
|
|
||||||
For example::
|
For example::
|
||||||
@ -389,7 +400,6 @@ For example::
|
|||||||
return ''.join([''.join(l) for l in (self.north,
|
return ''.join([''.join(l) for l in (self.north,
|
||||||
self.east, self.south, self.west)])
|
self.east, self.south, self.west)])
|
||||||
|
|
||||||
|
|
||||||
``pre_save(self, model_instance, add)``
|
``pre_save(self, model_instance, add)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -399,10 +409,10 @@ The attribute name is in ``self.attname`` (this is set up by ``Field``). If
|
|||||||
the model is being saved to the database for the first time, the ``add``
|
the model is being saved to the database for the first time, the ``add``
|
||||||
parameter will be ``True``, otherwise it will be ``False``.
|
parameter will be ``True``, otherwise it will be ``False``.
|
||||||
|
|
||||||
Often you won't need to override this method. However, at times it can be very
|
You only need to override this method if you want to preprocess the value
|
||||||
useful. For example, the Django ``DateTimeField`` uses this method to set the
|
somehow, just before saving. For example, Django's ``DateTimeField`` uses this
|
||||||
attribute to the correct value before returning it in the cases when
|
method to set the attribute correctly in the case of ``auto_now`` or
|
||||||
``auto_now`` or ``auto_now_add`` are set on the field.
|
``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
|
||||||
@ -460,9 +470,9 @@ All of the ``kwargs`` dictionary is passed directly to the form field's
|
|||||||
``__init__()`` method. Normally, all you need to do is set up a good default
|
``__init__()`` method. Normally, all you need to do is set up a good default
|
||||||
for the ``form_class`` argument and then delegate further handling to the
|
for the ``form_class`` argument and then delegate further handling to the
|
||||||
parent class. This might require you to write a custom form field (and even a
|
parent class. This might require you to write a custom form field (and even a
|
||||||
form widget). See the `forms documentation`_ for information about this. Also
|
form widget). See the `forms documentation`_ for information about this, and
|
||||||
have a look at ``django.contrib.localflavor`` for some examples of custom
|
take a look at the code in ``django.contrib.localflavor`` for some examples of
|
||||||
widgets.
|
custom widgets.
|
||||||
|
|
||||||
Continuing our ongoing example, we can write the ``formfield()`` method as::
|
Continuing our ongoing example, we can write the ``formfield()`` method as::
|
||||||
|
|
||||||
@ -471,14 +481,14 @@ Continuing our ongoing example, we can write the ``formfield()`` method as::
|
|||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
# This is a fairly standard way to set up some defaults
|
# This is a fairly standard way to set up some defaults
|
||||||
# whilst letting the caller override them.
|
# while letting the caller override them.
|
||||||
defaults = {'form_class': MyFormField}
|
defaults = {'form_class': MyFormField}
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return super(HandField, self).formfield(**defaults)
|
return super(HandField, self).formfield(**defaults)
|
||||||
|
|
||||||
This assumes we have some ``MyFormField`` field class (which has its own
|
This assumes we're imported a ``MyFormField`` field class (which has its own
|
||||||
default widget) imported. This document doesn't cover the details of writing
|
default widget). This document doesn't cover the details of writing custom form
|
||||||
custom form fields.
|
fields.
|
||||||
|
|
||||||
.. _helper functions: ../newforms/#generating-forms-for-models
|
.. _helper functions: ../newforms/#generating-forms-for-models
|
||||||
.. _forms documentation: ../newforms/
|
.. _forms documentation: ../newforms/
|
||||||
@ -490,7 +500,7 @@ Returns a string giving the name of the ``Field`` subclass we are emulating at
|
|||||||
the database level. This is used to determine the type of database column for
|
the database level. This is used to determine the type of database column for
|
||||||
simple cases.
|
simple cases.
|
||||||
|
|
||||||
If you have created a ``db_type()`` method, you do not need to worry about
|
If you have created a ``db_type()`` method, you don't need to worry about
|
||||||
``get_internal_type()`` -- it won't be used much. Sometimes, though, your
|
``get_internal_type()`` -- it won't be used much. Sometimes, though, your
|
||||||
database storage is similar in type to some other field, so you can use that
|
database storage is similar in type to some other field, so you can use that
|
||||||
other field's logic to create the right column.
|
other field's logic to create the right column.
|
||||||
@ -512,7 +522,7 @@ the database backend you are using -- that is, it doesn't appear in
|
|||||||
be used by the serializer, but the default ``db_type()`` method will return
|
be used by the serializer, but the default ``db_type()`` method will return
|
||||||
``None``. See the documentation of ``db_type()`` above_ for reasons why this
|
``None``. See the documentation of ``db_type()`` above_ for reasons why this
|
||||||
might be useful. Putting a descriptive string in as the type of the field for
|
might be useful. Putting a descriptive string in as the type of the field for
|
||||||
the serializer is a useful idea if you are ever going to be using the
|
the serializer is a useful idea if you're ever going to be using the
|
||||||
serializer output in some other place, outside of Django.
|
serializer output in some other place, outside of Django.
|
||||||
|
|
||||||
.. _above: #db-type-self
|
.. _above: #db-type-self
|
||||||
@ -528,7 +538,7 @@ serializer output in some other place, outside of Django.
|
|||||||
Returns a dictionary, mapping the field's attribute name to a flattened string
|
Returns a dictionary, mapping the field's attribute name to a flattened string
|
||||||
version of the data. This method has some internal uses that aren't of
|
version of the data. This method has some internal uses that aren't of
|
||||||
interest to use here (mostly having to do with manipulators). For our
|
interest to use here (mostly having to do with manipulators). For our
|
||||||
purposes, it is sufficient to return a one item dictionary that maps the
|
purposes, it's sufficient to return a one item dictionary that maps the
|
||||||
attribute name to a string.
|
attribute name to a string.
|
||||||
|
|
||||||
This method is used by the serializers to convert the field into a string for
|
This method is used by the serializers to convert the field into a string for
|
||||||
@ -549,19 +559,20 @@ we can reuse some existing conversion code::
|
|||||||
Some general advice
|
Some general advice
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Writing a custom field can be a tricky process sometimes, particularly if you
|
Writing a custom field can be a tricky process, particularly if you're doing
|
||||||
are doing complex conversions between your Python types and your database and
|
complex conversions between your Python types and your database and
|
||||||
serialization formats. A couple of tips to make things go more smoothly:
|
serialization formats. Here are a couple of tips to make things go more
|
||||||
|
smoothly:
|
||||||
|
|
||||||
1. Look at the existing Django fields (in
|
1. Look at the existing Django fields (in
|
||||||
``django/db/models/fields/__init__.py``) for inspiration. Try to find a field
|
``django/db/models/fields/__init__.py``) for inspiration. Try to find a
|
||||||
that is already close to what you want and extend it a little bit, in
|
field that's similar to what you want and extend it a little bit,
|
||||||
preference to creating an entirely new field from scratch.
|
instead of creating an entirely new field from scratch.
|
||||||
|
|
||||||
2. Put a ``__str__()`` or ``__unicode__()`` method on the class you are
|
|
||||||
wrapping up as a field. There are a lot of places where the default behaviour
|
|
||||||
of the field code is to call ``force_unicode()`` on the value (in our
|
|
||||||
examples in this document, ``value`` would be a ``Hand`` instance, not a
|
|
||||||
``HandField``). So if your ``__unicode__()`` method automatically converts to
|
|
||||||
the string form of your Python object, you can save yourself a lot of work.
|
|
||||||
|
|
||||||
|
2. Put a ``__str__()`` or ``__unicode__()`` method on the class you're
|
||||||
|
wrapping up as a field. There are a lot of places where the default
|
||||||
|
behavior of the field code is to call ``force_unicode()`` on the value.
|
||||||
|
(In our examples in this document, ``value`` would be a ``Hand``
|
||||||
|
instance, not a ``HandField``). So if your ``__unicode__()`` method
|
||||||
|
automatically converts to the string form of your Python object, you can
|
||||||
|
save yourself a lot of work.
|
||||||
|
@ -258,6 +258,12 @@ many-to-many table would be stored in the ``indexes`` tablespace. The ``data``
|
|||||||
field would also generate an index, but no tablespace for it is specified, so
|
field would also generate an index, but no tablespace for it is specified, so
|
||||||
it would be stored in the model tablespace ``tables`` by default.
|
it would be stored in the model tablespace ``tables`` by default.
|
||||||
|
|
||||||
|
The settings.py file supports two additional options to specify
|
||||||
|
default values for the db_tablespace options. This is useful for
|
||||||
|
setting a tablespace for the Django internal apps and other
|
||||||
|
contributed applications. These options are ``DEFAULT_TABLESPACE``
|
||||||
|
and ``DEFAULT_INDEX_TABLESPACE``.
|
||||||
|
|
||||||
Django does not create the tablespaces for you. Please refer to `Oracle's
|
Django does not create the tablespaces for you. Please refer to `Oracle's
|
||||||
documentation`_ for details on creating and managing tablespaces.
|
documentation`_ for details on creating and managing tablespaces.
|
||||||
|
|
||||||
|
@ -253,9 +253,11 @@ For example::
|
|||||||
|
|
||||||
The class has the following methods:
|
The class has the following methods:
|
||||||
|
|
||||||
* ``send()`` sends the message, using either the connection that is
|
* ``send(fail_silently=False)`` sends the message, using either
|
||||||
specified in the ``connection`` attribute, or creating a new connection
|
the connection that is specified in the ``connection``
|
||||||
if none already exists.
|
attribute, or creating a new connection if none already
|
||||||
|
exists. If the keyword argument ``fail_silently`` is ``True``,
|
||||||
|
exceptions raised while sending the message will be quashed.
|
||||||
|
|
||||||
* ``message()`` constructs a ``django.core.mail.SafeMIMEText`` object (a
|
* ``message()`` constructs a ``django.core.mail.SafeMIMEText`` object (a
|
||||||
subclass of Python's ``email.MIMEText.MIMEText`` class) or a
|
subclass of Python's ``email.MIMEText.MIMEText`` class) or a
|
||||||
|
418
docs/form_for_model.txt
Normal file
418
docs/form_for_model.txt
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
Generating forms for models
|
||||||
|
===========================
|
||||||
|
|
||||||
|
If you're building a database-driven app, chances are you'll have forms that
|
||||||
|
map closely to Django models. For instance, you might have a ``BlogComment``
|
||||||
|
model, and you want to create a form that lets people submit comments. In this
|
||||||
|
case, it would be redundant to define the field types in your form, because
|
||||||
|
you've already defined the fields in your model.
|
||||||
|
|
||||||
|
For this reason, Django provides a few helper functions that let you create a
|
||||||
|
``Form`` class from a Django model.
|
||||||
|
|
||||||
|
``form_for_model()``
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The method ``django.newforms.form_for_model()`` creates a form based on the
|
||||||
|
definition of a specific model. Pass it the model class, and it will return a
|
||||||
|
``Form`` class that contains a form field for each model field.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
>>> from django.newforms import form_for_model
|
||||||
|
|
||||||
|
# Create the form class.
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
|
||||||
|
# Create an empty form instance.
|
||||||
|
>>> f = ArticleForm()
|
||||||
|
|
||||||
|
It bears repeating that ``form_for_model()`` takes the model *class*, not a
|
||||||
|
model instance, and it returns a ``Form`` *class*, not a ``Form`` instance.
|
||||||
|
|
||||||
|
Field types
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The generated ``Form`` class will have a form field for every model field. Each
|
||||||
|
model field has a corresponding default form field. For example, a
|
||||||
|
``CharField`` on a model is represented as a ``CharField`` on a form. A
|
||||||
|
model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
|
||||||
|
the full list of conversions:
|
||||||
|
|
||||||
|
=============================== ========================================
|
||||||
|
Model field Form field
|
||||||
|
=============================== ========================================
|
||||||
|
``AutoField`` Not represented in the form
|
||||||
|
``BooleanField`` ``BooleanField``
|
||||||
|
``CharField`` ``CharField`` with ``max_length`` set to
|
||||||
|
the model field's ``max_length``
|
||||||
|
``CommaSeparatedIntegerField`` ``CharField``
|
||||||
|
``DateField`` ``DateField``
|
||||||
|
``DateTimeField`` ``DateTimeField``
|
||||||
|
``DecimalField`` ``DecimalField``
|
||||||
|
``EmailField`` ``EmailField``
|
||||||
|
``FileField`` ``FileField``
|
||||||
|
``FilePathField`` ``CharField``
|
||||||
|
``FloatField`` ``FloatField``
|
||||||
|
``ForeignKey`` ``ModelChoiceField`` (see below)
|
||||||
|
``ImageField`` ``ImageField``
|
||||||
|
``IntegerField`` ``IntegerField``
|
||||||
|
``IPAddressField`` ``IPAddressField``
|
||||||
|
``ManyToManyField`` ``ModelMultipleChoiceField`` (see
|
||||||
|
below)
|
||||||
|
``NullBooleanField`` ``CharField``
|
||||||
|
``PhoneNumberField`` ``USPhoneNumberField``
|
||||||
|
(from ``django.contrib.localflavor.us``)
|
||||||
|
``PositiveIntegerField`` ``IntegerField``
|
||||||
|
``PositiveSmallIntegerField`` ``IntegerField``
|
||||||
|
``SlugField`` ``CharField``
|
||||||
|
``SmallIntegerField`` ``IntegerField``
|
||||||
|
``TextField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
``TimeField`` ``TimeField``
|
||||||
|
``URLField`` ``URLField`` with ``verify_exists`` set
|
||||||
|
to the model field's ``verify_exists``
|
||||||
|
``USStateField`` ``CharField`` with
|
||||||
|
``widget=USStateSelect``
|
||||||
|
(``USStateSelect`` is from
|
||||||
|
``django.contrib.localflavor.us``)
|
||||||
|
``XMLField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
=============================== ========================================
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The ``FloatField`` form field and ``DecimalField`` model and form fields
|
||||||
|
are new in the development version.
|
||||||
|
|
||||||
|
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
||||||
|
types are special cases:
|
||||||
|
|
||||||
|
* ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``,
|
||||||
|
which is a ``ChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
* ``ManyToManyField`` is represented by
|
||||||
|
``django.newforms.ModelMultipleChoiceField``, which is a
|
||||||
|
``MultipleChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
In addition, each generated form field has attributes set as follows:
|
||||||
|
|
||||||
|
* If the model field has ``blank=True``, then ``required`` is set to
|
||||||
|
``False`` on the form field. Otherwise, ``required=True``.
|
||||||
|
|
||||||
|
* The form field's ``label`` is set to the ``verbose_name`` of the model
|
||||||
|
field, with the first character capitalized.
|
||||||
|
|
||||||
|
* The form field's ``help_text`` is set to the ``help_text`` of the model
|
||||||
|
field.
|
||||||
|
|
||||||
|
* If the model field has ``choices`` set, then the form field's ``widget``
|
||||||
|
will be set to ``Select``, with choices coming from the model field's
|
||||||
|
``choices``. The choices will normally include the blank choice which is
|
||||||
|
selected by default. If the field is required, this forces the user to
|
||||||
|
make a selection. The blank choice will not be included if the model
|
||||||
|
field has ``blank=False`` and an explicit ``default`` value (the
|
||||||
|
``default`` value will be initially selected instead).
|
||||||
|
|
||||||
|
Finally, note that you can override the form field used for a given model
|
||||||
|
field. See "Overriding the default field types" below.
|
||||||
|
|
||||||
|
A full example
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Consider this set of models::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
TITLE_CHOICES = (
|
||||||
|
('MR', 'Mr.'),
|
||||||
|
('MRS', 'Mrs.'),
|
||||||
|
('MS', 'Ms.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Author(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
|
||||||
|
birth_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Book(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
authors = models.ManyToManyField(Author)
|
||||||
|
|
||||||
|
With these models, a call to ``form_for_model(Author)`` would return a ``Form``
|
||||||
|
class equivalent to this::
|
||||||
|
|
||||||
|
class AuthorForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
title = forms.CharField(max_length=3,
|
||||||
|
widget=forms.Select(choices=TITLE_CHOICES))
|
||||||
|
birth_date = forms.DateField(required=False)
|
||||||
|
|
||||||
|
A call to ``form_for_model(Book)`` would return a ``Form`` class equivalent to
|
||||||
|
this::
|
||||||
|
|
||||||
|
class BookForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
|
||||||
|
|
||||||
|
The ``save()`` method
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Every form produced by ``form_for_model()`` also has a ``save()`` method. This
|
||||||
|
method creates and saves a database object from the data bound to the form. For
|
||||||
|
example::
|
||||||
|
|
||||||
|
# Create a form instance from POST data.
|
||||||
|
>>> f = ArticleForm(request.POST)
|
||||||
|
|
||||||
|
# Save a new Article object from the form's data.
|
||||||
|
>>> new_article = f.save()
|
||||||
|
|
||||||
|
Note that ``save()`` will raise a ``ValueError`` if the data in the form
|
||||||
|
doesn't validate -- i.e., ``if form.errors``.
|
||||||
|
|
||||||
|
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
||||||
|
accepts either ``True`` or ``False``. If you call ``save()`` with
|
||||||
|
``commit=False``, then it will return an object that hasn't yet been saved to
|
||||||
|
the database. In this case, it's up to you to call ``save()`` on the resulting
|
||||||
|
model instance. This is useful if you want to do custom processing on the
|
||||||
|
object before saving it. ``commit`` is ``True`` by default.
|
||||||
|
|
||||||
|
Another side effect of using ``commit=False`` is seen when your model has
|
||||||
|
a many-to-many relation with another model. If your model has a many-to-many
|
||||||
|
relation and you specify ``commit=False`` when you save a form, Django cannot
|
||||||
|
immediately save the form data for the many-to-many relation. This is because
|
||||||
|
it isn't possible to save many-to-many data for an instance until the instance
|
||||||
|
exists in the database.
|
||||||
|
|
||||||
|
To work around this problem, every time you save a form using ``commit=False``,
|
||||||
|
Django adds a ``save_m2m()`` method to the form created by ``form_for_model``.
|
||||||
|
After you've manually saved the instance produced by the form, you can invoke
|
||||||
|
``save_m2m()`` to save the many-to-many form data. For example::
|
||||||
|
|
||||||
|
# Create a form instance with POST data.
|
||||||
|
>>> f = AuthorForm(request.POST)
|
||||||
|
|
||||||
|
# Create, but don't save the new author instance.
|
||||||
|
>>> new_author = f.save(commit=False)
|
||||||
|
|
||||||
|
# Modify the author in some way.
|
||||||
|
>>> new_author.some_field = 'some_value'
|
||||||
|
|
||||||
|
# Save the new instance.
|
||||||
|
>>> new_author.save()
|
||||||
|
|
||||||
|
# Now, save the many-to-many data for the form.
|
||||||
|
>>> f.save_m2m()
|
||||||
|
|
||||||
|
Calling ``save_m2m()`` is only required if you use ``save(commit=False)``.
|
||||||
|
When you use a simple ``save()`` on a form, all data -- including
|
||||||
|
many-to-many data -- is saved without the need for any additional method calls.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
# Create a form instance with POST data.
|
||||||
|
>>> f = AuthorForm(request.POST)
|
||||||
|
|
||||||
|
# Create and save the new author instance. There's no need to do anything else.
|
||||||
|
>>> new_author = f.save()
|
||||||
|
|
||||||
|
Using an alternate base class
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you want to add custom methods to the form generated by
|
||||||
|
``form_for_model()``, write a class that extends ``django.newforms.BaseForm``
|
||||||
|
and contains your custom methods. Then, use the ``form`` argument to
|
||||||
|
``form_for_model()`` to tell it to use your custom form as its base class.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
# Create the new base class.
|
||||||
|
>>> class MyBase(BaseForm):
|
||||||
|
... def my_method(self):
|
||||||
|
... # Do whatever the method does
|
||||||
|
|
||||||
|
# Create the form class with a different base class.
|
||||||
|
>>> ArticleForm = form_for_model(Article, form=MyBase)
|
||||||
|
|
||||||
|
# Instantiate the form.
|
||||||
|
>>> f = ArticleForm()
|
||||||
|
|
||||||
|
# Use the base class method.
|
||||||
|
>>> f.my_method()
|
||||||
|
|
||||||
|
Using a subset of fields on the form
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
In some cases, you may not want all the model fields to appear on the generated
|
||||||
|
form. There are two ways of telling ``form_for_model()`` to use only a subset
|
||||||
|
of the model fields:
|
||||||
|
|
||||||
|
1. Set ``editable=False`` on the model field. As a result, *any* form
|
||||||
|
created from the model via ``form_for_model()`` will not include that
|
||||||
|
field.
|
||||||
|
|
||||||
|
2. Use the ``fields`` argument to ``form_for_model()``. This argument, if
|
||||||
|
given, should be a list of field names to include in the form.
|
||||||
|
|
||||||
|
For example, if you want a form for the ``Author`` model (defined above)
|
||||||
|
that includes only the ``name`` and ``title`` fields, you would specify
|
||||||
|
``fields`` like this::
|
||||||
|
|
||||||
|
PartialArticleForm = form_for_model(Author, fields=('name', 'title'))
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you specify ``fields`` when creating a form with ``form_for_model()``,
|
||||||
|
then the fields that are *not* specified will not be set by the form's
|
||||||
|
``save()`` method. Django will prevent any attempt to save an incomplete
|
||||||
|
model, so if the model does not allow the missing fields to be empty, and
|
||||||
|
does not provide a default value for the missing fields, any attempt to
|
||||||
|
``save()`` a ``form_for_model`` with missing fields will fail. To avoid
|
||||||
|
this failure, you must use ``save(commit=False)`` and manually set any
|
||||||
|
extra required fields::
|
||||||
|
|
||||||
|
instance = form.save(commit=False)
|
||||||
|
instance.required_field = 'new value'
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
See the `section on saving forms`_ for more details on using
|
||||||
|
``save(commit=False)``.
|
||||||
|
|
||||||
|
.. _section on saving forms: `The save() method`_
|
||||||
|
|
||||||
|
Overriding the default field types
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The default field types, as described in the "Field types" table above, are
|
||||||
|
sensible defaults; if you have a ``DateField`` in your model, chances are you'd
|
||||||
|
want that to be represented as a ``DateField`` in your form. But
|
||||||
|
``form_for_model()`` gives you the flexibility of changing the form field type
|
||||||
|
for a given model field. You do this by specifying a **formfield callback**.
|
||||||
|
|
||||||
|
A formfield callback is a function that, when provided with a model field,
|
||||||
|
returns a form field instance. When constructing a form, ``form_for_model()``
|
||||||
|
asks the formfield callback to provide form field types.
|
||||||
|
|
||||||
|
By default, ``form_for_model()`` calls the ``formfield()`` method on the model
|
||||||
|
field::
|
||||||
|
|
||||||
|
def default_callback(field, **kwargs):
|
||||||
|
return field.formfield(**kwargs)
|
||||||
|
|
||||||
|
The ``kwargs`` are any keyword arguments that might be passed to the form
|
||||||
|
field, such as ``required=True`` or ``label='Foo'``.
|
||||||
|
|
||||||
|
For example, if you wanted to use ``MyDateFormField`` for any ``DateField``
|
||||||
|
field on the model, you could define the callback::
|
||||||
|
|
||||||
|
>>> def my_callback(field, **kwargs):
|
||||||
|
... if isinstance(field, models.DateField):
|
||||||
|
... return MyDateFormField(**kwargs)
|
||||||
|
... else:
|
||||||
|
... return field.formfield(**kwargs)
|
||||||
|
|
||||||
|
>>> ArticleForm = form_for_model(Article, formfield_callback=my_callback)
|
||||||
|
|
||||||
|
Note that your callback needs to handle *all* possible model field types, not
|
||||||
|
just the ones that you want to behave differently to the default. That's why
|
||||||
|
this example has an ``else`` clause that implements the default behavior.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
The field that is passed into the ``formfield_callback`` function in
|
||||||
|
``form_for_model()`` and ``form_for_instance`` is the field instance from
|
||||||
|
your model's class. You **must not** alter that object at all; treat it
|
||||||
|
as read-only!
|
||||||
|
|
||||||
|
If you make any alterations to that object, it will affect any future
|
||||||
|
users of the model class, because you will have changed the field object
|
||||||
|
used to construct the class. This is almost certainly what you don't want
|
||||||
|
to have happen.
|
||||||
|
|
||||||
|
Finding the model associated with a form
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The model class that was used to construct the form is available
|
||||||
|
using the ``_model`` property of the generated form::
|
||||||
|
|
||||||
|
>>> ArticleForm = form_for_model(Article)
|
||||||
|
>>> ArticleForm._model
|
||||||
|
<class 'myapp.models.Article'>
|
||||||
|
|
||||||
|
``form_for_instance()``
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
``form_for_instance()`` is like ``form_for_model()``, but it takes a model
|
||||||
|
instance instead of a model class::
|
||||||
|
|
||||||
|
# Create an Author.
|
||||||
|
>>> a = Author(name='Joe Smith', title='MR', birth_date=None)
|
||||||
|
>>> a.save()
|
||||||
|
|
||||||
|
# Create a form for this particular Author.
|
||||||
|
>>> AuthorForm = form_for_instance(a)
|
||||||
|
|
||||||
|
# Instantiate the form.
|
||||||
|
>>> f = AuthorForm()
|
||||||
|
|
||||||
|
When a form created by ``form_for_instance()`` is created, the initial data
|
||||||
|
values for the form fields are drawn from the instance. However, this data is
|
||||||
|
not bound to the form. You will need to bind data to the form before the form
|
||||||
|
can be saved.
|
||||||
|
|
||||||
|
Unlike ``form_for_model()``, a choice field in form created by
|
||||||
|
``form_for_instance()`` will not include the blank choice if the respective
|
||||||
|
model field has ``blank=False``. The initial choice is drawn from the instance.
|
||||||
|
|
||||||
|
When you call ``save()`` on a form created by ``form_for_instance()``,
|
||||||
|
the database instance will be updated. As in ``form_for_model()``, ``save()``
|
||||||
|
will raise ``ValueError`` if the data doesn't validate.
|
||||||
|
|
||||||
|
``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback``
|
||||||
|
arguments that behave the same way as they do for ``form_for_model()``.
|
||||||
|
|
||||||
|
Let's modify the earlier `contact form`_ view example a little bit. Suppose we
|
||||||
|
have a ``Message`` model that holds each contact submission. Something like::
|
||||||
|
|
||||||
|
class Message(models.Model):
|
||||||
|
subject = models.CharField(max_length=100)
|
||||||
|
message = models.TextField()
|
||||||
|
sender = models.EmailField()
|
||||||
|
cc_myself = models.BooleanField(required=False)
|
||||||
|
|
||||||
|
You could use this model to create a form (using ``form_for_model()``). You
|
||||||
|
could also use existing ``Message`` instances to create a form for editing
|
||||||
|
messages. The `simple example view`_ can be changed slightly to accept the ``id`` value
|
||||||
|
of an existing ``Message`` and present it for editing::
|
||||||
|
|
||||||
|
def contact_edit(request, msg_id):
|
||||||
|
# Create the form from the message id.
|
||||||
|
message = get_object_or_404(Message, id=msg_id)
|
||||||
|
ContactForm = form_for_instance(message)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = ContactForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
return HttpResponseRedirect('/url/on_success/')
|
||||||
|
else:
|
||||||
|
form = ContactForm()
|
||||||
|
return render_to_response('contact.html', {'form': form})
|
||||||
|
|
||||||
|
Aside from how we create the ``ContactForm`` class here, the main point to
|
||||||
|
note is that the form display in the ``GET`` branch of the function
|
||||||
|
will use the values from the ``message`` instance as initial values for the
|
||||||
|
form field.
|
||||||
|
|
||||||
|
.. _contact form: ../newforms/#simple-view-example
|
||||||
|
.. _`simple example view`: ../newforms/#simple-view-example
|
||||||
|
|
||||||
|
When should you use ``form_for_model()`` and ``form_for_instance()``?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``form_for_model()`` and ``form_for_instance()`` functions are meant to be
|
||||||
|
shortcuts for the common case. If you want to create a form whose fields map to
|
||||||
|
more than one model, or a form that contains fields that *aren't* on a model,
|
||||||
|
you shouldn't use these shortcuts. Creating a ``Form`` class the "long" way
|
||||||
|
isn't that difficult, after all.
|
@ -188,7 +188,7 @@ a date in the *future* are not included unless you set ``allow_future`` to
|
|||||||
* ``allow_empty``: A boolean specifying whether to display the page if no
|
* ``allow_empty``: A boolean specifying whether to display the page if no
|
||||||
objects are available. If this is ``False`` and no objects are available,
|
objects are available. If this is ``False`` and no objects are available,
|
||||||
the view will raise a 404 instead of displaying an empty page. By
|
the view will raise a 404 instead of displaying an empty page. By
|
||||||
default, this is ``False``.
|
default, this is ``True``.
|
||||||
|
|
||||||
* ``context_processors``: A list of template-context processors to apply to
|
* ``context_processors``: A list of template-context processors to apply to
|
||||||
the view's template. See the `RequestContext docs`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
@ -718,7 +718,7 @@ A page representing a list of objects.
|
|||||||
* ``allow_empty``: A boolean specifying whether to display the page if no
|
* ``allow_empty``: A boolean specifying whether to display the page if no
|
||||||
objects are available. If this is ``False`` and no objects are available,
|
objects are available. If this is ``False`` and no objects are available,
|
||||||
the view will raise a 404 instead of displaying an empty page. By
|
the view will raise a 404 instead of displaying an empty page. By
|
||||||
default, this is ``False``.
|
default, this is ``True``.
|
||||||
|
|
||||||
* ``context_processors``: A list of template-context processors to apply to
|
* ``context_processors``: A list of template-context processors to apply to
|
||||||
the view's template. See the `RequestContext docs`_.
|
the view's template. See the `RequestContext docs`_.
|
||||||
|
@ -73,13 +73,17 @@ installed.
|
|||||||
|
|
||||||
If you plan to use Django's ``manage.py syncdb`` command to
|
If you plan to use Django's ``manage.py syncdb`` command to
|
||||||
automatically create database tables for your models, you'll need to
|
automatically create database tables for your models, you'll need to
|
||||||
ensure that Django has permission to create tables in the database
|
ensure that Django has permission to create and alter tables in the
|
||||||
you're using; if you plan to manually create the tables, you can
|
database you're using; if you plan to manually create the tables, you
|
||||||
simply grant Django ``SELECT``, ``INSERT``, ``UPDATE`` and ``DELETE``
|
can simply grant Django ``SELECT``, ``INSERT``, ``UPDATE`` and
|
||||||
permissions. Django does not issue ``ALTER TABLE`` statements, and so
|
``DELETE`` permissions. On some databases, Django will need to have
|
||||||
will not require permission to do so. If you will be using Django's
|
``ALTER TABLE`` privileges during ``syncdb`` (in order to create
|
||||||
`testing framework`_ with data fixtures, Django will need permission
|
foreign key constraints properly on databases which do not allow them
|
||||||
to create a temporary test database.
|
to be deferred), but will not issue ``ALTER TABLE`` statements on a
|
||||||
|
table once ``syncdb`` has finished setting it up.
|
||||||
|
|
||||||
|
If you will be using Django's `testing framework`_ with data fixtures,
|
||||||
|
Django will need permission to create a temporary test database.
|
||||||
|
|
||||||
.. _PostgreSQL: http://www.postgresql.org/
|
.. _PostgreSQL: http://www.postgresql.org/
|
||||||
.. _MySQL: http://www.mysql.com/
|
.. _MySQL: http://www.mysql.com/
|
||||||
|
654
docs/localflavor.txt
Normal file
654
docs/localflavor.txt
Normal file
@ -0,0 +1,654 @@
|
|||||||
|
==========================
|
||||||
|
The "local flavor" add-ons
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Django comes with assorted pieces of code that are useful only for a particular
|
||||||
|
country or culture. These pieces of code are organized as a set of
|
||||||
|
subpackages, named using `ISO 3166 country codes`_.
|
||||||
|
|
||||||
|
.. _ISO 3166 country codes: http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
|
||||||
|
|
||||||
|
Most of the ``localflavor`` add-ons are localized form components deriving from
|
||||||
|
the newforms_ framework. To use one of these localized components, just import
|
||||||
|
the relevant subpackage. For example, a form with a field for French telephone
|
||||||
|
numbers is created like so::
|
||||||
|
|
||||||
|
from django import newforms as forms
|
||||||
|
from django.contrib.localflavor import fr
|
||||||
|
|
||||||
|
class MyForm(forms.Form):
|
||||||
|
my_french_phone_no = fr.forms.FRPhoneNumberField()
|
||||||
|
|
||||||
|
Countries currently supported by ``localflavor`` are:
|
||||||
|
|
||||||
|
* Argentina_
|
||||||
|
* Australia_
|
||||||
|
* Brazil_
|
||||||
|
* Canada_
|
||||||
|
* Chile_
|
||||||
|
* Finland_
|
||||||
|
* France_
|
||||||
|
* Germany_
|
||||||
|
* Holland_
|
||||||
|
* Iceland_
|
||||||
|
* India_
|
||||||
|
* Italy_
|
||||||
|
* Japan_
|
||||||
|
* Mexico_
|
||||||
|
* Norway_
|
||||||
|
* Peru_
|
||||||
|
* Poland_
|
||||||
|
* Slovakia_
|
||||||
|
* `South Africa`_
|
||||||
|
* Spain_
|
||||||
|
* Switzerland_
|
||||||
|
* `United Kingdom`_
|
||||||
|
* `United States of America`_
|
||||||
|
|
||||||
|
.. _Argentina: `Argentina (django.contrib.localflavor.ar)`_
|
||||||
|
.. _Australia: `Australia (django.contrib.localflavor.au)`_
|
||||||
|
.. _Brazil: `Brazil (django.contrib.localflavor.br)`_
|
||||||
|
.. _Canada: `Canada (django.contrib.localflavor.ca)`_
|
||||||
|
.. _Chile: `Chile (django.contrib.localflavor.cl)`_
|
||||||
|
.. _Finland: `Finland (django.contrib.localflavor.fi)`_
|
||||||
|
.. _France: `France (django.contrib.localflavor.fr)`_
|
||||||
|
.. _Germany: `Germany (django.contrib.localflavor.de)`_
|
||||||
|
.. _Holland: `Holland (django.contrib.localflavor.nl)`_
|
||||||
|
.. _Iceland: `Iceland (django.contrib.localflavor.is\_)`_
|
||||||
|
.. _India: `India (django.contrib.localflavor.in\_)`_
|
||||||
|
.. _Italy: `Italy (django.contrib.localflavor.it)`_
|
||||||
|
.. _Japan: `Japan (django.contrib.localflavor.jp)`_
|
||||||
|
.. _Mexico: `Mexico (django.contrib.localflavor.mx)`_
|
||||||
|
.. _Norway: `Norway (django.contrib.localflavor.no)`_
|
||||||
|
.. _Peru: `Peru (django.contrib.localflavor.pe)`_
|
||||||
|
.. _Poland: `Poland (django.contrib.localflavor.pl)`_
|
||||||
|
.. _Slovakia: `Slovakia (django.contrib.localflavor.sk)`_
|
||||||
|
.. _South Africa: `South Africa (django.contrib.localflavor.za)`_
|
||||||
|
.. _Spain: `Spain (django.contrib.localflavor.es)`_
|
||||||
|
.. _Switzerland: `Switzerland (django.contrib.localflavor.ch)`_
|
||||||
|
.. _United Kingdom: `United Kingdom (django.contrib.localflavor.uk)`_
|
||||||
|
.. _United States of America: `United States of America (django.contrib.localflavor.us)`_
|
||||||
|
|
||||||
|
The ``localflavor`` add-on also includes the ``generic`` subpackage, containing
|
||||||
|
useful code that is not specific to one particular country or culture.
|
||||||
|
Currently, it defines date and date & time input fields based on those from
|
||||||
|
newforms_, but with non-US default formats. Here's an example of how to use
|
||||||
|
them::
|
||||||
|
|
||||||
|
from django import newforms as forms
|
||||||
|
from django.contrib.localflavor import generic
|
||||||
|
|
||||||
|
class MyForm(forms.Form):
|
||||||
|
my_date_field = generic.forms.DateField()
|
||||||
|
|
||||||
|
.. _newforms: ../newforms/
|
||||||
|
|
||||||
|
|
||||||
|
.. admonition:: Adding a Flavor
|
||||||
|
|
||||||
|
We'd love to add more of these to Django, so please create a ticket for
|
||||||
|
anything that you've found useful. Please use unicode objects
|
||||||
|
(``u'mystring'``) for strings, rather than setting the encoding in the file
|
||||||
|
(see any of the existing flavors for examples).
|
||||||
|
|
||||||
|
|
||||||
|
Argentina (``django.contrib.localflavor.ar``)
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
ARPostalCodeField
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A form field that validates input as either a classic four-digit Argentinian
|
||||||
|
postal code or a CPA_.
|
||||||
|
|
||||||
|
.. _CPA: http://www.correoargentino.com.ar/consulta_cpa/home.php
|
||||||
|
|
||||||
|
ARProvinceSelect
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Argentina's provinces as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Australia (``django.contrib.localflavor.au``)
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
AUPostCodeField
|
||||||
|
---------------
|
||||||
|
|
||||||
|
A form field that validates input as an Australian postcode.
|
||||||
|
|
||||||
|
AUPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as an Australian phone number. Valid numbers
|
||||||
|
have ten digits.
|
||||||
|
|
||||||
|
AUStateSelect
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Australian states/territories as its
|
||||||
|
choices.
|
||||||
|
|
||||||
|
|
||||||
|
Brazil (``django.contrib.localflavor.br``)
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
BRPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Brazilian phone number, with the format
|
||||||
|
XX-XXXX-XXXX.
|
||||||
|
|
||||||
|
BRZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as a Brazilian zip code, with the format
|
||||||
|
XXXXX-XXX.
|
||||||
|
|
||||||
|
BRStateSelect
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Brazilian states/territories as its
|
||||||
|
choices.
|
||||||
|
|
||||||
|
|
||||||
|
Canada (``django.contrib.localflavor.ca``)
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
CAPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Canadian phone number, with the format
|
||||||
|
XXX-XXX-XXXX.
|
||||||
|
|
||||||
|
CAPostalCodeField
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A form field that validates input as a Canadian postal code, with the format
|
||||||
|
XXX XXX.
|
||||||
|
|
||||||
|
CAProvinceField
|
||||||
|
---------------
|
||||||
|
|
||||||
|
A form field that validates input as a Canadian province name or abbreviation.
|
||||||
|
|
||||||
|
CASocialInsuranceNumberField
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Canadian Social Insurance Number (SIN).
|
||||||
|
A valid number must have the format XXX-XXX-XXXX and pass a `Luhn mod-10
|
||||||
|
checksum`_.
|
||||||
|
|
||||||
|
.. _Luhn mod-10 checksum: http://en.wikipedia.org/wiki/Luhn_algorithm
|
||||||
|
|
||||||
|
CAProvinceSelect
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Canadian provinces and territories as
|
||||||
|
its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Chile (``django.contrib.localflavor.cl``)
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
CLRutField
|
||||||
|
----------
|
||||||
|
|
||||||
|
A form field that validates input as a Chilean national identification number
|
||||||
|
('Rol Unico Tributario' or RUT). The valid format is XX.XXX.XXX-X.
|
||||||
|
|
||||||
|
CLRegionSelect
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Chilean regions (Regiones) as its
|
||||||
|
choices.
|
||||||
|
|
||||||
|
|
||||||
|
Finland (``django.contrib.localflavor.fi``)
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
FISocialSecurityNumber
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Finnish social security number.
|
||||||
|
|
||||||
|
FIZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as a Finnish zip code. Valid codes
|
||||||
|
consist of five digits.
|
||||||
|
|
||||||
|
FIMunicipalitySelect
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Finnish municipalities as its
|
||||||
|
choices.
|
||||||
|
|
||||||
|
|
||||||
|
France (``django.contrib.localflavor.fr``)
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
FRPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as a French local phone number. The
|
||||||
|
correct format is 0X XX XX XX XX. 0X.XX.XX.XX.XX and 0XXXXXXXXX validate
|
||||||
|
but are corrected to 0X XX XX XX XX.
|
||||||
|
|
||||||
|
FRZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as a French zip code. Valid codes
|
||||||
|
consist of five digits.
|
||||||
|
|
||||||
|
FRDepartmentSelect
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of French departments as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Germany (``django.contrib.localflavor.de``)
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
DEIdentityCardNumberField
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
A form field that validates input as a German identity card number
|
||||||
|
(Personalausweis_). Valid numbers have the format
|
||||||
|
XXXXXXXXXXX-XXXXXXX-XXXXXXX-X, with no group consisting entirely of zeroes.
|
||||||
|
|
||||||
|
.. _Personalausweis: http://de.wikipedia.org/wiki/Personalausweis
|
||||||
|
|
||||||
|
DEZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as a German zip code. Valid codes
|
||||||
|
consist of five digits.
|
||||||
|
|
||||||
|
DEStateSelect
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of German states as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Holland (``django.contrib.localflavor.nl``)
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
NLPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Dutch telephone number.
|
||||||
|
|
||||||
|
NLSofiNumberField
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A form field that validates input as a Dutch social security number
|
||||||
|
(SoFI/BSN).
|
||||||
|
|
||||||
|
NLZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as a Dutch zip code.
|
||||||
|
|
||||||
|
NLProvinceSelect
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Dutch provinces as its list of
|
||||||
|
choices.
|
||||||
|
|
||||||
|
|
||||||
|
Iceland (``django.contrib.localflavor.is_``)
|
||||||
|
============================================
|
||||||
|
|
||||||
|
ISIdNumberField
|
||||||
|
---------------
|
||||||
|
|
||||||
|
A form field that validates input as an Icelandic identification number
|
||||||
|
(kennitala). The format is XXXXXX-XXXX.
|
||||||
|
|
||||||
|
ISPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as an Icelandtic phone number (seven
|
||||||
|
digits with an optional hyphen or space after the first three digits).
|
||||||
|
|
||||||
|
ISPostalCodeSelect
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Icelandic postal codes as its
|
||||||
|
choices.
|
||||||
|
|
||||||
|
|
||||||
|
India (``django.contrib.localflavor.in_``)
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
INStateField
|
||||||
|
------------
|
||||||
|
|
||||||
|
A form field that validates input as an Indian state/territory name or
|
||||||
|
abbreviation. Input is normalized to the standard two-letter vehicle
|
||||||
|
registration abbreviation for the given state or territory.
|
||||||
|
|
||||||
|
INZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as an Indian zip code, with the
|
||||||
|
format XXXXXXX.
|
||||||
|
|
||||||
|
INStateSelect
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Indian states/territories as its
|
||||||
|
choices.
|
||||||
|
|
||||||
|
|
||||||
|
Italy (``django.contrib.localflavor.it``)
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
ITSocialSecurityNumberField
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
A form field that validates input as an Italian social security number
|
||||||
|
(`codice fiscale`_).
|
||||||
|
|
||||||
|
.. _codice fiscale: http://www.agenziaentrate.it/ilwwcm/connect/Nsi/Servizi/Codice+fiscale+-+tessera+sanitaria/Codice+fiscale/NSI+Informazioni+sulla+codificazione+delle+persone+fisiche
|
||||||
|
|
||||||
|
ITVatNumberField
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A form field that validates Italian VAT numbers (partita IVA).
|
||||||
|
|
||||||
|
ITZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as an Italian zip code. Valid codes
|
||||||
|
must have five digits.
|
||||||
|
|
||||||
|
ITProvinceSelect
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Italian provinces as its choices.
|
||||||
|
|
||||||
|
ITRegionSelect
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Italian regions as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Japan (``django.contrib.localflavor.jp``)
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
JPPostalCodeField
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A form field that validates input as a Japanese postcode.
|
||||||
|
It accepts seven digits, with or without a hyphen.
|
||||||
|
|
||||||
|
JPPrefectureSelect
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Japanese prefectures as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Mexico (``django.contrib.localflavor.mx``)
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
MXStateSelect
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Mexican states as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Norway (``django.contrib.localflavor.no``)
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
NOSocialSecurityNumber
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Norwegian social security number
|
||||||
|
(personnummer_).
|
||||||
|
|
||||||
|
.. _personnummer: http://no.wikipedia.org/wiki/Personnummer
|
||||||
|
|
||||||
|
NOZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as a Norwegian zip code. Valid codes
|
||||||
|
have four digits.
|
||||||
|
|
||||||
|
NOMunicipalitySelect
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Norwegian municipalities (fylker) as
|
||||||
|
its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Peru (``django.contrib.localflavor.pe``)
|
||||||
|
========================================
|
||||||
|
|
||||||
|
PEDNIField
|
||||||
|
----------
|
||||||
|
|
||||||
|
A form field that validates input as a DNI (Peruvian national identity)
|
||||||
|
number.
|
||||||
|
|
||||||
|
PERUCField
|
||||||
|
----------
|
||||||
|
|
||||||
|
A form field that validates input as an RUC (Registro Unico de
|
||||||
|
Contribuyentes) number. Valid RUC numbers have eleven digits.
|
||||||
|
|
||||||
|
PEDepartmentSelect
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Peruvian Departments as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Poland (``django.contrib.localflavor.pl``)
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
PLNationalIdentificationNumberField
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Polish national identification number
|
||||||
|
(PESEL_).
|
||||||
|
|
||||||
|
.. _PESEL: http://en.wikipedia.org/wiki/PESEL
|
||||||
|
|
||||||
|
PLNationalBusinessRegisterField
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Polish National Official Business
|
||||||
|
Register Number (REGON_), having either seven or nine digits. The checksum
|
||||||
|
algorithm used for REGONs is documented at
|
||||||
|
http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
|
||||||
|
|
||||||
|
.. _REGON: http://www.stat.gov.pl/bip/regon_ENG_HTML.htm
|
||||||
|
|
||||||
|
PLPostalCodeField
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A form field that validates input as a Polish postal code. The valid format
|
||||||
|
is XX-XXX, where X is a digit.
|
||||||
|
|
||||||
|
PLTaxNumberField
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A form field that validates input as a Polish Tax Number (NIP). Valid
|
||||||
|
formats are XXX-XXX-XX-XX or XX-XX-XXX-XXX. The checksum algorithm used
|
||||||
|
for NIPs is documented at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
|
||||||
|
|
||||||
|
PLAdministrativeUnitSelect
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Polish administrative units as its
|
||||||
|
choices.
|
||||||
|
|
||||||
|
PLVoivodeshipSelect
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Polish voivodeships (administrative
|
||||||
|
provinces) as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Slovakia (``django.contrib.localflavor.sk``)
|
||||||
|
============================================
|
||||||
|
|
||||||
|
SKPostalCodeField
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A form field that validates input as a Slovak postal code. Valid formats
|
||||||
|
are XXXXX or XXX XX, where X is a digit.
|
||||||
|
|
||||||
|
SKDistrictSelect
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Slovak districts as its choices.
|
||||||
|
|
||||||
|
SKRegionSelect
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Slovak regions as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
South Africa (``django.contrib.localflavor.za``)
|
||||||
|
================================================
|
||||||
|
|
||||||
|
ZAIDField
|
||||||
|
---------
|
||||||
|
|
||||||
|
A form field that validates input as a South African ID number. Validation
|
||||||
|
uses the Luhn checksum and a simplistic (i.e., not entirely accurate) check
|
||||||
|
for birth date.
|
||||||
|
|
||||||
|
ZAPostCodeField
|
||||||
|
---------------
|
||||||
|
|
||||||
|
A form field that validates input as a South African postcode. Valid
|
||||||
|
postcodes must have four digits.
|
||||||
|
|
||||||
|
|
||||||
|
Spain (``django.contrib.localflavor.es``)
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
ESIdentityCardNumberField
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Spanish NIF/NIE/CIF (Fiscal
|
||||||
|
Identification Number) code.
|
||||||
|
|
||||||
|
ESCCCField
|
||||||
|
----------
|
||||||
|
|
||||||
|
A form field that validates input as a Spanish bank account number (Codigo
|
||||||
|
Cuenta Cliente or CCC). A valid CCC number has the format
|
||||||
|
EEEE-OOOO-CC-AAAAAAAAAA, where the E, O, C and A digits denote the entity,
|
||||||
|
office, checksum and account, respectively. The first checksum digit
|
||||||
|
validates the entity and office. The second checksum digit validates the
|
||||||
|
account. It is also valid to use a space as a delimiter, or to use no
|
||||||
|
delimiter.
|
||||||
|
|
||||||
|
ESPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Spanish phone number. Valid numbers
|
||||||
|
have nine digits, the first of which is 6, 8 or 9.
|
||||||
|
|
||||||
|
ESPostalCodeField
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A form field that validates input as a Spanish postal code. Valid codes
|
||||||
|
have five digits, the first two being in the range 01 to 52, representing
|
||||||
|
the province.
|
||||||
|
|
||||||
|
ESProvinceSelect
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Spanish provinces as its choices.
|
||||||
|
|
||||||
|
ESRegionSelect
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Spanish regions as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
Switzerland (``django.contrib.localflavor.ch``)
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
CHIdentityCardNumberField
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Swiss identity card number.
|
||||||
|
A valid number must confirm to the X1234567<0 or 1234567890 format and
|
||||||
|
have the correct checksums -- see http://adi.kousz.ch/artikel/IDCHE.htm.
|
||||||
|
|
||||||
|
CHPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as a Swiss phone number. The correct
|
||||||
|
format is 0XX XXX XX XX. 0XX.XXX.XX.XX and 0XXXXXXXXX validate but are
|
||||||
|
corrected to 0XX XXX XX XX.
|
||||||
|
|
||||||
|
CHZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as a Swiss zip code. Valid codes
|
||||||
|
consist of four digits.
|
||||||
|
|
||||||
|
CHStateSelect
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A ``Select`` widget that uses a list of Swiss states as its choices.
|
||||||
|
|
||||||
|
|
||||||
|
United Kingdom (``django.contrib.localflavor.uk``)
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
UKPostcodeField
|
||||||
|
---------------
|
||||||
|
|
||||||
|
A form field that validates input as a UK postcode. The regular
|
||||||
|
expression used is sourced from the schema for British Standard BS7666
|
||||||
|
address types at http://www.govtalk.gov.uk/gdsc/schemas/bs7666-v2-0.xsd.
|
||||||
|
|
||||||
|
|
||||||
|
United States of America (``django.contrib.localflavor.us``)
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
USPhoneNumberField
|
||||||
|
------------------
|
||||||
|
|
||||||
|
A form field that validates input as a U.S. phone number.
|
||||||
|
|
||||||
|
USSocialSecurityNumberField
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
A form field that validates input as a U.S. Social Security Number (SSN).
|
||||||
|
A valid SSN must obey the following rules:
|
||||||
|
|
||||||
|
* Format of XXX-XX-XXXX
|
||||||
|
* No group of digits consisting entirely of zeroes
|
||||||
|
* Leading group of digits cannot be 666
|
||||||
|
* Number not in promotional block 987-65-4320 through 987-65-4329
|
||||||
|
* Number not one known to be invalid due to widespread promotional
|
||||||
|
use or distribution (e.g., the Woolworth's number or the 1962
|
||||||
|
promotional number)
|
||||||
|
|
||||||
|
USStateField
|
||||||
|
------------
|
||||||
|
|
||||||
|
A form field that validates input as a U.S. state name or abbreviation. It
|
||||||
|
normalizes the input to the standard two-letter postal service abbreviation
|
||||||
|
for the given state.
|
||||||
|
|
||||||
|
USZipCodeField
|
||||||
|
--------------
|
||||||
|
|
||||||
|
A form field that validates input as a U.S. zip code. Valid formats are
|
||||||
|
XXXXX or XXXXX-XXXX.
|
||||||
|
|
||||||
|
USStateSelect
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A form Select widget that uses a list of U.S. states/territories as its
|
||||||
|
choices.
|
@ -58,11 +58,20 @@ Adds a few conveniences for perfectionists:
|
|||||||
which should be a list of strings.
|
which should be a list of strings.
|
||||||
|
|
||||||
* Performs URL rewriting based on the ``APPEND_SLASH`` and ``PREPEND_WWW``
|
* Performs URL rewriting based on the ``APPEND_SLASH`` and ``PREPEND_WWW``
|
||||||
settings. If ``APPEND_SLASH`` is ``True``, URLs that lack a trailing
|
settings.
|
||||||
slash will be redirected to the same URL with a trailing slash, unless the
|
|
||||||
last component in the path contains a period. So ``foo.com/bar`` is
|
If ``APPEND_SLASH`` is ``True`` and the initial URL doesn't end with a slash,
|
||||||
redirected to ``foo.com/bar/``, but ``foo.com/bar/file.txt`` is passed
|
and it is not found in urlpatterns, a new URL is formed by appending a slash
|
||||||
through unchanged.
|
at the end. If this new URL is found in urlpatterns, then an HTTP-redirect is
|
||||||
|
returned to this new URL; otherwise the initial URL is processed as usual.
|
||||||
|
|
||||||
|
So ``foo.com/bar`` will be redirected to ``foo.com/bar/`` if you do not
|
||||||
|
have a valid urlpattern for ``foo.com/bar``, and do have a valid urlpattern
|
||||||
|
for ``foo.com/bar/``.
|
||||||
|
|
||||||
|
**New in Django development version:** The behaviour of ``APPEND_SLASH`` has
|
||||||
|
changed slightly in the development version (it didn't used to check to see
|
||||||
|
if the pattern was matched in the URL patterns).
|
||||||
|
|
||||||
If ``PREPEND_WWW`` is ``True``, URLs that lack a leading "www." will be
|
If ``PREPEND_WWW`` is ``True``, URLs that lack a leading "www." will be
|
||||||
redirected to the same URL with a leading "www."
|
redirected to the same URL with a leading "www."
|
||||||
|
@ -618,8 +618,9 @@ statement for this field.
|
|||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
The name of the database tablespace to use for this field's index, if
|
The name of the database tablespace to use for this field's index, if
|
||||||
indeed this field is indexed. The default is the ``db_tablespace`` of
|
indeed this field is indexed. The default is the project's
|
||||||
the model, if any. If the backend doesn't support tablespaces, this
|
``DEFAULT_INDEX_TABLESPACE`` setting, if set, or the ``db_tablespace``
|
||||||
|
of the model, if any. If the backend doesn't support tablespaces, this
|
||||||
option is ignored.
|
option is ignored.
|
||||||
|
|
||||||
``default``
|
``default``
|
||||||
|
310
docs/modelforms.txt
Normal file
310
docs/modelforms.txt
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
==========================
|
||||||
|
Using newforms with models
|
||||||
|
==========================
|
||||||
|
|
||||||
|
``ModelForm``
|
||||||
|
=============
|
||||||
|
|
||||||
|
If you're building a database-driven app, chances are you'll have forms that
|
||||||
|
map closely to Django models. For instance, you might have a ``BlogComment``
|
||||||
|
model, and you want to create a form that lets people submit comments. In this
|
||||||
|
case, it would be redundant to define the field types in your form, because
|
||||||
|
you've already defined the fields in your model.
|
||||||
|
|
||||||
|
For this reason, Django provides a helper class that let you create a ``Form``
|
||||||
|
class from a Django model.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
>>> from django.newforms import ModelForm
|
||||||
|
|
||||||
|
# Create the form class.
|
||||||
|
>>> class ArticleForm(ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
|
||||||
|
# Creating a form to add an article.
|
||||||
|
>>> article = Article()
|
||||||
|
>>> form = ArticleForm(article)
|
||||||
|
|
||||||
|
# Creating a form to change an existing article.
|
||||||
|
>>> article = Article.objects.get(pk=1)
|
||||||
|
>>> form = ArticleForm(article)
|
||||||
|
|
||||||
|
Field types
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The generated ``Form`` class will have a form field for every model field. Each
|
||||||
|
model field has a corresponding default form field. For example, a
|
||||||
|
``CharField`` on a model is represented as a ``CharField`` on a form. A
|
||||||
|
model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
|
||||||
|
the full list of conversions:
|
||||||
|
|
||||||
|
=============================== ========================================
|
||||||
|
Model field Form field
|
||||||
|
=============================== ========================================
|
||||||
|
``AutoField`` Not represented in the form
|
||||||
|
``BooleanField`` ``BooleanField``
|
||||||
|
``CharField`` ``CharField`` with ``max_length`` set to
|
||||||
|
the model field's ``max_length``
|
||||||
|
``CommaSeparatedIntegerField`` ``CharField``
|
||||||
|
``DateField`` ``DateField``
|
||||||
|
``DateTimeField`` ``DateTimeField``
|
||||||
|
``DecimalField`` ``DecimalField``
|
||||||
|
``EmailField`` ``EmailField``
|
||||||
|
``FileField`` ``FileField``
|
||||||
|
``FilePathField`` ``CharField``
|
||||||
|
``FloatField`` ``FloatField``
|
||||||
|
``ForeignKey`` ``ModelChoiceField`` (see below)
|
||||||
|
``ImageField`` ``ImageField``
|
||||||
|
``IntegerField`` ``IntegerField``
|
||||||
|
``IPAddressField`` ``IPAddressField``
|
||||||
|
``ManyToManyField`` ``ModelMultipleChoiceField`` (see
|
||||||
|
below)
|
||||||
|
``NullBooleanField`` ``CharField``
|
||||||
|
``PhoneNumberField`` ``USPhoneNumberField``
|
||||||
|
(from ``django.contrib.localflavor.us``)
|
||||||
|
``PositiveIntegerField`` ``IntegerField``
|
||||||
|
``PositiveSmallIntegerField`` ``IntegerField``
|
||||||
|
``SlugField`` ``CharField``
|
||||||
|
``SmallIntegerField`` ``IntegerField``
|
||||||
|
``TextField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
``TimeField`` ``TimeField``
|
||||||
|
``URLField`` ``URLField`` with ``verify_exists`` set
|
||||||
|
to the model field's ``verify_exists``
|
||||||
|
``USStateField`` ``CharField`` with
|
||||||
|
``widget=USStateSelect``
|
||||||
|
(``USStateSelect`` is from
|
||||||
|
``django.contrib.localflavor.us``)
|
||||||
|
``XMLField`` ``CharField`` with ``widget=Textarea``
|
||||||
|
=============================== ========================================
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The ``FloatField`` form field and ``DecimalField`` model and form fields
|
||||||
|
are new in the development version.
|
||||||
|
|
||||||
|
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
||||||
|
types are special cases:
|
||||||
|
|
||||||
|
* ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``,
|
||||||
|
which is a ``ChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
* ``ManyToManyField`` is represented by
|
||||||
|
``django.newforms.ModelMultipleChoiceField``, which is a
|
||||||
|
``MultipleChoiceField`` whose choices are a model ``QuerySet``.
|
||||||
|
|
||||||
|
In addition, each generated form field has attributes set as follows:
|
||||||
|
|
||||||
|
* If the model field has ``blank=True``, then ``required`` is set to
|
||||||
|
``False`` on the form field. Otherwise, ``required=True``.
|
||||||
|
|
||||||
|
* The form field's ``label`` is set to the ``verbose_name`` of the model
|
||||||
|
field, with the first character capitalized.
|
||||||
|
|
||||||
|
* The form field's ``help_text`` is set to the ``help_text`` of the model
|
||||||
|
field.
|
||||||
|
|
||||||
|
* If the model field has ``choices`` set, then the form field's ``widget``
|
||||||
|
will be set to ``Select``, with choices coming from the model field's
|
||||||
|
``choices``. The choices will normally include the blank choice which is
|
||||||
|
selected by default. If the field is required, this forces the user to
|
||||||
|
make a selection. The blank choice will not be included if the model
|
||||||
|
field has ``blank=False`` and an explicit ``default`` value (the
|
||||||
|
``default`` value will be initially selected instead).
|
||||||
|
|
||||||
|
Finally, note that you can override the form field used for a given model
|
||||||
|
field. See "Overriding the default field types" below.
|
||||||
|
|
||||||
|
A full example
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Consider this set of models::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
TITLE_CHOICES = (
|
||||||
|
('MR', 'Mr.'),
|
||||||
|
('MRS', 'Mrs.'),
|
||||||
|
('MS', 'Ms.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Author(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
|
||||||
|
birth_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Book(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
authors = models.ManyToManyField(Author)
|
||||||
|
|
||||||
|
class AuthorForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Author
|
||||||
|
|
||||||
|
class BookForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Book
|
||||||
|
|
||||||
|
With these models, the ``ModelForm`` subclasses above would be roughly
|
||||||
|
equivalent to this (the only difference being the ``save()`` method, which
|
||||||
|
we'll discuss in a moment.)::
|
||||||
|
|
||||||
|
class AuthorForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
title = forms.CharField(max_length=3,
|
||||||
|
widget=forms.Select(choices=TITLE_CHOICES))
|
||||||
|
birth_date = forms.DateField(required=False)
|
||||||
|
|
||||||
|
class BookForm(forms.Form):
|
||||||
|
name = forms.CharField(max_length=100)
|
||||||
|
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
|
||||||
|
|
||||||
|
The ``save()`` method
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Every form produced by ``ModelForm`` also has a ``save()`` method. This
|
||||||
|
method creates and saves a database object from the data bound to the form.
|
||||||
|
A subclass of ``ModelForm`` also requires a model instance as the first
|
||||||
|
arument to its constructor. For example::
|
||||||
|
|
||||||
|
# Create a form instance from POST data.
|
||||||
|
>>> a = Article()
|
||||||
|
>>> f = ArticleForm(a, request.POST)
|
||||||
|
|
||||||
|
# Save a new Article object from the form's data.
|
||||||
|
>>> new_article = f.save()
|
||||||
|
|
||||||
|
Note that ``save()`` will raise a ``ValueError`` if the data in the form
|
||||||
|
doesn't validate -- i.e., ``if form.errors``.
|
||||||
|
|
||||||
|
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
||||||
|
accepts either ``True`` or ``False``. If you call ``save()`` with
|
||||||
|
``commit=False``, then it will return an object that hasn't yet been saved to
|
||||||
|
the database. In this case, it's up to you to call ``save()`` on the resulting
|
||||||
|
model instance. This is useful if you want to do custom processing on the
|
||||||
|
object before saving it. ``commit`` is ``True`` by default.
|
||||||
|
|
||||||
|
Another side effect of using ``commit=False`` is seen when your model has
|
||||||
|
a many-to-many relation with another model. If your model has a many-to-many
|
||||||
|
relation and you specify ``commit=False`` when you save a form, Django cannot
|
||||||
|
immediately save the form data for the many-to-many relation. This is because
|
||||||
|
it isn't possible to save many-to-many data for an instance until the instance
|
||||||
|
exists in the database.
|
||||||
|
|
||||||
|
To work around this problem, every time you save a form using ``commit=False``,
|
||||||
|
Django adds a ``save_m2m()`` method to your ``ModelForm`` subclass. After
|
||||||
|
you've manually saved the instance produced by the form, you can invoke
|
||||||
|
``save_m2m()`` to save the many-to-many form data. For example::
|
||||||
|
|
||||||
|
# Create a form instance with POST data.
|
||||||
|
>>> a = Author()
|
||||||
|
>>> f = AuthorForm(a, request.POST)
|
||||||
|
|
||||||
|
# Create, but don't save the new author instance.
|
||||||
|
>>> new_author = f.save(commit=False)
|
||||||
|
|
||||||
|
# Modify the author in some way.
|
||||||
|
>>> new_author.some_field = 'some_value'
|
||||||
|
|
||||||
|
# Save the new instance.
|
||||||
|
>>> new_author.save()
|
||||||
|
|
||||||
|
# Now, save the many-to-many data for the form.
|
||||||
|
>>> f.save_m2m()
|
||||||
|
|
||||||
|
Calling ``save_m2m()`` is only required if you use ``save(commit=False)``.
|
||||||
|
When you use a simple ``save()`` on a form, all data -- including
|
||||||
|
many-to-many data -- is saved without the need for any additional method calls.
|
||||||
|
For example::
|
||||||
|
|
||||||
|
# Create a form instance with POST data.
|
||||||
|
>>> a = Author()
|
||||||
|
>>> f = AuthorForm(a, request.POST)
|
||||||
|
|
||||||
|
# Create and save the new author instance. There's no need to do anything else.
|
||||||
|
>>> new_author = f.save()
|
||||||
|
|
||||||
|
Using a subset of fields on the form
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
In some cases, you may not want all the model fields to appear on the generated
|
||||||
|
form. There are three ways of telling ``ModelForm`` to use only a subset of the
|
||||||
|
model fields:
|
||||||
|
|
||||||
|
1. Set ``editable=False`` on the model field. As a result, *any* form
|
||||||
|
created from the model via ``ModelForm`` will not include that
|
||||||
|
field.
|
||||||
|
|
||||||
|
2. Use the ``fields`` attribute of the ``ModelForm``'s inner ``Meta`` class.
|
||||||
|
This attribute, if given, should be a list of field names to include in
|
||||||
|
the form.
|
||||||
|
|
||||||
|
3. Use the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` class.
|
||||||
|
This attribute, if given, should be a list of field names to exclude
|
||||||
|
the form.
|
||||||
|
|
||||||
|
For example, if you want a form for the ``Author`` model (defined above)
|
||||||
|
that includes only the ``name`` and ``title`` fields, you would specify
|
||||||
|
``fields`` or ``exclude`` like this::
|
||||||
|
|
||||||
|
class PartialAuthorForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Author
|
||||||
|
fields = ('name', 'title')
|
||||||
|
|
||||||
|
class PartialAuthorForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Author
|
||||||
|
exclude = ('birth_date',)
|
||||||
|
|
||||||
|
Since the Author model has only 3 fields, 'name', 'title', and
|
||||||
|
'birth_date', the forms above will contain exactly the same fields.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you specify ``fields`` or ``exclude`` when creating a form with
|
||||||
|
``ModelForm``, then the fields that are not in the resulting form will not
|
||||||
|
be set by the form's ``save()`` method. Django will prevent any attempt to
|
||||||
|
save an incomplete model, so if the model does not allow the missing fields
|
||||||
|
to be empty, and does not provide a default value for the missing fields,
|
||||||
|
any attempt to ``save()`` a ``ModelForm`` with missing fields will fail.
|
||||||
|
To avoid this failure, you must instantiate your model with initial values
|
||||||
|
for the missing, but required fields, or use ``save(commit=False)`` and
|
||||||
|
manually set anyextra required fields::
|
||||||
|
|
||||||
|
instance = Instance(required_field='value')
|
||||||
|
form = InstanceForm(instance, request.POST)
|
||||||
|
new_instance = form.save()
|
||||||
|
|
||||||
|
instance = form.save(commit=False)
|
||||||
|
instance.required_field = 'new value'
|
||||||
|
new_instance = instance.save()
|
||||||
|
|
||||||
|
See the `section on saving forms`_ for more details on using
|
||||||
|
``save(commit=False)``.
|
||||||
|
|
||||||
|
.. _section on saving forms: `The save() method`_
|
||||||
|
|
||||||
|
Overriding the default field types
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
The default field types, as described in the "Field types" table above, are
|
||||||
|
sensible defaults; if you have a ``DateField`` in your model, chances are you'd
|
||||||
|
want that to be represented as a ``DateField`` in your form. But
|
||||||
|
``ModelForm`` gives you the flexibility of changing the form field type
|
||||||
|
for a given model field. You do this by declaratively specifying fields like
|
||||||
|
you would in a regular ``Form``. Declared fields will override the default
|
||||||
|
ones generated by using the ``model`` attribute.
|
||||||
|
|
||||||
|
For example, if you wanted to use ``MyDateFormField`` for the ``pub_date``
|
||||||
|
field, you could do the following::
|
||||||
|
|
||||||
|
>>> class ArticleForm(ModelForm):
|
||||||
|
... pub_date = MyDateFormField()
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
@ -759,8 +759,9 @@ For example::
|
|||||||
Highlighting required fields in templates
|
Highlighting required fields in templates
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
You may wish to show a visitor which fields are required. Here is the above
|
It's common to show a user which fields are required. Here's an example of how
|
||||||
example modified to insert an asterix after the label of each required field::
|
to do that, using the above example modified to insert an asterisk after the
|
||||||
|
label of each required field::
|
||||||
|
|
||||||
<form method="post" action="">
|
<form method="post" action="">
|
||||||
<dl>
|
<dl>
|
||||||
@ -775,10 +776,11 @@ example modified to insert an asterix after the label of each required field::
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
The ``{% if field.field.required %}*{% endif %}`` fragment is the relevant
|
The ``{% if field.field.required %}*{% endif %}`` fragment is the relevant
|
||||||
addition here. It adds the asterix only if the field is required. Note that we
|
addition here. It adds the asterisk only if the field is required.
|
||||||
check ``field.field.required`` and not ``field.required``. In the template,
|
|
||||||
``field`` is a ``newforms.forms.BoundField`` instance, which holds the actual
|
Note that we check ``field.field.required`` and not ``field.required``. In the
|
||||||
``Field`` instance in its ``field`` attribute.
|
template, ``field`` is a ``newforms.forms.BoundField`` instance, which holds
|
||||||
|
the actual ``Field`` instance in its ``field`` attribute.
|
||||||
|
|
||||||
Binding uploaded files to a form
|
Binding uploaded files to a form
|
||||||
--------------------------------
|
--------------------------------
|
||||||
@ -1108,9 +1110,9 @@ fields. We've specified ``auto_id=False`` to simplify the output::
|
|||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
The ``error_messages`` argument lets you override the default messages which the
|
The ``error_messages`` argument lets you override the default messages that the
|
||||||
field will raise. Pass in a dictionary with keys matching the error messages you
|
field will raise. Pass in a dictionary with keys matching the error messages you
|
||||||
want to override. For example::
|
want to override. For example, here is the default error message::
|
||||||
|
|
||||||
>>> generic = forms.CharField()
|
>>> generic = forms.CharField()
|
||||||
>>> generic.clean('')
|
>>> generic.clean('')
|
||||||
@ -1118,14 +1120,16 @@ want to override. For example::
|
|||||||
...
|
...
|
||||||
ValidationError: [u'This field is required.']
|
ValidationError: [u'This field is required.']
|
||||||
|
|
||||||
|
And here is a custom error message::
|
||||||
|
|
||||||
>>> name = forms.CharField(error_messages={'required': 'Please enter your name'})
|
>>> name = forms.CharField(error_messages={'required': 'Please enter your name'})
|
||||||
>>> name.clean('')
|
>>> name.clean('')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValidationError: [u'Please enter your name']
|
ValidationError: [u'Please enter your name']
|
||||||
|
|
||||||
In the `built-in Field classes`_ section below, each Field defines the error
|
In the `built-in Field classes`_ section below, each ``Field`` defines the
|
||||||
message keys it uses.
|
error message keys it uses.
|
||||||
|
|
||||||
Dynamic initial values
|
Dynamic initial values
|
||||||
----------------------
|
----------------------
|
||||||
@ -1769,421 +1773,14 @@ You can then use this field whenever you have a form that requires a comment::
|
|||||||
Generating forms for models
|
Generating forms for models
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
If you're building a database-driven app, chances are you'll have forms that
|
The prefered way of generating forms that work with models is explained in the
|
||||||
map closely to Django models. For instance, you might have a ``BlogComment``
|
`ModelForms documentation`_.
|
||||||
model, and you want to create a form that lets people submit comments. In this
|
|
||||||
case, it would be redundant to define the field types in your form, because
|
|
||||||
you've already defined the fields in your model.
|
|
||||||
|
|
||||||
For this reason, Django provides a few helper functions that let you create a
|
Looking for the ``form_for_model`` and ``form_for_instance`` documentation?
|
||||||
``Form`` class from a Django model.
|
They've been deprecated, but you can still `view the documentation`_.
|
||||||
|
|
||||||
``form_for_model()``
|
.. _ModelForms documentation: ../modelforms/
|
||||||
--------------------
|
.. _view the documentation: ../form_for_model/
|
||||||
|
|
||||||
The method ``django.newforms.form_for_model()`` creates a form based on the
|
|
||||||
definition of a specific model. Pass it the model class, and it will return a
|
|
||||||
``Form`` class that contains a form field for each model field.
|
|
||||||
|
|
||||||
For example::
|
|
||||||
|
|
||||||
>>> from django.newforms import form_for_model
|
|
||||||
|
|
||||||
# Create the form class.
|
|
||||||
>>> ArticleForm = form_for_model(Article)
|
|
||||||
|
|
||||||
# Create an empty form instance.
|
|
||||||
>>> f = ArticleForm()
|
|
||||||
|
|
||||||
It bears repeating that ``form_for_model()`` takes the model *class*, not a
|
|
||||||
model instance, and it returns a ``Form`` *class*, not a ``Form`` instance.
|
|
||||||
|
|
||||||
Field types
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
The generated ``Form`` class will have a form field for every model field. Each
|
|
||||||
model field has a corresponding default form field. For example, a
|
|
||||||
``CharField`` on a model is represented as a ``CharField`` on a form. A
|
|
||||||
model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is
|
|
||||||
the full list of conversions:
|
|
||||||
|
|
||||||
=============================== ========================================
|
|
||||||
Model field Form field
|
|
||||||
=============================== ========================================
|
|
||||||
``AutoField`` Not represented in the form
|
|
||||||
``BooleanField`` ``BooleanField``
|
|
||||||
``CharField`` ``CharField`` with ``max_length`` set to
|
|
||||||
the model field's ``max_length``
|
|
||||||
``CommaSeparatedIntegerField`` ``CharField``
|
|
||||||
``DateField`` ``DateField``
|
|
||||||
``DateTimeField`` ``DateTimeField``
|
|
||||||
``DecimalField`` ``DecimalField``
|
|
||||||
``EmailField`` ``EmailField``
|
|
||||||
``FileField`` ``FileField``
|
|
||||||
``FilePathField`` ``CharField``
|
|
||||||
``FloatField`` ``FloatField``
|
|
||||||
``ForeignKey`` ``ModelChoiceField`` (see below)
|
|
||||||
``ImageField`` ``ImageField``
|
|
||||||
``IntegerField`` ``IntegerField``
|
|
||||||
``IPAddressField`` ``IPAddressField``
|
|
||||||
``ManyToManyField`` ``ModelMultipleChoiceField`` (see
|
|
||||||
below)
|
|
||||||
``NullBooleanField`` ``CharField``
|
|
||||||
``PhoneNumberField`` ``USPhoneNumberField``
|
|
||||||
(from ``django.contrib.localflavor.us``)
|
|
||||||
``PositiveIntegerField`` ``IntegerField``
|
|
||||||
``PositiveSmallIntegerField`` ``IntegerField``
|
|
||||||
``SlugField`` ``CharField``
|
|
||||||
``SmallIntegerField`` ``IntegerField``
|
|
||||||
``TextField`` ``CharField`` with ``widget=Textarea``
|
|
||||||
``TimeField`` ``TimeField``
|
|
||||||
``URLField`` ``URLField`` with ``verify_exists`` set
|
|
||||||
to the model field's ``verify_exists``
|
|
||||||
``USStateField`` ``CharField`` with
|
|
||||||
``widget=USStateSelect``
|
|
||||||
(``USStateSelect`` is from
|
|
||||||
``django.contrib.localflavor.us``)
|
|
||||||
``XMLField`` ``CharField`` with ``widget=Textarea``
|
|
||||||
=============================== ========================================
|
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
The ``FloatField`` form field and ``DecimalField`` model and form fields
|
|
||||||
are new in the development version.
|
|
||||||
|
|
||||||
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
|
||||||
types are special cases:
|
|
||||||
|
|
||||||
* ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``,
|
|
||||||
which is a ``ChoiceField`` whose choices are a model ``QuerySet``.
|
|
||||||
|
|
||||||
* ``ManyToManyField`` is represented by
|
|
||||||
``django.newforms.ModelMultipleChoiceField``, which is a
|
|
||||||
``MultipleChoiceField`` whose choices are a model ``QuerySet``.
|
|
||||||
|
|
||||||
In addition, each generated form field has attributes set as follows:
|
|
||||||
|
|
||||||
* If the model field has ``blank=True``, then ``required`` is set to
|
|
||||||
``False`` on the form field. Otherwise, ``required=True``.
|
|
||||||
|
|
||||||
* The form field's ``label`` is set to the ``verbose_name`` of the model
|
|
||||||
field, with the first character capitalized.
|
|
||||||
|
|
||||||
* The form field's ``help_text`` is set to the ``help_text`` of the model
|
|
||||||
field.
|
|
||||||
|
|
||||||
* If the model field has ``choices`` set, then the form field's ``widget``
|
|
||||||
will be set to ``Select``, with choices coming from the model field's
|
|
||||||
``choices``. The choices will normally include the blank choice which is
|
|
||||||
selected by default. If the field is required, this forces the user to
|
|
||||||
make a selection. The blank choice will not be included if the model
|
|
||||||
field has ``blank=False`` and an explicit ``default`` value (the
|
|
||||||
``default`` value will be initially selected instead).
|
|
||||||
|
|
||||||
Finally, note that you can override the form field used for a given model
|
|
||||||
field. See "Overriding the default field types" below.
|
|
||||||
|
|
||||||
A full example
|
|
||||||
~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Consider this set of models::
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
TITLE_CHOICES = (
|
|
||||||
('MR', 'Mr.'),
|
|
||||||
('MRS', 'Mrs.'),
|
|
||||||
('MS', 'Ms.'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Author(models.Model):
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
|
|
||||||
birth_date = models.DateField(blank=True, null=True)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Book(models.Model):
|
|
||||||
name = models.CharField(max_length=100)
|
|
||||||
authors = models.ManyToManyField(Author)
|
|
||||||
|
|
||||||
With these models, a call to ``form_for_model(Author)`` would return a ``Form``
|
|
||||||
class equivalent to this::
|
|
||||||
|
|
||||||
class AuthorForm(forms.Form):
|
|
||||||
name = forms.CharField(max_length=100)
|
|
||||||
title = forms.CharField(max_length=3,
|
|
||||||
widget=forms.Select(choices=TITLE_CHOICES))
|
|
||||||
birth_date = forms.DateField(required=False)
|
|
||||||
|
|
||||||
A call to ``form_for_model(Book)`` would return a ``Form`` class equivalent to
|
|
||||||
this::
|
|
||||||
|
|
||||||
class BookForm(forms.Form):
|
|
||||||
name = forms.CharField(max_length=100)
|
|
||||||
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
|
|
||||||
|
|
||||||
The ``save()`` method
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Every form produced by ``form_for_model()`` also has a ``save()`` method. This
|
|
||||||
method creates and saves a database object from the data bound to the form. For
|
|
||||||
example::
|
|
||||||
|
|
||||||
# Create a form instance from POST data.
|
|
||||||
>>> f = ArticleForm(request.POST)
|
|
||||||
|
|
||||||
# Save a new Article object from the form's data.
|
|
||||||
>>> new_article = f.save()
|
|
||||||
|
|
||||||
Note that ``save()`` will raise a ``ValueError`` if the data in the form
|
|
||||||
doesn't validate -- i.e., ``if form.errors``.
|
|
||||||
|
|
||||||
This ``save()`` method accepts an optional ``commit`` keyword argument, which
|
|
||||||
accepts either ``True`` or ``False``. If you call ``save()`` with
|
|
||||||
``commit=False``, then it will return an object that hasn't yet been saved to
|
|
||||||
the database. In this case, it's up to you to call ``save()`` on the resulting
|
|
||||||
model instance. This is useful if you want to do custom processing on the
|
|
||||||
object before saving it. ``commit`` is ``True`` by default.
|
|
||||||
|
|
||||||
Another side effect of using ``commit=False`` is seen when your model has
|
|
||||||
a many-to-many relation with another model. If your model has a many-to-many
|
|
||||||
relation and you specify ``commit=False`` when you save a form, Django cannot
|
|
||||||
immediately save the form data for the many-to-many relation. This is because
|
|
||||||
it isn't possible to save many-to-many data for an instance until the instance
|
|
||||||
exists in the database.
|
|
||||||
|
|
||||||
To work around this problem, every time you save a form using ``commit=False``,
|
|
||||||
Django adds a ``save_m2m()`` method to the form created by ``form_for_model``.
|
|
||||||
After you've manually saved the instance produced by the form, you can invoke
|
|
||||||
``save_m2m()`` to save the many-to-many form data. For example::
|
|
||||||
|
|
||||||
# Create a form instance with POST data.
|
|
||||||
>>> f = AuthorForm(request.POST)
|
|
||||||
|
|
||||||
# Create, but don't save the new author instance.
|
|
||||||
>>> new_author = f.save(commit=False)
|
|
||||||
|
|
||||||
# Modify the author in some way.
|
|
||||||
>>> new_author.some_field = 'some_value'
|
|
||||||
|
|
||||||
# Save the new instance.
|
|
||||||
>>> new_author.save()
|
|
||||||
|
|
||||||
# Now, save the many-to-many data for the form.
|
|
||||||
>>> f.save_m2m()
|
|
||||||
|
|
||||||
Calling ``save_m2m()`` is only required if you use ``save(commit=False)``.
|
|
||||||
When you use a simple ``save()`` on a form, all data -- including
|
|
||||||
many-to-many data -- is saved without the need for any additional method calls.
|
|
||||||
For example::
|
|
||||||
|
|
||||||
# Create a form instance with POST data.
|
|
||||||
>>> f = AuthorForm(request.POST)
|
|
||||||
|
|
||||||
# Create and save the new author instance. There's no need to do anything else.
|
|
||||||
>>> new_author = f.save()
|
|
||||||
|
|
||||||
Using an alternate base class
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
If you want to add custom methods to the form generated by
|
|
||||||
``form_for_model()``, write a class that extends ``django.newforms.BaseForm``
|
|
||||||
and contains your custom methods. Then, use the ``form`` argument to
|
|
||||||
``form_for_model()`` to tell it to use your custom form as its base class.
|
|
||||||
For example::
|
|
||||||
|
|
||||||
# Create the new base class.
|
|
||||||
>>> class MyBase(BaseForm):
|
|
||||||
... def my_method(self):
|
|
||||||
... # Do whatever the method does
|
|
||||||
|
|
||||||
# Create the form class with a different base class.
|
|
||||||
>>> ArticleForm = form_for_model(Article, form=MyBase)
|
|
||||||
|
|
||||||
# Instantiate the form.
|
|
||||||
>>> f = ArticleForm()
|
|
||||||
|
|
||||||
# Use the base class method.
|
|
||||||
>>> f.my_method()
|
|
||||||
|
|
||||||
Using a subset of fields on the form
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
**New in Django development version**
|
|
||||||
|
|
||||||
In some cases, you may not want all the model fields to appear on the generated
|
|
||||||
form. There are two ways of telling ``form_for_model()`` to use only a subset
|
|
||||||
of the model fields:
|
|
||||||
|
|
||||||
1. Set ``editable=False`` on the model field. As a result, *any* form
|
|
||||||
created from the model via ``form_for_model()`` will not include that
|
|
||||||
field.
|
|
||||||
|
|
||||||
2. Use the ``fields`` argument to ``form_for_model()``. This argument, if
|
|
||||||
given, should be a list of field names to include in the form.
|
|
||||||
|
|
||||||
For example, if you want a form for the ``Author`` model (defined above)
|
|
||||||
that includes only the ``name`` and ``title`` fields, you would specify
|
|
||||||
``fields`` like this::
|
|
||||||
|
|
||||||
PartialArticleForm = form_for_model(Author, fields=('name', 'title'))
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
If you specify ``fields`` when creating a form with ``form_for_model()``,
|
|
||||||
then the fields that are *not* specified will not be set by the form's
|
|
||||||
``save()`` method. Django will prevent any attempt to save an incomplete
|
|
||||||
model, so if the model does not allow the missing fields to be empty, and
|
|
||||||
does not provide a default value for the missing fields, any attempt to
|
|
||||||
``save()`` a ``form_for_model`` with missing fields will fail. To avoid
|
|
||||||
this failure, you must use ``save(commit=False)`` and manually set any
|
|
||||||
extra required fields::
|
|
||||||
|
|
||||||
instance = form.save(commit=False)
|
|
||||||
instance.required_field = 'new value'
|
|
||||||
instance.save()
|
|
||||||
|
|
||||||
See the `section on saving forms`_ for more details on using
|
|
||||||
``save(commit=False)``.
|
|
||||||
|
|
||||||
.. _section on saving forms: `The save() method`_
|
|
||||||
|
|
||||||
Overriding the default field types
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The default field types, as described in the "Field types" table above, are
|
|
||||||
sensible defaults; if you have a ``DateField`` in your model, chances are you'd
|
|
||||||
want that to be represented as a ``DateField`` in your form. But
|
|
||||||
``form_for_model()`` gives you the flexibility of changing the form field type
|
|
||||||
for a given model field. You do this by specifying a **formfield callback**.
|
|
||||||
|
|
||||||
A formfield callback is a function that, when provided with a model field,
|
|
||||||
returns a form field instance. When constructing a form, ``form_for_model()``
|
|
||||||
asks the formfield callback to provide form field types.
|
|
||||||
|
|
||||||
By default, ``form_for_model()`` calls the ``formfield()`` method on the model
|
|
||||||
field::
|
|
||||||
|
|
||||||
def default_callback(field, **kwargs):
|
|
||||||
return field.formfield(**kwargs)
|
|
||||||
|
|
||||||
The ``kwargs`` are any keyword arguments that might be passed to the form
|
|
||||||
field, such as ``required=True`` or ``label='Foo'``.
|
|
||||||
|
|
||||||
For example, if you wanted to use ``MyDateFormField`` for any ``DateField``
|
|
||||||
field on the model, you could define the callback::
|
|
||||||
|
|
||||||
>>> def my_callback(field, **kwargs):
|
|
||||||
... if isinstance(field, models.DateField):
|
|
||||||
... return MyDateFormField(**kwargs)
|
|
||||||
... else:
|
|
||||||
... return field.formfield(**kwargs)
|
|
||||||
|
|
||||||
>>> ArticleForm = form_for_model(Article, formfield_callback=my_callback)
|
|
||||||
|
|
||||||
Note that your callback needs to handle *all* possible model field types, not
|
|
||||||
just the ones that you want to behave differently to the default. That's why
|
|
||||||
this example has an ``else`` clause that implements the default behavior.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
The field that is passed into the ``formfield_callback`` function in
|
|
||||||
``form_for_model()`` and ``form_for_instance`` is the field instance from
|
|
||||||
your model's class. You **must not** alter that object at all; treat it
|
|
||||||
as read-only!
|
|
||||||
|
|
||||||
If you make any alterations to that object, it will affect any future
|
|
||||||
users of the model class, because you will have changed the field object
|
|
||||||
used to construct the class. This is almost certainly what you don't want
|
|
||||||
to have happen.
|
|
||||||
|
|
||||||
Finding the model associated with a form
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The model class that was used to construct the form is available
|
|
||||||
using the ``_model`` property of the generated form::
|
|
||||||
|
|
||||||
>>> ArticleForm = form_for_model(Article)
|
|
||||||
>>> ArticleForm._model
|
|
||||||
<class 'myapp.models.Article'>
|
|
||||||
|
|
||||||
``form_for_instance()``
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
``form_for_instance()`` is like ``form_for_model()``, but it takes a model
|
|
||||||
instance instead of a model class::
|
|
||||||
|
|
||||||
# Create an Author.
|
|
||||||
>>> a = Author(name='Joe Smith', title='MR', birth_date=None)
|
|
||||||
>>> a.save()
|
|
||||||
|
|
||||||
# Create a form for this particular Author.
|
|
||||||
>>> AuthorForm = form_for_instance(a)
|
|
||||||
|
|
||||||
# Instantiate the form.
|
|
||||||
>>> f = AuthorForm()
|
|
||||||
|
|
||||||
When a form created by ``form_for_instance()`` is created, the initial data
|
|
||||||
values for the form fields are drawn from the instance. However, this data is
|
|
||||||
not bound to the form. You will need to bind data to the form before the form
|
|
||||||
can be saved.
|
|
||||||
|
|
||||||
Unlike ``form_for_model()``, a choice field in form created by
|
|
||||||
``form_for_instance()`` will not include the blank choice if the respective
|
|
||||||
model field has ``blank=False``. The initial choice is drawn from the instance.
|
|
||||||
|
|
||||||
When you call ``save()`` on a form created by ``form_for_instance()``,
|
|
||||||
the database instance will be updated. As in ``form_for_model()``, ``save()``
|
|
||||||
will raise ``ValueError`` if the data doesn't validate.
|
|
||||||
|
|
||||||
``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback``
|
|
||||||
arguments that behave the same way as they do for ``form_for_model()``.
|
|
||||||
|
|
||||||
Let's modify the earlier `contact form`_ view example a little bit. Suppose we
|
|
||||||
have a ``Message`` model that holds each contact submission. Something like::
|
|
||||||
|
|
||||||
class Message(models.Model):
|
|
||||||
subject = models.CharField(max_length=100)
|
|
||||||
message = models.TextField()
|
|
||||||
sender = models.EmailField()
|
|
||||||
cc_myself = models.BooleanField(required=False)
|
|
||||||
|
|
||||||
You could use this model to create a form (using ``form_for_model()``). You
|
|
||||||
could also use existing ``Message`` instances to create a form for editing
|
|
||||||
messages. The earlier_ view can be changed slightly to accept the ``id`` value
|
|
||||||
of an existing ``Message`` and present it for editing::
|
|
||||||
|
|
||||||
def contact_edit(request, msg_id):
|
|
||||||
# Create the form from the message id.
|
|
||||||
message = get_object_or_404(Message, id=msg_id)
|
|
||||||
ContactForm = form_for_instance(message)
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = ContactForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
form.save()
|
|
||||||
return HttpResponseRedirect('/url/on_success/')
|
|
||||||
else:
|
|
||||||
form = ContactForm()
|
|
||||||
return render_to_response('contact.html', {'form': form})
|
|
||||||
|
|
||||||
Aside from how we create the ``ContactForm`` class here, the main point to
|
|
||||||
note is that the form display in the ``GET`` branch of the function
|
|
||||||
will use the values from the ``message`` instance as initial values for the
|
|
||||||
form field.
|
|
||||||
|
|
||||||
.. _contact form: `Simple view example`_
|
|
||||||
.. _earlier: `Simple view example`_
|
|
||||||
|
|
||||||
When should you use ``form_for_model()`` and ``form_for_instance()``?
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The ``form_for_model()`` and ``form_for_instance()`` functions are meant to be
|
|
||||||
shortcuts for the common case. If you want to create a form whose fields map to
|
|
||||||
more than one model, or a form that contains fields that *aren't* on a model,
|
|
||||||
you shouldn't use these shortcuts. Creating a ``Form`` class the "long" way
|
|
||||||
isn't that difficult, after all.
|
|
||||||
|
|
||||||
Media
|
Media
|
||||||
=====
|
=====
|
||||||
|
@ -555,10 +555,13 @@ Three things to note about 404 views:
|
|||||||
* The 404 view is also called if Django doesn't find a match after checking
|
* The 404 view is also called if Django doesn't find a match after checking
|
||||||
every regular expression in the URLconf.
|
every regular expression in the URLconf.
|
||||||
|
|
||||||
* If you don't define your own 404 view -- and simply use the default,
|
* If you don't define your own 404 view -- and simply use the
|
||||||
which is recommended -- you still have one obligation: To create a
|
default, which is recommended -- you still have one obligation:
|
||||||
``404.html`` template in the root of your template directory. The default
|
you must create a ``404.html`` template in the root of your
|
||||||
404 view will use that template for all 404 errors.
|
template directory. The default 404 view will use that template
|
||||||
|
for all 404 errors. The default 404 view will pass one variable
|
||||||
|
to the template: ``request_path``, which is the URL which
|
||||||
|
resulted in the 404.
|
||||||
|
|
||||||
* If ``DEBUG`` is set to ``True`` (in your settings module) then your 404
|
* If ``DEBUG`` is set to ``True`` (in your settings module) then your 404
|
||||||
view will never be used, and the traceback will be displayed instead.
|
view will never be used, and the traceback will be displayed instead.
|
||||||
@ -572,7 +575,8 @@ the view ``django.views.defaults.server_error``, which loads and renders the
|
|||||||
template ``500.html``.
|
template ``500.html``.
|
||||||
|
|
||||||
This means you need to define a ``500.html`` template in your root template
|
This means you need to define a ``500.html`` template in your root template
|
||||||
directory. This template will be used for all server errors.
|
directory. This template will be used for all server errors. The
|
||||||
|
default 500 view passes no variables to this template.
|
||||||
|
|
||||||
This ``server_error`` view should suffice for 99% of Web applications, but if
|
This ``server_error`` view should suffice for 99% of Web applications, but if
|
||||||
you want to override the view, you can specify ``handler500`` in your
|
you want to override the view, you can specify ``handler500`` in your
|
||||||
|
@ -135,8 +135,8 @@ For example::
|
|||||||
json_serializer = serializers.get_serializer("json")()
|
json_serializer = serializers.get_serializer("json")()
|
||||||
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
|
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
|
||||||
|
|
||||||
Django ships with a copy of simplejson_ in the source. Be aware, that if
|
The Django source code includes the simplejson_ module. Be aware that if
|
||||||
you're using that for serializing directly that not all Django output can be
|
you're serializing using that module directly, not all Django output can be
|
||||||
passed unmodified to simplejson. In particular, `lazy translation objects`_
|
passed unmodified to simplejson. In particular, `lazy translation objects`_
|
||||||
need a `special encoder`_ written for them. Something like this will work::
|
need a `special encoder`_ written for them. Something like this will work::
|
||||||
|
|
||||||
@ -151,8 +151,3 @@ need a `special encoder`_ written for them. Something like this will work::
|
|||||||
|
|
||||||
.. _lazy translation objects: ../i18n/#lazy-translation
|
.. _lazy translation objects: ../i18n/#lazy-translation
|
||||||
.. _special encoder: http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.7/docs/index.html
|
.. _special encoder: http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.7/docs/index.html
|
||||||
|
|
||||||
Writing custom serializers
|
|
||||||
``````````````````````````
|
|
||||||
|
|
||||||
XXX ...
|
|
||||||
|
@ -99,6 +99,8 @@ It implements the following standard dictionary methods:
|
|||||||
|
|
||||||
* ``items()``
|
* ``items()``
|
||||||
|
|
||||||
|
* ``setdefault()``
|
||||||
|
|
||||||
It also has these three methods:
|
It also has these three methods:
|
||||||
|
|
||||||
* ``set_test_cookie()``
|
* ``set_test_cookie()``
|
||||||
|
@ -225,6 +225,27 @@ Whether to append trailing slashes to URLs. This is only used if
|
|||||||
``CommonMiddleware`` is installed (see the `middleware docs`_). See also
|
``CommonMiddleware`` is installed (see the `middleware docs`_). See also
|
||||||
``PREPEND_WWW``.
|
``PREPEND_WWW``.
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Default: ``('django.contrib.auth.backends.ModelBackend',)``
|
||||||
|
|
||||||
|
A tuple of authentication backend classes (as strings) to use when
|
||||||
|
attempting to authenticate a user. See the `authentication backends
|
||||||
|
documentation`_ for details.
|
||||||
|
|
||||||
|
.. _authentication backends documentation: ../authentication/#other-authentication-sources
|
||||||
|
|
||||||
|
AUTH_PROFILE_MODULE
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Default: Not defined
|
||||||
|
|
||||||
|
The site-specific user profile model used by this site. See the
|
||||||
|
`documentation on user profile models`_ for details.
|
||||||
|
|
||||||
|
.. _documentation on user profile models: ../authentication/#storing-additional-information-about-users
|
||||||
|
|
||||||
CACHE_BACKEND
|
CACHE_BACKEND
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@ -393,6 +414,22 @@ Default: ``'webmaster@localhost'``
|
|||||||
Default e-mail address to use for various automated correspondence from the
|
Default e-mail address to use for various automated correspondence from the
|
||||||
site manager(s).
|
site manager(s).
|
||||||
|
|
||||||
|
DEFAULT_TABLESPACE
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Default: ``''`` (Empty string)
|
||||||
|
|
||||||
|
Default tablespace to use for models that do not specify one, if the
|
||||||
|
backend supports it.
|
||||||
|
|
||||||
|
DEFAULT_INDEX_TABLESPACE
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Default: ``''`` (Empty string)
|
||||||
|
|
||||||
|
Default tablespace to use for indexes on fields that do not specify
|
||||||
|
one, if the backend supports it.
|
||||||
|
|
||||||
DISALLOWED_USER_AGENTS
|
DISALLOWED_USER_AGENTS
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -1129,12 +1166,12 @@ If you're not setting the ``DJANGO_SETTINGS_MODULE`` environment variable, you
|
|||||||
settings.
|
settings.
|
||||||
|
|
||||||
If you don't set ``DJANGO_SETTINGS_MODULE`` and don't call ``configure()``,
|
If you don't set ``DJANGO_SETTINGS_MODULE`` and don't call ``configure()``,
|
||||||
Django will raise an ``EnvironmentError`` exception the first time a setting
|
Django will raise an ``ImportError`` exception the first time a setting
|
||||||
is accessed.
|
is accessed.
|
||||||
|
|
||||||
If you set ``DJANGO_SETTINGS_MODULE``, access settings values somehow, *then*
|
If you set ``DJANGO_SETTINGS_MODULE``, access settings values somehow, *then*
|
||||||
call ``configure()``, Django will raise an ``EnvironmentError`` saying settings
|
call ``configure()``, Django will raise a ``RuntimeError`` indicating
|
||||||
have already been configured.
|
that settings have already been configured.
|
||||||
|
|
||||||
Also, it's an error to call ``configure()`` more than once, or to call
|
Also, it's an error to call ``configure()`` more than once, or to call
|
||||||
``configure()`` after any setting has been accessed.
|
``configure()`` after any setting has been accessed.
|
||||||
|
@ -98,8 +98,8 @@ This example is equivalent to::
|
|||||||
except MyModel.DoesNotExist:
|
except MyModel.DoesNotExist:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
Note: As with ``get()``, an ``AssertionError`` will be raised if more than
|
Note: As with ``get()``, an ``MultipleObjectsReturned`` exception will be
|
||||||
one object is found.
|
raised if more than one object is found.
|
||||||
|
|
||||||
.. _get(): ../db-api/#get-kwargs
|
.. _get(): ../db-api/#get-kwargs
|
||||||
|
|
||||||
|
@ -603,8 +603,9 @@ This example illustrates all possible attributes and methods for a ``Feed`` clas
|
|||||||
# ITEM LINK -- One of these three is required. The framework looks for
|
# ITEM LINK -- One of these three is required. The framework looks for
|
||||||
# them in this order.
|
# them in this order.
|
||||||
|
|
||||||
# First, the framework tries the get_absolute_url() method on each item
|
# First, the framework tries the two methods below, in
|
||||||
# returned by items(). Failing that, it tries these two methods:
|
# order. Failing that, it falls back to the get_absolute_url()
|
||||||
|
# method on each item returned by items().
|
||||||
|
|
||||||
def item_link(self, item):
|
def item_link(self, item):
|
||||||
"""
|
"""
|
||||||
|
@ -310,58 +310,106 @@ Automatic HTML escaping
|
|||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
A very real problem when creating HTML (and other) output using templates and
|
When generating HTML from templates, there's always a risk that a variable will
|
||||||
variable substitution is the possibility of accidently inserting some variable
|
include characters that affect the resulting HTML. For example, consider this
|
||||||
value that affects the resulting HTML. For example, a template fragment such as
|
template fragment::
|
||||||
::
|
|
||||||
|
|
||||||
Hello, {{ name }}.
|
Hello, {{ name }}.
|
||||||
|
|
||||||
seems like a harmless way to display the user's name. However, if you are
|
At first, this seems like a harmless way to display a user's name, but consider
|
||||||
displaying data that the user entered directly and they had entered their name as ::
|
what would happen if the user entered his name as this::
|
||||||
|
|
||||||
<script>alert('hello')</script>
|
<script>alert('hello')</script>
|
||||||
|
|
||||||
this would always display a Javascript alert box when the page was loaded.
|
With this name value, the template would be rendered as::
|
||||||
Similarly, if you were displaying some data generated by another process and it
|
|
||||||
contained a '<' symbol, you couldn't just dump this straight into your HTML,
|
Hello, <script>alert('hello')</script>
|
||||||
because it would be treated as the start of an element. The effects of these
|
|
||||||
sorts of problems can vary from merely annoying to allowing exploits via `Cross
|
...which means the browser would pop-up a JavaScript alert box!
|
||||||
Site Scripting`_ (XSS) attacks.
|
|
||||||
|
Similarly, what if the name contained a ``'<'`` symbol, like this?
|
||||||
|
|
||||||
|
<b>username
|
||||||
|
|
||||||
|
That would result in a rendered template like this::
|
||||||
|
|
||||||
|
Hello, <b>username
|
||||||
|
|
||||||
|
...which, in turn, would result in the remainder of the Web page being bolded!
|
||||||
|
|
||||||
|
Clearly, user-submitted data shouldn't be trusted blindly and inserted directly
|
||||||
|
into your Web pages, because a malicious user could use this kind of hole to
|
||||||
|
do potentially bad things. This type of security exploit is called a
|
||||||
|
`Cross Site Scripting`_ (XSS) attack.
|
||||||
|
|
||||||
|
To avoid this problem, you have two options:
|
||||||
|
|
||||||
|
* One, you can make sure to run each untrusted variable through the
|
||||||
|
``escape`` filter (documented below), which converts potentially harmful
|
||||||
|
HTML characters to unharmful ones. This was default the default solution
|
||||||
|
in Django for its first few years, but the problem is that it puts the
|
||||||
|
onus on *you*, the developer / template author, to ensure you're escaping
|
||||||
|
everything. It's easy to forget to escape data.
|
||||||
|
|
||||||
|
* Two, you can take advantage of Django's automatic HTML escaping. The
|
||||||
|
remainder of this section describes how auto-escaping works.
|
||||||
|
|
||||||
|
By default in the Django development version, every template automatically
|
||||||
|
escapes the output of every variable tag. Specifically, these five characters
|
||||||
|
are escaped:
|
||||||
|
|
||||||
|
* ``<`` is converted to ``<``
|
||||||
|
* ``>`` is converted to ``>``
|
||||||
|
* ``'`` (single quote) is converted to ``'``
|
||||||
|
* ``"`` (double quote) is converted to ``"``
|
||||||
|
* ``&`` is converted to ``&``
|
||||||
|
|
||||||
|
Again, we stress that this behavior is on by default. If you're using Django's
|
||||||
|
template system, you're protected.
|
||||||
|
|
||||||
.. _Cross Site Scripting: http://en.wikipedia.org/wiki/Cross-site_scripting
|
.. _Cross Site Scripting: http://en.wikipedia.org/wiki/Cross-site_scripting
|
||||||
|
|
||||||
In order to provide some protection against these problems, Django
|
How to turn it off
|
||||||
provides automatic (but controllable) HTML escaping for data coming from
|
------------------
|
||||||
tempate variables. Inside this tag, any data that comes from template
|
|
||||||
variables is examined to see if it contains one of the five HTML characters
|
|
||||||
(<, >, ', " and &) that often need escaping and those characters are converted
|
|
||||||
to their respective HTML entities. It causes no harm if a character is
|
|
||||||
converted to an entity when it doesn't need to be, so all five characters are
|
|
||||||
always converted.
|
|
||||||
|
|
||||||
Since some variables will contain data that is *intended* to be rendered
|
If you don't want data to be auto-escaped, on a per-site, per-template level or
|
||||||
as HTML, template tag and filter writers can mark their output strings as
|
per-variable level, you can turn it off in several ways.
|
||||||
requiring no further escaping. For example, the ``unordered_list`` filter is
|
|
||||||
designed to return raw HTML and we want the template processor to simply
|
|
||||||
display the results as returned, without applying any escaping. That is taken
|
|
||||||
care of by the filter. The template author need do nothing special in that
|
|
||||||
case.
|
|
||||||
|
|
||||||
By default, automatic HTML escaping is always applied. However, sometimes you
|
Why would you want to turn it off? Because sometimes, template variables
|
||||||
will not want this to occur (for example, if you're using the templating
|
contain data that you *intend* to be rendered as raw HTML, in which case you
|
||||||
system to create an email). To control automatic escaping inside your template,
|
don't want their contents to be escaped. For example, you might store a blob of
|
||||||
wrap the affected content in the ``autoescape`` tag, like so::
|
HTML in your database and want to embed that directly into your template. Or,
|
||||||
|
you might be using Django's template system to produce text that is *not* HTML
|
||||||
|
-- like an e-mail message, for instance.
|
||||||
|
|
||||||
|
For individual variables
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To disable auto-escaping for an individual variable, use the ``safe`` filter::
|
||||||
|
|
||||||
|
This will be escaped: {{ data }}
|
||||||
|
This will not be escaped: {{ data|safe }}
|
||||||
|
|
||||||
|
Think of *safe* as shorthand for *safe from further escaping* or *can be
|
||||||
|
safely interpreted as HTML*. In this example, if ``data`` contains ``'<b>'``,
|
||||||
|
the output will be::
|
||||||
|
|
||||||
|
This will be escaped: <b>
|
||||||
|
This will not be escaped: <b>
|
||||||
|
|
||||||
|
For template blocks
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To control auto-escaping for a template, wrap the template (or just a
|
||||||
|
particular section of the template) in the ``autoescape`` tag, like so::
|
||||||
|
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
Hello {{ name }}
|
Hello {{ name }}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
|
||||||
The auto-escaping tag passes its effect onto templates that extend the
|
The ``autoescape`` tag takes either ``on`` or ``off`` as its argument. At
|
||||||
current one as well as templates included via the ``include`` tag, just like
|
times, you might want to force auto-escaping when it would otherwise be
|
||||||
all block tags.
|
disabled. Here is an example template::
|
||||||
|
|
||||||
The ``autoescape`` tag takes either ``on`` or ``off`` as its argument. At times, you might want to force auto-escaping when it would otherwise be disabled. For example::
|
|
||||||
|
|
||||||
Auto-escaping is on by default. Hello {{ name }}
|
Auto-escaping is on by default. Hello {{ name }}
|
||||||
|
|
||||||
@ -370,52 +418,60 @@ The ``autoescape`` tag takes either ``on`` or ``off`` as its argument. At times,
|
|||||||
|
|
||||||
Nor this: {{ other_data }}
|
Nor this: {{ other_data }}
|
||||||
{% autoescape on %}
|
{% autoescape on %}
|
||||||
Auto-escaping applies again, {{ name }}
|
Auto-escaping applies again: {{ name }}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
|
||||||
For individual variables, the ``safe`` filter can also be used to indicate
|
The auto-escaping tag passes its effect onto templates that extend the
|
||||||
that the contents should not be automatically escaped::
|
current one as well as templates included via the ``include`` tag, just like
|
||||||
|
all block tags. For example::
|
||||||
|
|
||||||
This will be escaped: {{ data }}
|
# base.html
|
||||||
This will not be escaped: {{ data|safe }}
|
|
||||||
|
|
||||||
Think of *safe* as shorthand for *safe from further escaping* or *can be
|
{% autoescape off %}
|
||||||
safely interpreted as HTML*. In this example, if ``data`` contains ``'<a>'``,
|
<h1>{% block title %}</h1>
|
||||||
the output will be::
|
{% block content %}
|
||||||
|
{% endautoescape %}
|
||||||
|
|
||||||
This will be escaped: <a>
|
|
||||||
This will not be escaped: <a>
|
|
||||||
|
|
||||||
Generally, you won't need to worry about auto-escaping very much. View
|
# child.html
|
||||||
developers and custom filter authors need to think about when their data
|
|
||||||
shouldn't be escaped and mark it appropriately. They are in a better position
|
|
||||||
to know when that should happen than the template author, so it is their
|
|
||||||
responsibility. By default, all output is escaped unless the template
|
|
||||||
processor is explicitly told otherwise.
|
|
||||||
|
|
||||||
You should also note that if you are trying to write a template that might be
|
{% extends "base.html" %}
|
||||||
used in situations where automatic escaping is enabled or disabled and you
|
{% block title %}This & that{% endblock %}
|
||||||
don't know which (such as when your template is included in other templates),
|
{% block content %}<b>Hello!</b>{% endblock %}
|
||||||
you can safely write as if you were in an ``{% autoescape off %}`` situation.
|
|
||||||
Scatter ``escape`` filters around for any variables that need escaping. When
|
Because auto-escaping is turned off in the base template, it will also be
|
||||||
auto-escaping is on, these extra filters won't change the output -- any
|
turned off in the child template, resulting in the following rendered HTML::
|
||||||
variables that use the ``escape`` filter do not have further automatic
|
|
||||||
escaping applied to them.
|
<h1>This & that</h1>
|
||||||
|
<b>Hello!</b>
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
|
||||||
|
Generally, template authors don't need to worry about auto-escaping very much.
|
||||||
|
Developers on the Python side (people writing views and custom filters) need to
|
||||||
|
think about the cases in which data shouldn't be escaped, and mark data
|
||||||
|
appropriately, so things Just Work in the template.
|
||||||
|
|
||||||
|
If you're creating a template that might be used in situations where you're
|
||||||
|
not sure whether auto-escaping is enabled, then add an ``escape`` filter to any
|
||||||
|
variable that needs escaping. When auto-escaping is on, there's no danger of
|
||||||
|
the ``escape`` filter *double-escaping* data -- the ``escape`` filter does not
|
||||||
|
affect auto-escaped variables.
|
||||||
|
|
||||||
String literals and automatic escaping
|
String literals and automatic escaping
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
Sometimes you will pass a string literal as an argument to a filter. For
|
As we mentioned earlier, filter arguments can be strings::
|
||||||
example::
|
|
||||||
|
|
||||||
{{ data|default:"This is a string literal." }}
|
{{ data|default:"This is a string literal." }}
|
||||||
|
|
||||||
All string literals are inserted **without** any automatic escaping into the
|
All string literals are inserted **without** any automatic escaping into the
|
||||||
template, if they are used (it's as if they were all passed through the
|
template -- they act as if they were all passed through the ``safe`` filter.
|
||||||
``safe`` filter). The reasoning behind this is that the template author is in
|
The reasoning behind this is that the template author is in control of what
|
||||||
control of what goes into the string literal, so they can make sure the text
|
goes into the string literal, so they can make sure the text is correctly
|
||||||
is correctly escaped when the template is written.
|
escaped when the template is written.
|
||||||
|
|
||||||
This means you would write ::
|
This means you would write ::
|
||||||
|
|
||||||
@ -426,7 +482,7 @@ This means you would write ::
|
|||||||
{{ data|default:"3 > 2" }} <-- Bad! Don't do this.
|
{{ data|default:"3 > 2" }} <-- Bad! Don't do this.
|
||||||
|
|
||||||
This doesn't affect what happens to data coming from the variable itself.
|
This doesn't affect what happens to data coming from the variable itself.
|
||||||
The variable's contents are still automatically escaped, if necessary, since
|
The variable's contents are still automatically escaped, if necessary, because
|
||||||
they're beyond the control of the template author.
|
they're beyond the control of the template author.
|
||||||
|
|
||||||
Using the built-in reference
|
Using the built-in reference
|
||||||
@ -1230,11 +1286,11 @@ once, after all other filters).
|
|||||||
|
|
||||||
Escapes a string's HTML. Specifically, it makes these replacements:
|
Escapes a string's HTML. Specifically, it makes these replacements:
|
||||||
|
|
||||||
* ``"&"`` to ``"&"``
|
* ``<`` is converted to ``<``
|
||||||
* ``<`` to ``"<"``
|
* ``>`` is converted to ``>``
|
||||||
* ``>`` to ``">"``
|
* ``'`` (single quote) is converted to ``'``
|
||||||
* ``'"'`` (double quote) to ``'"'``
|
* ``"`` (double quote) is converted to ``"``
|
||||||
* ``"'"`` (single quote) to ``'''``
|
* ``&`` is converted to ``&``
|
||||||
|
|
||||||
The escaping is only applied when the string is output, so it does not matter
|
The escaping is only applied when the string is output, so it does not matter
|
||||||
where in a chained sequence of filters you put ``escape``: it will always be
|
where in a chained sequence of filters you put ``escape``: it will always be
|
||||||
@ -1277,7 +1333,7 @@ value Template Output
|
|||||||
======== ======================= ======
|
======== ======================= ======
|
||||||
|
|
||||||
If used with a numeric integer argument, ``floatformat`` rounds a number to
|
If used with a numeric integer argument, ``floatformat`` rounds a number to
|
||||||
that many decimal places. For example:
|
that many decimal places. For example:
|
||||||
|
|
||||||
======== ========================= ======
|
======== ========================= ======
|
||||||
value Template Output
|
value Template Output
|
||||||
|
@ -727,134 +727,144 @@ Filters and auto-escaping
|
|||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
When you are writing a custom filter, you need to give some thought to how
|
When writing a custom filter, give some thought to how the filter will interact
|
||||||
this filter will interact with Django's auto-escaping behaviour. Firstly, you
|
with Django's auto-escaping behavior. Note that three types of strings can be
|
||||||
should realise that there are three types of strings that can be passed around
|
passed around inside the template code:
|
||||||
inside the template code:
|
|
||||||
|
|
||||||
* raw strings are the native Python ``str`` or ``unicode`` types. On
|
* **Raw strings** are the native Python ``str`` or ``unicode`` types. On
|
||||||
output, they are escaped if auto-escaping is in effect and presented
|
output, they're escaped if auto-escaping is in effect and presented
|
||||||
unchanged, otherwise.
|
unchanged, otherwise.
|
||||||
|
|
||||||
* "safe" strings are strings that are safe from further escaping at output
|
* **Safe strings** are strings that have been marked safe from further
|
||||||
time. Any necessary escaping has already been done. They are commonly used
|
escaping at output time. Any necessary escaping has already been done.
|
||||||
for output that contains raw HTML that is intended to be intrepreted on the
|
They're commonly used for output that contains raw HTML that is intended
|
||||||
client side.
|
to be interpreted as-is on the client side.
|
||||||
|
|
||||||
Internally, these strings are of type ``SafeString`` or ``SafeUnicode``,
|
Internally, these strings are of type ``SafeString`` or ``SafeUnicode``.
|
||||||
although they share a common base class in ``SafeData``, so you can test
|
They share a common base class of ``SafeData``, so you can test
|
||||||
for them using code like::
|
for them using code like::
|
||||||
|
|
||||||
if isinstance(value, SafeData):
|
if isinstance(value, SafeData):
|
||||||
# Do something with the "safe" string.
|
# Do something with the "safe" string.
|
||||||
|
|
||||||
* strings which are marked as "needing escaping" are *always* escaped on
|
* **Strings marked as "needing escaping"** are *always* escaped on
|
||||||
output, regardless of whether they are in an ``autoescape`` block or not.
|
output, regardless of whether they are in an ``autoescape`` block or not.
|
||||||
These strings are only escaped once, however, even if auto-escaping
|
These strings are only escaped once, however, even if auto-escaping
|
||||||
applies. This type of string is internally represented by the types
|
applies.
|
||||||
``EscapeString`` and ``EscapeUnicode``. You will not normally need to worry
|
|
||||||
about these; they exist for the implementation of the ``escape`` filter.
|
|
||||||
|
|
||||||
When you are writing a filter, your code will typically fall into one of two
|
Internally, these strings are of type ``EscapeString`` or
|
||||||
situations:
|
``EscapeUnicode``. Generally you don't have to worry about these; they
|
||||||
|
exist for the implementation of the ``escape`` filter.
|
||||||
|
|
||||||
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
Template filter code falls into one of two situations:
|
||||||
``'``, ``"`` or ``&``) into the result that were not already present. In
|
|
||||||
this case, you can let Django take care of all the auto-escaping handling
|
|
||||||
for you. All you need to do is put the ``is_safe`` attribute on your
|
|
||||||
filter function and set it to ``True``. This attribute tells Django that
|
|
||||||
is a "safe" string is passed into your filter, the result will still be
|
|
||||||
"safe" and if a non-safe string is passed in, Django will automatically
|
|
||||||
escape it, if necessary. The reason ``is_safe`` is necessary is because
|
|
||||||
there are plenty of normal string operations that will turn a ``SafeData``
|
|
||||||
object back into a normal ``str`` or ``unicode`` object and, rather than
|
|
||||||
try to catch them all, which would be very difficult, Django repairs the
|
|
||||||
damage after the filter has completed.
|
|
||||||
|
|
||||||
For example, suppose you have a filter that adds the string ``xx`` to the
|
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
||||||
end of any input. Since this introduces no dangerous HTML characters into
|
``'``, ``"`` or ``&``) into the result that were not already present. In
|
||||||
the result (aside from any that were already present), you should mark
|
this case, you can let Django take care of all the auto-escaping
|
||||||
your filter with ``is_safe``::
|
handling for you. All you need to do is put the ``is_safe`` attribute on
|
||||||
|
your filter function and set it to ``True``, like so::
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def myfilter(value):
|
||||||
|
return value
|
||||||
|
myfilter.is_safe = True
|
||||||
|
|
||||||
|
This attribute tells Django that if a "safe" string is passed into your
|
||||||
|
filter, the result will still be "safe" and if a non-safe string is
|
||||||
|
passed in, Django will automatically escape it, if necessary.
|
||||||
|
|
||||||
|
You can think of this as meaning "this filter is safe -- it doesn't
|
||||||
|
introduce any possibility of unsafe HTML."
|
||||||
|
|
||||||
|
The reason ``is_safe`` is necessary is because there are plenty of
|
||||||
|
normal string operations that will turn a ``SafeData`` object back into
|
||||||
|
a normal ``str`` or ``unicode`` object and, rather than try to catch
|
||||||
|
them all, which would be very difficult, Django repairs the damage after
|
||||||
|
the filter has completed.
|
||||||
|
|
||||||
|
For example, suppose you have a filter that adds the string ``xx`` to the
|
||||||
|
end of any input. Since this introduces no dangerous HTML characters to
|
||||||
|
the result (aside from any that were already present), you should mark
|
||||||
|
your filter with ``is_safe``::
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def add_xx(value):
|
def add_xx(value):
|
||||||
return '%sxx' % value
|
return '%sxx' % value
|
||||||
add_xx.is_safe = True
|
add_xx.is_safe = True
|
||||||
|
|
||||||
When this filter is used in a template where auto-escaping is enabled,
|
When this filter is used in a template where auto-escaping is enabled,
|
||||||
Django will escape the output whenever the input is not already marked as
|
Django will escape the output whenever the input is not already marked as
|
||||||
"safe".
|
"safe".
|
||||||
|
|
||||||
By default, ``is_safe`` defaults to ``False`` and you can omit it from
|
By default, ``is_safe`` defaults to ``False``, and you can omit it from
|
||||||
any filters where it isn't required.
|
any filters where it isn't required.
|
||||||
|
|
||||||
Be careful when deciding if your filter really does leave safe strings
|
Be careful when deciding if your filter really does leave safe strings
|
||||||
as safe. Sometimes if you are *removing* characters, you can
|
as safe. If you're *removing* characters, you might inadvertently leave
|
||||||
inadvertently leave unbalanced HTML tags or entities in the result.
|
unbalanced HTML tags or entities in the result. For example, removing a
|
||||||
For example, removing a ``>`` from the input might turn ``<a>`` into
|
``>`` from the input might turn ``<a>`` into ``<a``, which would need to
|
||||||
``<a``, which would need to be escaped on output to avoid causing
|
be escaped on output to avoid causing problems. Similarly, removing a
|
||||||
problems. Similarly, removing a semicolon (``;``) can turn ``&``
|
semicolon (``;``) can turn ``&`` into ``&``, which is no longer a
|
||||||
into ``&``, which is no longer a valid entity and thus needs
|
valid entity and thus needs further escaping. Most cases won't be nearly
|
||||||
further escaping. Most cases won't be nearly this tricky, but keep an
|
this tricky, but keep an eye out for any problems like that when
|
||||||
eye out for any problems like that when reviewing your code.
|
reviewing your code.
|
||||||
|
|
||||||
2. Alternatively, your filter code can manually take care of any necessary
|
2. Alternatively, your filter code can manually take care of any necessary
|
||||||
escaping. This is usually necessary when you are introducing new HTML
|
escaping. This is necessary when you're introducing new HTML markup into
|
||||||
markup into the result. You want to mark the output as safe from further
|
the result. You want to mark the output as safe from further
|
||||||
escaping so that your HTML markup isn't escaped further, so you'll need to
|
escaping so that your HTML markup isn't escaped further, so you'll need
|
||||||
handle the input yourself.
|
to handle the input yourself.
|
||||||
|
|
||||||
To mark the output as a safe string, use
|
To mark the output as a safe string, use ``django.utils.safestring.mark_safe()``.
|
||||||
``django.utils.safestring.mark_safe()``.
|
|
||||||
|
|
||||||
Be careful, though. You need to do more than just mark the output as
|
Be careful, though. You need to do more than just mark the output as
|
||||||
safe. You need to ensure it really *is* safe and what you do will often
|
safe. You need to ensure it really *is* safe, and what you do depends on
|
||||||
depend upon whether or not auto-escaping is in effect. The idea is to
|
whether auto-escaping is in effect. The idea is to write filters than
|
||||||
write filters than can operate in templates where auto-escaping is either
|
can operate in templates where auto-escaping is either on or off in
|
||||||
on or off in order to make things easier for your template authors.
|
order to make things easier for your template authors.
|
||||||
|
|
||||||
In order for you filter to know the current auto-escaping state, set the
|
In order for you filter to know the current auto-escaping state, set the
|
||||||
``needs_autoescape`` attribute to ``True`` on your function (if you don't
|
``needs_autoescape`` attribute to ``True`` on your function. (If you
|
||||||
specify this attribute, it defaults to ``False``). This attribute tells
|
don't specify this attribute, it defaults to ``False``). This attribute
|
||||||
Django that your filter function wants to be passed an extra keyword
|
tells Django that your filter function wants to be passed an extra
|
||||||
argument, called ``autoescape`` that is ``True`` is auto-escaping is in
|
keyword argument, called ``autoescape``, that is ``True`` is
|
||||||
effect and ``False`` otherwise.
|
auto-escaping is in effect and ``False`` otherwise.
|
||||||
|
|
||||||
An example might make this clearer. Let's write a filter that emphasizes
|
For example, let's write a filter that emphasizes the first character of
|
||||||
the first character of a string::
|
a string::
|
||||||
|
|
||||||
from django.utils.html import conditional_escape
|
from django.utils.html import conditional_escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
def initial_letter_filter(text, autoescape=None):
|
def initial_letter_filter(text, autoescape=None):
|
||||||
first, other = text[0] ,text[1:]
|
first, other = text[0], text[1:]
|
||||||
if autoescape:
|
if autoescape:
|
||||||
esc = conditional_escape
|
esc = conditional_escape
|
||||||
else:
|
else:
|
||||||
esc = lambda x: x
|
esc = lambda x: x
|
||||||
result = '<strong>%s</strong>%s' % (esc(first), esc(other))
|
result = '<strong>%s</strong>%s' % (esc(first), esc(other))
|
||||||
return mark_safe(result)
|
return mark_safe(result)
|
||||||
initial_letter_filter.needs_autoescape = True
|
initial_letter_filter.needs_autoescape = True
|
||||||
|
|
||||||
The ``needs_autoescape`` attribute on the filter function and the
|
The ``needs_autoescape`` attribute on the filter function and the
|
||||||
``autoescape`` keyword argument mean that our function will know whether
|
``autoescape`` keyword argument mean that our function will know whether
|
||||||
or not automatic escaping is in effect when the filter is called. We use
|
automatic escaping is in effect when the filter is called. We use
|
||||||
``autoescape`` to decide whether the input data needs to be passed through
|
``autoescape`` to decide whether the input data needs to be passed through
|
||||||
``django.utils.html.conditional_escape`` or not (in the latter case, we
|
``django.utils.html.conditional_escape`` or not. (In the latter case, we
|
||||||
just use the identity function as the "escape" function). The
|
just use the identity function as the "escape" function.) The
|
||||||
``conditional_escape()`` function is like ``escape()`` except it only
|
``conditional_escape()`` function is like ``escape()`` except it only
|
||||||
escapes input that is **not** a ``SafeData`` instance. If a ``SafeData``
|
escapes input that is **not** a ``SafeData`` instance. If a ``SafeData``
|
||||||
instance is passed to ``conditional_escape()``, the data is returned
|
instance is passed to ``conditional_escape()``, the data is returned
|
||||||
unchanged.
|
unchanged.
|
||||||
|
|
||||||
Finally, in the above example, we remember to mark the result as safe
|
Finally, in the above example, we remember to mark the result as safe
|
||||||
so that our HTML is inserted directly into the template without further
|
so that our HTML is inserted directly into the template without further
|
||||||
escaping.
|
escaping.
|
||||||
|
|
||||||
There is no need to worry about the ``is_safe`` attribute in this case
|
There's no need to worry about the ``is_safe`` attribute in this case
|
||||||
(although including it wouldn't hurt anything). Whenever you are manually
|
(although including it wouldn't hurt anything). Whenever you manually
|
||||||
handling the auto-escaping issues and returning a safe string, the
|
handle the auto-escaping issues and return a safe string, the
|
||||||
``is_safe`` attribute won't change anything either way.
|
``is_safe`` attribute won't change anything either way.
|
||||||
|
|
||||||
Writing custom template tags
|
Writing custom template tags
|
||||||
----------------------------
|
----------------------------
|
||||||
@ -981,7 +991,7 @@ Auto-escaping considerations
|
|||||||
|
|
||||||
The output from template tags is **not** automatically run through the
|
The output from template tags is **not** automatically run through the
|
||||||
auto-escaping filters. However, there are still a couple of things you should
|
auto-escaping filters. However, there are still a couple of things you should
|
||||||
keep in mind when writing a template tag:
|
keep in mind when writing a template tag.
|
||||||
|
|
||||||
If the ``render()`` function of your template stores the result in a context
|
If the ``render()`` function of your template stores the result in a context
|
||||||
variable (rather than returning the result in a string), it should take care
|
variable (rather than returning the result in a string), it should take care
|
||||||
@ -991,18 +1001,17 @@ time, so content that should be safe from further escaping needs to be marked
|
|||||||
as such.
|
as such.
|
||||||
|
|
||||||
Also, if your template tag creates a new context for performing some
|
Also, if your template tag creates a new context for performing some
|
||||||
sub-rendering, you should be careful to set the auto-escape attribute to the
|
sub-rendering, set the auto-escape attribute to the current context's value.
|
||||||
current context's value. The ``__init__`` method for the ``Context`` class
|
The ``__init__`` method for the ``Context`` class takes a parameter called
|
||||||
takes a parameter called ``autoescape`` that you can use for this purpose. For
|
``autoescape`` that you can use for this purpose. For example::
|
||||||
example::
|
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
# ...
|
# ...
|
||||||
new_context = Context({'var': obj}, autoescape=context.autoescape)
|
new_context = Context({'var': obj}, autoescape=context.autoescape)
|
||||||
# ... Do something with new_context ...
|
# ... Do something with new_context ...
|
||||||
|
|
||||||
This is not a very common situation, but it is sometimes useful, particularly
|
This is not a very common situation, but it's useful if you're rendering a
|
||||||
if you are rendering a template yourself. For example::
|
template yourself. For example::
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
t = template.load_template('small_fragment.html')
|
t = template.load_template('small_fragment.html')
|
||||||
@ -1010,7 +1019,7 @@ if you are rendering a template yourself. For example::
|
|||||||
|
|
||||||
If we had neglected to pass in the current ``context.autoescape`` value to our
|
If we had neglected to pass in the current ``context.autoescape`` value to our
|
||||||
new ``Context`` in this example, the results would have *always* been
|
new ``Context`` in this example, the results would have *always* been
|
||||||
automatically escaped, which may not be the desired behaviour if the template
|
automatically escaped, which may not be the desired behavior if the template
|
||||||
tag is used inside a ``{% autoescape off %}`` block.
|
tag is used inside a ``{% autoescape off %}`` block.
|
||||||
|
|
||||||
Registering the tag
|
Registering the tag
|
||||||
|
@ -48,7 +48,7 @@ Recall from Tutorial 1 that you start the development server like so::
|
|||||||
Now, open a Web browser and go to "/admin/" on your local domain -- e.g.,
|
Now, open a Web browser and go to "/admin/" on your local domain -- e.g.,
|
||||||
http://127.0.0.1:8000/admin/. You should see the admin's login screen:
|
http://127.0.0.1:8000/admin/. You should see the admin's login screen:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin01.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin01.png
|
||||||
:alt: Django admin login screen
|
:alt: Django admin login screen
|
||||||
|
|
||||||
Enter the admin site
|
Enter the admin site
|
||||||
@ -57,9 +57,9 @@ Enter the admin site
|
|||||||
Now, try logging in. (You created a superuser account in the first part of this
|
Now, try logging in. (You created a superuser account in the first part of this
|
||||||
tutorial, remember?) You should see the Django admin index page:
|
tutorial, remember?) You should see the Django admin index page:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin02t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin02t.png
|
||||||
:alt: Django admin index page
|
:alt: Django admin index page
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin02.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin02.png
|
||||||
|
|
||||||
You should see a few other types of editable content, including groups, users
|
You should see a few other types of editable content, including groups, users
|
||||||
and sites. These are core features Django ships with by default.
|
and sites. These are core features Django ships with by default.
|
||||||
@ -95,23 +95,23 @@ Explore the free admin functionality
|
|||||||
Now that ``Poll`` has the inner ``Admin`` class, Django knows that it should be
|
Now that ``Poll`` has the inner ``Admin`` class, Django knows that it should be
|
||||||
displayed on the admin index page:
|
displayed on the admin index page:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin03t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin03t.png
|
||||||
:alt: Django admin index page, now with polls displayed
|
:alt: Django admin index page, now with polls displayed
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin03.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin03.png
|
||||||
|
|
||||||
Click "Polls." Now you're at the "change list" page for polls. This page
|
Click "Polls." Now you're at the "change list" page for polls. This page
|
||||||
displays all the polls in the database and lets you choose one to change it.
|
displays all the polls in the database and lets you choose one to change it.
|
||||||
There's the "What's up?" poll we created in the first tutorial:
|
There's the "What's up?" poll we created in the first tutorial:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin04t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin04t.png
|
||||||
:alt: Polls change list page
|
:alt: Polls change list page
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin04.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin04.png
|
||||||
|
|
||||||
Click the "What's up?" poll to edit it:
|
Click the "What's up?" poll to edit it:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin05t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin05t.png
|
||||||
:alt: Editing form for poll object
|
:alt: Editing form for poll object
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin05.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin05.png
|
||||||
|
|
||||||
Things to note here:
|
Things to note here:
|
||||||
|
|
||||||
@ -138,9 +138,9 @@ click "Save and continue editing." Then click "History" in the upper right.
|
|||||||
You'll see a page listing all changes made to this object via the Django admin,
|
You'll see a page listing all changes made to this object via the Django admin,
|
||||||
with the timestamp and username of the person who made the change:
|
with the timestamp and username of the person who made the change:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin06t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin06t.png
|
||||||
:alt: History page for poll object
|
:alt: History page for poll object
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin06.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin06.png
|
||||||
|
|
||||||
Customize the admin form
|
Customize the admin form
|
||||||
========================
|
========================
|
||||||
@ -157,7 +157,7 @@ Let's customize this a bit. We can reorder the fields by explicitly adding a
|
|||||||
|
|
||||||
That made the "Publication date" show up first instead of second:
|
That made the "Publication date" show up first instead of second:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin07.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin07.png
|
||||||
:alt: Fields have been reordered
|
:alt: Fields have been reordered
|
||||||
|
|
||||||
This isn't impressive with only two fields, but for admin forms with dozens
|
This isn't impressive with only two fields, but for admin forms with dozens
|
||||||
@ -175,9 +175,9 @@ up into fieldsets::
|
|||||||
The first element of each tuple in ``fields`` is the title of the fieldset.
|
The first element of each tuple in ``fields`` is the title of the fieldset.
|
||||||
Here's what our form looks like now:
|
Here's what our form looks like now:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin08t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin08t.png
|
||||||
:alt: Form has fieldsets now
|
:alt: Form has fieldsets now
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin08.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin08.png
|
||||||
|
|
||||||
You can assign arbitrary HTML classes to each fieldset. Django provides a
|
You can assign arbitrary HTML classes to each fieldset. Django provides a
|
||||||
``"collapse"`` class that displays a particular fieldset initially collapsed.
|
``"collapse"`` class that displays a particular fieldset initially collapsed.
|
||||||
@ -190,7 +190,7 @@ aren't commonly used::
|
|||||||
('Date information', {'fields': ('pub_date',), 'classes': 'collapse'}),
|
('Date information', {'fields': ('pub_date',), 'classes': 'collapse'}),
|
||||||
)
|
)
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin09.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin09.png
|
||||||
:alt: Fieldset is initially collapsed
|
:alt: Fieldset is initially collapsed
|
||||||
|
|
||||||
Adding related objects
|
Adding related objects
|
||||||
@ -213,7 +213,7 @@ that would look like::
|
|||||||
Now "Choices" is an available option in the Django admin. The "Add choice" form
|
Now "Choices" is an available option in the Django admin. The "Add choice" form
|
||||||
looks like this:
|
looks like this:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin10.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin10.png
|
||||||
:alt: Choice admin page
|
:alt: Choice admin page
|
||||||
|
|
||||||
In that form, the "Poll" field is a select box containing every poll in the
|
In that form, the "Poll" field is a select box containing every poll in the
|
||||||
@ -250,9 +250,9 @@ deletion of that existing Choice object."
|
|||||||
|
|
||||||
Load the "Add poll" page to see how that looks:
|
Load the "Add poll" page to see how that looks:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin11t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin11t.png
|
||||||
:alt: Add poll page now has choices on it
|
:alt: Add poll page now has choices on it
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin11.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin11.png
|
||||||
|
|
||||||
It works like this: There are three slots for related Choices -- as specified
|
It works like this: There are three slots for related Choices -- as specified
|
||||||
by ``num_in_admin`` -- but each time you come back to the "Change" page for an
|
by ``num_in_admin`` -- but each time you come back to the "Change" page for an
|
||||||
@ -270,7 +270,7 @@ alternate way of displaying inline related objects::
|
|||||||
With that ``edit_inline=models.TABULAR`` (instead of ``models.STACKED``), the
|
With that ``edit_inline=models.TABULAR`` (instead of ``models.STACKED``), the
|
||||||
related objects are displayed in a more compact, table-based format:
|
related objects are displayed in a more compact, table-based format:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin12.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin12.png
|
||||||
:alt: Add poll page now has more compact choices
|
:alt: Add poll page now has more compact choices
|
||||||
|
|
||||||
Customize the admin change list
|
Customize the admin change list
|
||||||
@ -281,9 +281,9 @@ Now that the Poll admin page is looking good, let's make some tweaks to the
|
|||||||
|
|
||||||
Here's what it looks like at this point:
|
Here's what it looks like at this point:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin04t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin04t.png
|
||||||
:alt: Polls change list page
|
:alt: Polls change list page
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin04.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin04.png
|
||||||
|
|
||||||
By default, Django displays the ``str()`` of each object. But sometimes it'd
|
By default, Django displays the ``str()`` of each object. But sometimes it'd
|
||||||
be more helpful if we could display individual fields. To do that, use the
|
be more helpful if we could display individual fields. To do that, use the
|
||||||
@ -303,9 +303,9 @@ method from Tutorial 1::
|
|||||||
|
|
||||||
Now the poll change list page looks like this:
|
Now the poll change list page looks like this:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin13t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin13t.png
|
||||||
:alt: Polls change list page, updated
|
:alt: Polls change list page, updated
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin13.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin13.png
|
||||||
|
|
||||||
You can click on the column headers to sort by those values -- except in the
|
You can click on the column headers to sort by those values -- except in the
|
||||||
case of the ``was_published_today`` header, because sorting by the output of
|
case of the ``was_published_today`` header, because sorting by the output of
|
||||||
@ -327,9 +327,9 @@ following line to ``Poll.Admin``::
|
|||||||
That adds a "Filter" sidebar that lets people filter the change list by the
|
That adds a "Filter" sidebar that lets people filter the change list by the
|
||||||
``pub_date`` field:
|
``pub_date`` field:
|
||||||
|
|
||||||
.. image:: http://media.djangoproject.com/img/doc/tutorial/admin14t.png
|
.. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin14t.png
|
||||||
:alt: Polls change list page, updated
|
:alt: Polls change list page, updated
|
||||||
:target: http://media.djangoproject.com/img/doc/tutorial/admin14.png
|
:target: http://media.djangoproject.com/img/doc/tutorial-trunk/admin14.png
|
||||||
|
|
||||||
The type of filter displayed depends on the type of field you're filtering on.
|
The type of filter displayed depends on the type of field you're filtering on.
|
||||||
Because ``pub_date`` is a DateTimeField, Django knows to give the default
|
Because ``pub_date`` is a DateTimeField, Django knows to give the default
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
39. Empty model tests
|
40. Empty model tests
|
||||||
|
|
||||||
These test that things behave sensibly for the rare corner-case of a model with
|
These test that things behave sensibly for the rare corner-case of a model with
|
||||||
no fields.
|
no fields.
|
||||||
|
@ -48,4 +48,9 @@ u'Default headline'
|
|||||||
>>> d = now - a.pub_date
|
>>> d = now - a.pub_date
|
||||||
>>> d.seconds < 5
|
>>> d.seconds < 5
|
||||||
True
|
True
|
||||||
|
|
||||||
|
# make sure that SafeUnicode fields work
|
||||||
|
>>> from django.utils.safestring import SafeUnicode
|
||||||
|
>>> a.headline = SafeUnicode(u'SafeUnicode Headline')
|
||||||
|
>>> a.save()
|
||||||
"""}
|
"""}
|
||||||
|
@ -78,7 +78,7 @@ Http404: No Article matches the given query.
|
|||||||
>>> get_object_or_404(Author.objects.all())
|
>>> get_object_or_404(Author.objects.all())
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
AssertionError: get() returned more than one Author -- it returned ...! Lookup parameters were {}
|
MultipleObjectsReturned: get() returned more than one Author -- it returned ...! Lookup parameters were {}
|
||||||
|
|
||||||
# Using an EmptyQuerySet raises a Http404 error.
|
# Using an EmptyQuerySet raises a Http404 error.
|
||||||
>>> get_object_or_404(Article.objects.none(), title__contains="Run")
|
>>> get_object_or_404(Article.objects.none(), title__contains="Run")
|
||||||
|
@ -107,6 +107,10 @@ class Car(models.Model):
|
|||||||
colour = models.CharField(max_length=5)
|
colour = models.CharField(max_length=5)
|
||||||
model = models.ForeignKey(Model)
|
model = models.ForeignKey(Model)
|
||||||
|
|
||||||
|
class MissingRelations(models.Model):
|
||||||
|
rel1 = models.ForeignKey("Rel1")
|
||||||
|
rel2 = models.ManyToManyField("Rel2")
|
||||||
|
|
||||||
model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute.
|
model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute.
|
||||||
invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute.
|
invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute.
|
||||||
invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute.
|
invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute.
|
||||||
@ -189,4 +193,6 @@ invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related
|
|||||||
invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'.
|
invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'.
|
||||||
invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_3' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_3'.
|
invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_3' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_3'.
|
||||||
invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_4' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_4'.
|
invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_4' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_4'.
|
||||||
|
invalid_models.missingrelations: 'rel2' has m2m relation with model Rel2, which has not been installed
|
||||||
|
invalid_models.missingrelations: 'rel1' has relation with model Rel1, which has not been installed
|
||||||
"""
|
"""
|
||||||
|
@ -1,25 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
36. Generating HTML forms from models
|
XX. Generating HTML forms from models
|
||||||
|
|
||||||
Django provides shortcuts for creating Form objects from a model class and a
|
This is mostly just a reworking of the form_for_model/form_for_instance tests
|
||||||
model instance.
|
to use ModelForm. As such, the text may not make sense in all cases, and the
|
||||||
|
examples are probably a poor fit for the ModelForm syntax. In other words,
|
||||||
The function django.newforms.form_for_model() takes a model class and returns
|
most of these tests should be rewritten.
|
||||||
a Form that is tied to the model. This Form works just like any other Form,
|
|
||||||
with one additional method: save(). The save() method creates an instance
|
|
||||||
of the model and returns that newly created instance. It saves the instance to
|
|
||||||
the database if save(commit=True), which is default. If you pass
|
|
||||||
commit=False, then you'll get the object without committing the changes to the
|
|
||||||
database.
|
|
||||||
|
|
||||||
The function django.newforms.form_for_instance() takes a model instance and
|
|
||||||
returns a Form that is tied to the instance. This form works just like any
|
|
||||||
other Form, with one additional method: save(). The save()
|
|
||||||
method updates the model instance. It also takes a commit=True parameter.
|
|
||||||
|
|
||||||
The function django.newforms.save_instance() takes a bound form instance and a
|
|
||||||
model instance and saves the form's cleaned_data into the instance. It also takes
|
|
||||||
a commit=True parameter.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@ -30,23 +15,6 @@ ARTICLE_STATUS = (
|
|||||||
(3, 'Live'),
|
(3, 'Live'),
|
||||||
)
|
)
|
||||||
|
|
||||||
STEERING_TYPE = (
|
|
||||||
('left', 'Left steering wheel'),
|
|
||||||
('right', 'Right steering wheel'),
|
|
||||||
)
|
|
||||||
|
|
||||||
FUEL_TYPE = (
|
|
||||||
('gas', 'Gasoline'),
|
|
||||||
('diesel', 'Diesel'),
|
|
||||||
('other', 'Other'),
|
|
||||||
)
|
|
||||||
|
|
||||||
TRANSMISSION_TYPE = (
|
|
||||||
('at', 'Automatic'),
|
|
||||||
('mt', 'Manual'),
|
|
||||||
('cvt', 'CVT'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
name = models.CharField(max_length=20)
|
name = models.CharField(max_length=20)
|
||||||
slug = models.SlugField(max_length=20)
|
slug = models.SlugField(max_length=20)
|
||||||
@ -87,21 +55,119 @@ class PhoneNumber(models.Model):
|
|||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.phone
|
return self.phone
|
||||||
|
|
||||||
class Car(models.Model):
|
|
||||||
name = models.CharField(max_length=50)
|
|
||||||
steering = models.CharField(max_length=5, choices=STEERING_TYPE, default='left')
|
|
||||||
fuel = models.CharField(max_length=10, choices=FUEL_TYPE)
|
|
||||||
transmission = models.CharField(max_length=3, choices=TRANSMISSION_TYPE, blank=True, help_text='Leave empty if not applicable.')
|
|
||||||
|
|
||||||
__test__ = {'API_TESTS': """
|
__test__ = {'API_TESTS': """
|
||||||
>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
|
>>> from django import newforms as forms
|
||||||
|
>>> from django.newforms.models import ModelForm
|
||||||
|
|
||||||
|
The bare bones, absolutely nothing custom, basic case.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['name', 'slug', 'url']
|
||||||
|
|
||||||
|
|
||||||
|
Extra fields.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
... some_extra_field = forms.BooleanField()
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['name', 'slug', 'url', 'some_extra_field']
|
||||||
|
|
||||||
|
|
||||||
|
Replacing a field.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
... url = forms.BooleanField()
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields['url'].__class__
|
||||||
|
<class 'django.newforms.fields.BooleanField'>
|
||||||
|
|
||||||
|
|
||||||
|
Using 'fields'.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
... fields = ['url']
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['url']
|
||||||
|
|
||||||
|
|
||||||
|
Using 'exclude'
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
... exclude = ['url']
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
|
Using 'fields' *and* 'exclude'. Not sure why you'd want to do this, but uh,
|
||||||
|
"be liberal in what you accept" and all.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
...
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
... fields = ['name', 'url']
|
||||||
|
... exclude = ['url']
|
||||||
|
|
||||||
|
>>> CategoryForm.base_fields.keys()
|
||||||
|
['name']
|
||||||
|
|
||||||
|
Don't allow more than one 'model' definition in the inheritance hierarchy.
|
||||||
|
Technically, it would generate a valid form, but the fact that the resulting
|
||||||
|
save method won't deal with multiple objects is likely to trip up people not
|
||||||
|
familiar with the mechanics.
|
||||||
|
|
||||||
|
>>> class CategoryForm(ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
|
||||||
|
>>> class BadForm(CategoryForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ImproperlyConfigured: BadForm defines more than one model.
|
||||||
|
|
||||||
|
>>> class ArticleForm(ModelForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
|
||||||
|
>>> class BadForm(ArticleForm, CategoryForm):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ImproperlyConfigured: BadForm's base classes define more than one model.
|
||||||
|
|
||||||
|
|
||||||
|
# Old form_for_x tests #######################################################
|
||||||
|
|
||||||
|
>>> from django.newforms import ModelForm, CharField
|
||||||
>>> import datetime
|
>>> import datetime
|
||||||
|
|
||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
[]
|
[]
|
||||||
|
|
||||||
>>> CategoryForm = form_for_model(Category)
|
>>> class CategoryForm(ModelForm):
|
||||||
>>> f = CategoryForm()
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
>>> f = CategoryForm(Category())
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
|
<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
|
||||||
<tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
|
<tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
|
||||||
@ -113,13 +179,13 @@ __test__ = {'API_TESTS': """
|
|||||||
>>> print f['name']
|
>>> print f['name']
|
||||||
<input id="id_name" type="text" name="name" maxlength="20" />
|
<input id="id_name" type="text" name="name" maxlength="20" />
|
||||||
|
|
||||||
>>> f = CategoryForm(auto_id=False)
|
>>> f = CategoryForm(Category(), auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Name: <input type="text" name="name" maxlength="20" /></li>
|
<li>Name: <input type="text" name="name" maxlength="20" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" maxlength="20" /></li>
|
<li>Slug: <input type="text" name="slug" maxlength="20" /></li>
|
||||||
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
|
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
|
||||||
|
|
||||||
>>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
|
>>> f = CategoryForm(Category(), {'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
@ -130,7 +196,7 @@ True
|
|||||||
>>> Category.objects.all()
|
>>> Category.objects.all()
|
||||||
[<Category: Entertainment>]
|
[<Category: Entertainment>]
|
||||||
|
|
||||||
>>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
|
>>> f = CategoryForm(Category(), {'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
@ -144,7 +210,7 @@ True
|
|||||||
If you call save() with commit=False, then it will return an object that
|
If you call save() with commit=False, then it will return an object that
|
||||||
hasn't yet been saved to the database. In this case, it's up to you to call
|
hasn't yet been saved to the database. In this case, it's up to you to call
|
||||||
save() on the resulting model instance.
|
save() on the resulting model instance.
|
||||||
>>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
|
>>> f = CategoryForm(Category(), {'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
@ -159,7 +225,7 @@ True
|
|||||||
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
|
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
|
||||||
|
|
||||||
If you call save() with invalid data, you'll get a ValueError.
|
If you call save() with invalid data, you'll get a ValueError.
|
||||||
>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
|
>>> f = CategoryForm(Category(), {'name': '', 'slug': '', 'url': 'foo'})
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
|
{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
@ -170,7 +236,7 @@ AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
|
|||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValueError: The Category could not be created because the data didn't validate.
|
ValueError: The Category could not be created because the data didn't validate.
|
||||||
>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
|
>>> f = CategoryForm(Category(), {'name': '', 'slug': '', 'url': 'foo'})
|
||||||
>>> f.save()
|
>>> f.save()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
@ -184,8 +250,10 @@ Create a couple of Writers.
|
|||||||
|
|
||||||
ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any
|
ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any
|
||||||
fields with the 'choices' attribute are represented by a ChoiceField.
|
fields with the 'choices' attribute are represented by a ChoiceField.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), auto_id=False)
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
||||||
<tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
|
<tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
|
||||||
@ -214,28 +282,23 @@ model created with such a form, you need to ensure that the fields
|
|||||||
that are _not_ on the form have default values, or are allowed to have
|
that are _not_ on the form have default values, or are allowed to have
|
||||||
a value of None. If a field isn't specified on a form, the object created
|
a value of None. If a field isn't specified on a form, the object created
|
||||||
from the form can't provide a value for that field!
|
from the form can't provide a value for that field!
|
||||||
>>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date'))
|
>>> class PartialArticleForm(ModelForm):
|
||||||
>>> f = PartialArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
... fields = ('headline','pub_date')
|
||||||
|
>>> f = PartialArticleForm(Article(), auto_id=False)
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
|
||||||
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
|
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
|
||||||
|
|
||||||
You can pass a custom Form class to form_for_model. Make sure it's a
|
|
||||||
subclass of BaseForm, not Form.
|
|
||||||
>>> class CustomForm(BaseForm):
|
|
||||||
... def say_hello(self):
|
|
||||||
... print 'hello'
|
|
||||||
>>> CategoryForm = form_for_model(Category, form=CustomForm)
|
|
||||||
>>> f = CategoryForm()
|
|
||||||
>>> f.say_hello()
|
|
||||||
hello
|
|
||||||
|
|
||||||
Use form_for_instance to create a Form from a model instance. The difference
|
Use form_for_instance to create a Form from a model instance. The difference
|
||||||
between this Form and one created via form_for_model is that the object's
|
between this Form and one created via form_for_model is that the object's
|
||||||
current values are inserted as 'initial' data in each Field.
|
current values are inserted as 'initial' data in each Field.
|
||||||
>>> w = Writer.objects.get(name='Mike Royko')
|
>>> w = Writer.objects.get(name='Mike Royko')
|
||||||
>>> RoykoForm = form_for_instance(w)
|
>>> class RoykoForm(ModelForm):
|
||||||
>>> f = RoykoForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Writer
|
||||||
|
>>> f = RoykoForm(w, auto_id=False)
|
||||||
>>> print f
|
>>> print f
|
||||||
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
|
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
|
||||||
|
|
||||||
@ -243,8 +306,10 @@ current values are inserted as 'initial' data in each Field.
|
|||||||
>>> art.save()
|
>>> art.save()
|
||||||
>>> art.id
|
>>> art.id
|
||||||
1
|
1
|
||||||
>>> TestArticleForm = form_for_instance(art)
|
>>> class TestArticleForm(ModelForm):
|
||||||
>>> f = TestArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = TestArticleForm(art, auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
|
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
|
<li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
|
||||||
@ -266,7 +331,7 @@ current values are inserted as 'initial' data in each Field.
|
|||||||
<option value="2">It's a test</option>
|
<option value="2">It's a test</option>
|
||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
>>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'})
|
>>> f = TestArticleForm(art, {'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> test_art = f.save()
|
>>> test_art = f.save()
|
||||||
@ -278,8 +343,11 @@ u'Test headline'
|
|||||||
|
|
||||||
You can create a form over a subset of the available fields
|
You can create a form over a subset of the available fields
|
||||||
by specifying a 'fields' argument to form_for_instance.
|
by specifying a 'fields' argument to form_for_instance.
|
||||||
>>> PartialArticleForm = form_for_instance(art, fields=('headline', 'slug', 'pub_date'))
|
>>> class PartialArticleForm(ModelForm):
|
||||||
>>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
... fields=('headline', 'slug', 'pub_date')
|
||||||
|
>>> f = PartialArticleForm(art, {'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
|
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
|
||||||
@ -299,8 +367,10 @@ Add some categories and test the many-to-many form output.
|
|||||||
>>> new_art.categories.add(Category.objects.get(name='Entertainment'))
|
>>> new_art.categories.add(Category.objects.get(name='Entertainment'))
|
||||||
>>> new_art.categories.all()
|
>>> new_art.categories.all()
|
||||||
[<Category: Entertainment>]
|
[<Category: Entertainment>]
|
||||||
>>> TestArticleForm = form_for_instance(new_art)
|
>>> class TestArticleForm(ModelForm):
|
||||||
>>> f = TestArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = TestArticleForm(new_art, auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
|
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
|
||||||
@ -323,7 +393,7 @@ Add some categories and test the many-to-many form output.
|
|||||||
<option value="3">Third test</option>
|
<option value="3">Third test</option>
|
||||||
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
|
||||||
|
|
||||||
>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
|
>>> f = TestArticleForm(new_art, {'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
|
||||||
... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
|
... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
@ -333,7 +403,7 @@ Add some categories and test the many-to-many form output.
|
|||||||
[<Category: Entertainment>, <Category: It's a test>]
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
Now, submit form data with no categories. This deletes the existing categories.
|
Now, submit form data with no categories. This deletes the existing categories.
|
||||||
>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
|
>>> f = TestArticleForm(new_art, {'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
|
||||||
... 'writer': u'1', 'article': u'Hello.'})
|
... 'writer': u'1', 'article': u'Hello.'})
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
@ -343,8 +413,10 @@ Now, submit form data with no categories. This deletes the existing categories.
|
|||||||
[]
|
[]
|
||||||
|
|
||||||
Create a new article, with categories, via the form.
|
Create a new article, with categories, via the form.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||||
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
@ -354,8 +426,10 @@ Create a new article, with categories, via the form.
|
|||||||
[<Category: Entertainment>, <Category: It's a test>]
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
Create a new article, with no categories, via the form.
|
Create a new article, with no categories, via the form.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||||
... 'writer': u'1', 'article': u'Test.'})
|
... 'writer': u'1', 'article': u'Test.'})
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
@ -366,8 +440,10 @@ Create a new article, with no categories, via the form.
|
|||||||
|
|
||||||
Create a new article, with categories, via the form, but use commit=False.
|
Create a new article, with categories, via the form, but use commit=False.
|
||||||
The m2m data won't be saved until save_m2m() is invoked on the form.
|
The m2m data won't be saved until save_m2m() is invoked on the form.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
|
||||||
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
||||||
>>> new_art = f.save(commit=False)
|
>>> new_art = f.save(commit=False)
|
||||||
|
|
||||||
@ -386,10 +462,10 @@ The m2m data won't be saved until save_m2m() is invoked on the form.
|
|||||||
>>> new_art.categories.order_by('name')
|
>>> new_art.categories.order_by('name')
|
||||||
[<Category: Entertainment>, <Category: It's a test>]
|
[<Category: Entertainment>, <Category: It's a test>]
|
||||||
|
|
||||||
Here, we define a custom Form. Because it happens to have the same fields as
|
Here, we define a custom ModelForm. Because it happens to have the same fields as
|
||||||
the Category model, we can use save_instance() to apply its changes to an
|
the Category model, we can just call the form's save() to apply its changes to an
|
||||||
existing Category instance.
|
existing Category instance.
|
||||||
>>> class ShortCategory(Form):
|
>>> class ShortCategory(ModelForm):
|
||||||
... name = CharField(max_length=5)
|
... name = CharField(max_length=5)
|
||||||
... slug = CharField(max_length=5)
|
... slug = CharField(max_length=5)
|
||||||
... url = CharField(max_length=3)
|
... url = CharField(max_length=3)
|
||||||
@ -398,8 +474,8 @@ existing Category instance.
|
|||||||
<Category: Third test>
|
<Category: Third test>
|
||||||
>>> cat.id
|
>>> cat.id
|
||||||
3
|
3
|
||||||
>>> sc = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'})
|
>>> form = ShortCategory(cat, {'name': 'Third', 'slug': 'third', 'url': '3rd'})
|
||||||
>>> save_instance(sc, cat)
|
>>> form.save()
|
||||||
<Category: Third>
|
<Category: Third>
|
||||||
>>> Category.objects.get(id=3)
|
>>> Category.objects.get(id=3)
|
||||||
<Category: Third>
|
<Category: Third>
|
||||||
@ -407,8 +483,10 @@ existing Category instance.
|
|||||||
Here, we demonstrate that choices for a ForeignKey ChoiceField are determined
|
Here, we demonstrate that choices for a ForeignKey ChoiceField are determined
|
||||||
at runtime, based on the data in the database when the form is displayed, not
|
at runtime, based on the data in the database when the form is displayed, not
|
||||||
the data in the database when the form is instantiated.
|
the data in the database when the form is instantiated.
|
||||||
>>> ArticleForm = form_for_model(Article)
|
>>> class ArticleForm(ModelForm):
|
||||||
>>> f = ArticleForm(auto_id=False)
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
>>> f = ArticleForm(Article(), auto_id=False)
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li>Headline: <input type="text" name="headline" maxlength="50" /></li>
|
<li>Headline: <input type="text" name="headline" maxlength="50" /></li>
|
||||||
<li>Slug: <input type="text" name="slug" maxlength="50" /></li>
|
<li>Slug: <input type="text" name="slug" maxlength="50" /></li>
|
||||||
@ -609,60 +687,12 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
|
|||||||
|
|
||||||
# PhoneNumberField ############################################################
|
# PhoneNumberField ############################################################
|
||||||
|
|
||||||
>>> PhoneNumberForm = form_for_model(PhoneNumber)
|
>>> class PhoneNumberForm(ModelForm):
|
||||||
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
... class Meta:
|
||||||
|
... model = PhoneNumber
|
||||||
|
>>> f = PhoneNumberForm(PhoneNumber(), {'phone': '(312) 555-1212', 'description': 'Assistance'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
{'phone': u'312-555-1212', 'description': u'Assistance'}
|
{'phone': u'312-555-1212', 'description': u'Assistance'}
|
||||||
|
|
||||||
# form_for_* blank choices ####################################################
|
|
||||||
|
|
||||||
Show the form for a new Car. Note that steering field doesn't include the blank choice,
|
|
||||||
because the field is obligatory and has an explicit default.
|
|
||||||
>>> CarForm = form_for_model(Car)
|
|
||||||
>>> f = CarForm(auto_id=False)
|
|
||||||
>>> print f
|
|
||||||
<tr><th>Name:</th><td><input type="text" name="name" maxlength="50" /></td></tr>
|
|
||||||
<tr><th>Steering:</th><td><select name="steering">
|
|
||||||
<option value="left" selected="selected">Left steering wheel</option>
|
|
||||||
<option value="right">Right steering wheel</option>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><th>Fuel:</th><td><select name="fuel">
|
|
||||||
<option value="" selected="selected">---------</option>
|
|
||||||
<option value="gas">Gasoline</option>
|
|
||||||
<option value="diesel">Diesel</option>
|
|
||||||
<option value="other">Other</option>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><th>Transmission:</th><td><select name="transmission">
|
|
||||||
<option value="" selected="selected">---------</option>
|
|
||||||
<option value="at">Automatic</option>
|
|
||||||
<option value="mt">Manual</option>
|
|
||||||
<option value="cvt">CVT</option>
|
|
||||||
</select><br />Leave empty if not applicable.</td></tr>
|
|
||||||
|
|
||||||
Create a Car, and display the form for modifying it. Note that now the fuel
|
|
||||||
selector doesn't include the blank choice as well, since the field is
|
|
||||||
obligatory and can not be changed to be blank.
|
|
||||||
>>> honda = Car(name='Honda Accord Wagon', steering='right', fuel='gas', transmission='at')
|
|
||||||
>>> honda.save()
|
|
||||||
>>> HondaForm = form_for_instance(honda)
|
|
||||||
>>> f = HondaForm(auto_id=False)
|
|
||||||
>>> print f
|
|
||||||
<tr><th>Name:</th><td><input type="text" name="name" value="Honda Accord Wagon" maxlength="50" /></td></tr>
|
|
||||||
<tr><th>Steering:</th><td><select name="steering">
|
|
||||||
<option value="left">Left steering wheel</option>
|
|
||||||
<option value="right" selected="selected">Right steering wheel</option>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><th>Fuel:</th><td><select name="fuel">
|
|
||||||
<option value="gas" selected="selected">Gasoline</option>
|
|
||||||
<option value="diesel">Diesel</option>
|
|
||||||
<option value="other">Other</option>
|
|
||||||
</select></td></tr>
|
|
||||||
<tr><th>Transmission:</th><td><select name="transmission">
|
|
||||||
<option value="">---------</option>
|
|
||||||
<option value="at" selected="selected">Automatic</option>
|
|
||||||
<option value="mt">Manual</option>
|
|
||||||
<option value="cvt">CVT</option>
|
|
||||||
</select><br />Leave empty if not applicable.</td></tr>
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
40. Tests for select_related()
|
41. Tests for select_related()
|
||||||
|
|
||||||
``select_related()`` follows all relationships and pre-caches any foreign key
|
``select_related()`` follows all relationships and pre-caches any foreign key
|
||||||
values so that complex trees can be fetched in a single query. However, this
|
values so that complex trees can be fetched in a single query. However, this
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
41. Serialization
|
42. Serialization
|
||||||
|
|
||||||
``django.core.serializers`` provides interfaces to converting Django querysets
|
``django.core.serializers`` provides interfaces to converting Django querysets
|
||||||
to and from "flat" data (i.e. strings).
|
to and from "flat" data (i.e. strings).
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
"""
|
"""
|
||||||
38. Testing using the Test Client
|
39. Testing using the Test Client
|
||||||
|
|
||||||
The test client is a class that can act like a simple
|
The test client is a class that can act like a simple
|
||||||
browser for testing purposes.
|
browser for testing purposes.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
37. User-registered management commands
|
38. User-registered management commands
|
||||||
|
|
||||||
The manage.py utility provides a number of useful commands for managing a
|
The manage.py utility provides a number of useful commands for managing a
|
||||||
Django project. If you want to add a utility command of your own, you can.
|
Django project. If you want to add a utility command of your own, you can.
|
||||||
|
2
tests/regressiontests/cache/tests.py
vendored
2
tests/regressiontests/cache/tests.py
vendored
@ -72,6 +72,8 @@ class Cache(unittest.TestCase):
|
|||||||
'function' : f,
|
'function' : f,
|
||||||
'class' : C,
|
'class' : C,
|
||||||
}
|
}
|
||||||
|
cache.set("stuff", stuff)
|
||||||
|
self.assertEqual(cache.get("stuff"), stuff)
|
||||||
|
|
||||||
def test_expiration(self):
|
def test_expiration(self):
|
||||||
# expiration
|
# expiration
|
||||||
|
40
tests/regressiontests/forms/localflavor/za.py
Normal file
40
tests/regressiontests/forms/localflavor/za.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
tests = r"""
|
||||||
|
# ZAIDField #################################################################
|
||||||
|
|
||||||
|
ZAIDField validates that the date is a valid birthdate and that the value
|
||||||
|
has a valid checksum. It allows spaces and dashes, and returns a plain
|
||||||
|
string of digits.
|
||||||
|
>>> from django.contrib.localflavor.za.forms import ZAIDField
|
||||||
|
>>> f = ZAIDField()
|
||||||
|
>>> f.clean('0002290001003')
|
||||||
|
'0002290001003'
|
||||||
|
>>> f.clean('000229 0001 003')
|
||||||
|
'0002290001003'
|
||||||
|
>>> f.clean('0102290001001')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid South African ID number']
|
||||||
|
>>> f.clean('811208')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid South African ID number']
|
||||||
|
>>> f.clean('0002290001004')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid South African ID number']
|
||||||
|
|
||||||
|
# ZAPostCodeField ###########################################################
|
||||||
|
>>> from django.contrib.localflavor.za.forms import ZAPostCodeField
|
||||||
|
>>> f = ZAPostCodeField()
|
||||||
|
>>> f.clean('abcd')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid South African postal code']
|
||||||
|
>>> f.clean('0000')
|
||||||
|
u'0000'
|
||||||
|
>>> f.clean(' 7530')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid South African postal code']
|
||||||
|
|
||||||
|
"""
|
@ -22,6 +22,7 @@ from localflavor.pl import tests as localflavor_pl_tests
|
|||||||
from localflavor.sk import tests as localflavor_sk_tests
|
from localflavor.sk import tests as localflavor_sk_tests
|
||||||
from localflavor.uk import tests as localflavor_uk_tests
|
from localflavor.uk import tests as localflavor_uk_tests
|
||||||
from localflavor.us import tests as localflavor_us_tests
|
from localflavor.us import tests as localflavor_us_tests
|
||||||
|
from localflavor.za import tests as localflavor_za_tests
|
||||||
from regressions import tests as regression_tests
|
from regressions import tests as regression_tests
|
||||||
from util import tests as util_tests
|
from util import tests as util_tests
|
||||||
from widgets import tests as widgets_tests
|
from widgets import tests as widgets_tests
|
||||||
@ -52,6 +53,7 @@ __test__ = {
|
|||||||
'localflavor_sk_tests': localflavor_sk_tests,
|
'localflavor_sk_tests': localflavor_sk_tests,
|
||||||
'localflavor_uk_tests': localflavor_uk_tests,
|
'localflavor_uk_tests': localflavor_uk_tests,
|
||||||
'localflavor_us_tests': localflavor_us_tests,
|
'localflavor_us_tests': localflavor_us_tests,
|
||||||
|
'localflavor_za_tests': localflavor_za_tests,
|
||||||
'regression_tests': regression_tests,
|
'regression_tests': regression_tests,
|
||||||
'formset_tests': formset_tests,
|
'formset_tests': formset_tests,
|
||||||
'media_tests': media_tests,
|
'media_tests': media_tests,
|
||||||
|
@ -43,7 +43,7 @@ u'django'
|
|||||||
Translating a string requiring no auto-escaping shouldn't change the "safe"
|
Translating a string requiring no auto-escaping shouldn't change the "safe"
|
||||||
status.
|
status.
|
||||||
|
|
||||||
>>> from django.utils.safestring import mark_safe
|
>>> from django.utils.safestring import mark_safe, SafeString
|
||||||
>>> s = mark_safe('Password')
|
>>> s = mark_safe('Password')
|
||||||
>>> type(s)
|
>>> type(s)
|
||||||
<class 'django.utils.safestring.SafeString'>
|
<class 'django.utils.safestring.SafeString'>
|
||||||
@ -51,6 +51,19 @@ status.
|
|||||||
>>> type(ugettext(s))
|
>>> type(ugettext(s))
|
||||||
<class 'django.utils.safestring.SafeUnicode'>
|
<class 'django.utils.safestring.SafeUnicode'>
|
||||||
>>> deactivate()
|
>>> deactivate()
|
||||||
|
|
||||||
|
>>> SafeString('a') + s
|
||||||
|
'aPassword'
|
||||||
|
>>> s + SafeString('a')
|
||||||
|
'Passworda'
|
||||||
|
>>> s + mark_safe('a')
|
||||||
|
'Passworda'
|
||||||
|
>>> mark_safe('a') + s
|
||||||
|
'aPassword'
|
||||||
|
>>> mark_safe('a') + mark_safe('s')
|
||||||
|
'as'
|
||||||
|
>>> print s
|
||||||
|
Password
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__test__ = {
|
__test__ = {
|
||||||
|
0
tests/regressiontests/middleware/__init__.py
Normal file
0
tests/regressiontests/middleware/__init__.py
Normal file
93
tests/regressiontests/middleware/tests.py
Normal file
93
tests/regressiontests/middleware/tests.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.middleware.common import CommonMiddleware
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
class CommonMiddlewareTest(TestCase):
|
||||||
|
def _get_request(self, path):
|
||||||
|
request = HttpRequest()
|
||||||
|
request.META = {
|
||||||
|
'SERVER_NAME': 'testserver',
|
||||||
|
'SERVER_PORT': 80,
|
||||||
|
}
|
||||||
|
request.path = "/middleware/%s" % path
|
||||||
|
return request
|
||||||
|
|
||||||
|
def test_append_slash_have_slash(self):
|
||||||
|
"""
|
||||||
|
tests that urls with slashes go unmolested
|
||||||
|
"""
|
||||||
|
settings.APPEND_SLASH = True
|
||||||
|
request = self._get_request('slash/')
|
||||||
|
self.assertEquals(CommonMiddleware().process_request(request), None)
|
||||||
|
|
||||||
|
def test_append_slash_slashless_resource(self):
|
||||||
|
"""
|
||||||
|
tests that matches to explicit slashless urls go unmolested
|
||||||
|
"""
|
||||||
|
settings.APPEND_SLASH = True
|
||||||
|
request = self._get_request('noslash')
|
||||||
|
self.assertEquals(CommonMiddleware().process_request(request), None)
|
||||||
|
|
||||||
|
def test_append_slash_slashless_unknown(self):
|
||||||
|
"""
|
||||||
|
tests that APPEND_SLASH doesn't redirect to unknown resources
|
||||||
|
"""
|
||||||
|
settings.APPEND_SLASH = True
|
||||||
|
request = self._get_request('unknown')
|
||||||
|
self.assertEquals(CommonMiddleware().process_request(request), None)
|
||||||
|
|
||||||
|
def test_append_slash_redirect(self):
|
||||||
|
"""
|
||||||
|
tests that APPEND_SLASH redirects slashless urls to a valid pattern
|
||||||
|
"""
|
||||||
|
settings.APPEND_SLASH = True
|
||||||
|
request = self._get_request('slash')
|
||||||
|
r = CommonMiddleware().process_request(request)
|
||||||
|
self.assertEquals(r.status_code, 301)
|
||||||
|
self.assertEquals(r['Location'], 'http://testserver/middleware/slash/')
|
||||||
|
|
||||||
|
def test_append_slash_no_redirect_on_POST_in_DEBUG(self):
|
||||||
|
"""
|
||||||
|
tests that while in debug mode, an exception is raised with a warning
|
||||||
|
when a failed attempt is made to POST to an url which would normally be
|
||||||
|
redirected to a slashed version
|
||||||
|
"""
|
||||||
|
settings.APPEND_SLASH = True
|
||||||
|
settings.DEBUG = True
|
||||||
|
request = self._get_request('slash')
|
||||||
|
request.method = 'POST'
|
||||||
|
self.assertRaises(
|
||||||
|
RuntimeError,
|
||||||
|
CommonMiddleware().process_request,
|
||||||
|
request)
|
||||||
|
try:
|
||||||
|
CommonMiddleware().process_request(request)
|
||||||
|
except RuntimeError, e:
|
||||||
|
self.assertTrue('end in a slash' in str(e))
|
||||||
|
|
||||||
|
def test_append_slash_disabled(self):
|
||||||
|
"""
|
||||||
|
tests disabling append slash functionality
|
||||||
|
"""
|
||||||
|
settings.APPEND_SLASH = False
|
||||||
|
request = self._get_request('slash')
|
||||||
|
self.assertEquals(CommonMiddleware().process_request(request), None)
|
||||||
|
|
||||||
|
def test_append_slash_quoted(self):
|
||||||
|
"""
|
||||||
|
tests that urls which require quoting are redirected to their slash
|
||||||
|
version ok
|
||||||
|
"""
|
||||||
|
settings.APPEND_SLASH = True
|
||||||
|
request = self._get_request('needsquoting#')
|
||||||
|
r = CommonMiddleware().process_request(request)
|
||||||
|
self.assertEquals(r.status_code, 301)
|
||||||
|
self.assertEquals(
|
||||||
|
r['Location'],
|
||||||
|
'http://testserver/middleware/needsquoting%23/')
|
||||||
|
|
7
tests/regressiontests/middleware/urls.py
Normal file
7
tests/regressiontests/middleware/urls.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django.conf.urls.defaults import patterns
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
(r'^noslash$', 'view'),
|
||||||
|
(r'^slash/$', 'view'),
|
||||||
|
(r'^needsquoting#/$', 'view'),
|
||||||
|
)
|
@ -18,26 +18,26 @@ class Bar(models.Model):
|
|||||||
return "Bar %s" % self.place.name
|
return "Bar %s" % self.place.name
|
||||||
|
|
||||||
class Whiz(models.Model):
|
class Whiz(models.Model):
|
||||||
name = models.CharField(max_length = 50)
|
name = models.CharField(max_length=50)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return "Whiz %s" % self.name
|
return "Whiz %s" % self.name
|
||||||
|
|
||||||
class Child(models.Model):
|
class Child(models.Model):
|
||||||
parent = models.OneToOneField('Base')
|
parent = models.OneToOneField('Base')
|
||||||
name = models.CharField(max_length = 50)
|
name = models.CharField(max_length=50)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return "Child %s" % self.name
|
return "Child %s" % self.name
|
||||||
|
|
||||||
class Base(models.Model):
|
class Base(models.Model):
|
||||||
name = models.CharField(max_length = 50)
|
name = models.CharField(max_length=50)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return "Base %s" % self.name
|
return "Base %s" % self.name
|
||||||
|
|
||||||
class Article(models.Model):
|
class Article(models.Model):
|
||||||
name = models.CharField(maxlength = 50)
|
name = models.CharField(max_length=50)
|
||||||
text = models.TextField()
|
text = models.TextField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
18
tests/regressiontests/templates/context.py
Normal file
18
tests/regressiontests/templates/context.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
context_tests = r"""
|
||||||
|
>>> from django.template import Context
|
||||||
|
>>> c = Context({'a': 1, 'b': 'xyzzy'})
|
||||||
|
>>> c['a']
|
||||||
|
1
|
||||||
|
>>> c.push()
|
||||||
|
{}
|
||||||
|
>>> c['a'] = 2
|
||||||
|
>>> c['a']
|
||||||
|
2
|
||||||
|
>>> c.pop()
|
||||||
|
{'a': 2}
|
||||||
|
>>> c['a']
|
||||||
|
1
|
||||||
|
"""
|
||||||
|
|
@ -18,11 +18,13 @@ from django.utils.safestring import mark_safe
|
|||||||
from django.utils.tzinfo import LocalTimezone
|
from django.utils.tzinfo import LocalTimezone
|
||||||
|
|
||||||
from unicode import unicode_tests
|
from unicode import unicode_tests
|
||||||
|
from context import context_tests
|
||||||
import filters
|
import filters
|
||||||
|
|
||||||
# Some other tests we would like to run
|
# Some other tests we would like to run
|
||||||
__test__ = {
|
__test__ = {
|
||||||
'unicode': unicode_tests,
|
'unicode': unicode_tests,
|
||||||
|
'context': context_tests,
|
||||||
}
|
}
|
||||||
|
|
||||||
#################################
|
#################################
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.template import RequestContext
|
|
||||||
|
|
||||||
def index_page(request):
|
def index_page(request):
|
||||||
"""Dummy index page"""
|
"""Dummy index page"""
|
||||||
return HttpResponse('<html><body>Dummy page</body></html>')
|
return HttpResponse('<html><body>Dummy page</body></html>')
|
||||||
|
|
||||||
|
@ -107,8 +107,7 @@ def django_tests(verbosity, interactive, test_labels):
|
|||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
)
|
)
|
||||||
if not hasattr(settings, 'SITE_ID'):
|
settings.SITE_ID = 1
|
||||||
settings.SITE_ID = 1
|
|
||||||
|
|
||||||
# Load all the ALWAYS_INSTALLED_APPS.
|
# Load all the ALWAYS_INSTALLED_APPS.
|
||||||
# (This import statement is intentionally delayed until after we
|
# (This import statement is intentionally delayed until after we
|
||||||
|
@ -14,4 +14,7 @@ urlpatterns = patterns('',
|
|||||||
|
|
||||||
# django built-in views
|
# django built-in views
|
||||||
(r'^views/', include('regressiontests.views.urls')),
|
(r'^views/', include('regressiontests.views.urls')),
|
||||||
|
|
||||||
|
# test urlconf for middleware tests
|
||||||
|
(r'^middleware/', include('regressiontests.middleware.urls')),
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user