mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
gis: Merged revisions 6672,6686-6688,6690,6693,6707-6708,6726,6730,6753,6755-6762,6764,6776-6777,6779,6782-6919 via svnmerge from trunk; reverted oracle backend base.py
due to ikelly's patch in r6905.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6920 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
5799c2e048
commit
34560a01da
8
AUTHORS
8
AUTHORS
@ -89,6 +89,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>
|
||||||
@ -132,6 +133,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
|
||||||
@ -171,6 +173,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>
|
||||||
@ -190,6 +193,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>
|
||||||
@ -204,6 +208,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Waylan Limberg <waylan@gmail.com>
|
Waylan Limberg <waylan@gmail.com>
|
||||||
limodou
|
limodou
|
||||||
Philip Lindborg <philip.lindborg@gmail.com>
|
Philip Lindborg <philip.lindborg@gmail.com>
|
||||||
|
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>
|
||||||
@ -251,6 +256,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
|
||||||
Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
|
Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
|
||||||
@ -262,6 +268,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
|
||||||
@ -281,6 +288,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.
@ -5,9 +5,9 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: django\n"
|
"Project-Id-Version: django\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2007-11-05 17:33+0100\n"
|
"POT-Creation-Date: 2007-12-02 22:26+0100\n"
|
||||||
"PO-Revision-Date: 2007-11-05 17:34+0100\n"
|
"PO-Revision-Date: 2007-12-02 22:32+0100\n"
|
||||||
"Last-Translator: Marc Garcia <marc.garcia@accopensys.com>\n"
|
"Last-Translator: Marc Fargas <telenieko@telenieko.com>\n"
|
||||||
"Language-Team: <es@li.org>\n"
|
"Language-Team: <es@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"
|
||||||
@ -194,7 +194,7 @@ msgstr "Xinés simplificat"
|
|||||||
msgid "Traditional Chinese"
|
msgid "Traditional Chinese"
|
||||||
msgstr "Xinés tradicional"
|
msgstr "Xinés tradicional"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:42
|
#: contrib/admin/filterspecs.py:44
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"<h3>By %s:</h3>\n"
|
"<h3>By %s:</h3>\n"
|
||||||
@ -203,71 +203,71 @@ msgstr ""
|
|||||||
"<h3>Per %s:</h3>\n"
|
"<h3>Per %s:</h3>\n"
|
||||||
"<ul>\n"
|
"<ul>\n"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:72 contrib/admin/filterspecs.py:90
|
#: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:92
|
||||||
#: contrib/admin/filterspecs.py:145 contrib/admin/filterspecs.py:171
|
#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Tots"
|
msgstr "Tots"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:111
|
#: contrib/admin/filterspecs.py:113
|
||||||
msgid "Any date"
|
msgid "Any date"
|
||||||
msgstr "Qualsevol data"
|
msgstr "Qualsevol data"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:112
|
#: contrib/admin/filterspecs.py:114
|
||||||
msgid "Today"
|
msgid "Today"
|
||||||
msgstr "Avui"
|
msgstr "Avui"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:115
|
#: contrib/admin/filterspecs.py:117
|
||||||
msgid "Past 7 days"
|
msgid "Past 7 days"
|
||||||
msgstr "Últims 7 dies"
|
msgstr "Últims 7 dies"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:117
|
#: contrib/admin/filterspecs.py:119
|
||||||
msgid "This month"
|
msgid "This month"
|
||||||
msgstr "Aquest mes"
|
msgstr "Aquest mes"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:119
|
#: contrib/admin/filterspecs.py:121
|
||||||
msgid "This year"
|
msgid "This year"
|
||||||
msgstr "Aquest any"
|
msgstr "Aquest any"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221
|
#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231
|
||||||
#: oldforms/__init__.py:591
|
#: oldforms/__init__.py:592
|
||||||
msgid "Yes"
|
msgid "Yes"
|
||||||
msgstr "Si"
|
msgstr "Si"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221
|
#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231
|
||||||
#: oldforms/__init__.py:591
|
#: oldforms/__init__.py:592
|
||||||
msgid "No"
|
msgid "No"
|
||||||
msgstr "No"
|
msgstr "No"
|
||||||
|
|
||||||
#: contrib/admin/filterspecs.py:152 newforms/widgets.py:221
|
#: contrib/admin/filterspecs.py:154 newforms/widgets.py:231
|
||||||
#: oldforms/__init__.py:591
|
#: oldforms/__init__.py:592
|
||||||
msgid "Unknown"
|
msgid "Unknown"
|
||||||
msgstr "Desconegut"
|
msgstr "Desconegut"
|
||||||
|
|
||||||
#: contrib/admin/models.py:17
|
#: contrib/admin/models.py:18
|
||||||
msgid "action time"
|
msgid "action time"
|
||||||
msgstr "moment de l'acció"
|
msgstr "moment de l'acció"
|
||||||
|
|
||||||
#: contrib/admin/models.py:20
|
#: contrib/admin/models.py:21
|
||||||
msgid "object id"
|
msgid "object id"
|
||||||
msgstr "id del objecte"
|
msgstr "id del objecte"
|
||||||
|
|
||||||
#: contrib/admin/models.py:21
|
#: contrib/admin/models.py:22
|
||||||
msgid "object repr"
|
msgid "object repr"
|
||||||
msgstr "'repr' de l'objecte"
|
msgstr "'repr' de l'objecte"
|
||||||
|
|
||||||
#: contrib/admin/models.py:22
|
#: contrib/admin/models.py:23
|
||||||
msgid "action flag"
|
msgid "action flag"
|
||||||
msgstr "marca de l'acció"
|
msgstr "marca de l'acció"
|
||||||
|
|
||||||
#: contrib/admin/models.py:23
|
#: contrib/admin/models.py:24
|
||||||
msgid "change message"
|
msgid "change message"
|
||||||
msgstr "missatge del canvi"
|
msgstr "missatge del canvi"
|
||||||
|
|
||||||
#: contrib/admin/models.py:26
|
#: contrib/admin/models.py:27
|
||||||
msgid "log entry"
|
msgid "log entry"
|
||||||
msgstr "entrada del registre"
|
msgstr "entrada del registre"
|
||||||
|
|
||||||
#: contrib/admin/models.py:27
|
#: contrib/admin/models.py:28
|
||||||
msgid "log entries"
|
msgid "log entries"
|
||||||
msgstr "entrades del registre"
|
msgstr "entrades del registre"
|
||||||
|
|
||||||
@ -469,7 +469,7 @@ msgid "Password:"
|
|||||||
msgstr "Contrasenya:"
|
msgstr "Contrasenya:"
|
||||||
|
|
||||||
#: contrib/admin/templates/admin/login.html:25
|
#: contrib/admin/templates/admin/login.html:25
|
||||||
#: contrib/admin/views/decorators.py:24
|
#: contrib/admin/views/decorators.py:25
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Iniciar sessió"
|
msgstr "Iniciar sessió"
|
||||||
|
|
||||||
@ -769,17 +769,17 @@ msgstr "Actualment:"
|
|||||||
msgid "Change:"
|
msgid "Change:"
|
||||||
msgstr "Modificar:"
|
msgstr "Modificar:"
|
||||||
|
|
||||||
#: contrib/admin/templatetags/admin_list.py:254
|
#: contrib/admin/templatetags/admin_list.py:257
|
||||||
msgid "All dates"
|
msgid "All dates"
|
||||||
msgstr "Totes les dates"
|
msgstr "Totes les dates"
|
||||||
|
|
||||||
#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:264
|
#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:267
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The %(name)s \"%(obj)s\" was added successfully."
|
msgid "The %(name)s \"%(obj)s\" was added successfully."
|
||||||
msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit."
|
msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit."
|
||||||
|
|
||||||
#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:268
|
#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:271
|
||||||
#: contrib/admin/views/main.py:354
|
#: contrib/admin/views/main.py:356
|
||||||
msgid "You may edit it again below."
|
msgid "You may edit it again below."
|
||||||
msgstr "Pot editar-lo de nou abaix."
|
msgstr "Pot editar-lo de nou abaix."
|
||||||
|
|
||||||
@ -796,7 +796,7 @@ msgstr "Canvi de clau exitós"
|
|||||||
msgid "Change password: %s"
|
msgid "Change password: %s"
|
||||||
msgstr "Canviar clau: %s"
|
msgstr "Canviar clau: %s"
|
||||||
|
|
||||||
#: contrib/admin/views/decorators.py:10 contrib/auth/forms.py:60
|
#: contrib/admin/views/decorators.py:11 contrib/auth/forms.py:60
|
||||||
msgid ""
|
msgid ""
|
||||||
"Please enter a correct username and password. Note that both fields are case-"
|
"Please enter a correct username and password. Note that both fields are case-"
|
||||||
"sensitive."
|
"sensitive."
|
||||||
@ -804,7 +804,7 @@ msgstr ""
|
|||||||
"Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en "
|
"Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en "
|
||||||
"compte que tots dos camps son sensibles a majúscules i minúscules."
|
"compte que tots dos camps son sensibles a majúscules i minúscules."
|
||||||
|
|
||||||
#: contrib/admin/views/decorators.py:62
|
#: contrib/admin/views/decorators.py:63
|
||||||
msgid ""
|
msgid ""
|
||||||
"Please log in again, because your session has expired. Don't worry: Your "
|
"Please log in again, because your session has expired. Don't worry: Your "
|
||||||
"submission has been saved."
|
"submission has been saved."
|
||||||
@ -812,7 +812,7 @@ msgstr ""
|
|||||||
"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es "
|
"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es "
|
||||||
"preocupi, el seu enviament està emmagatzemat."
|
"preocupi, el seu enviament està emmagatzemat."
|
||||||
|
|
||||||
#: contrib/admin/views/decorators.py:69
|
#: contrib/admin/views/decorators.py:70
|
||||||
msgid ""
|
msgid ""
|
||||||
"Looks like your browser isn't configured to accept cookies. Please enable "
|
"Looks like your browser isn't configured to accept cookies. Please enable "
|
||||||
"cookies, reload this page, and try again."
|
"cookies, reload this page, and try again."
|
||||||
@ -821,247 +821,247 @@ msgstr ""
|
|||||||
"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta "
|
"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta "
|
||||||
"pàgina i provi-ho de nou. "
|
"pàgina i provi-ho de nou. "
|
||||||
|
|
||||||
#: contrib/admin/views/decorators.py:83
|
#: contrib/admin/views/decorators.py:84
|
||||||
msgid "Usernames cannot contain the '@' character."
|
msgid "Usernames cannot contain the '@' character."
|
||||||
msgstr "Els noms d'usuari no poden contenir el caracter '@'."
|
msgstr "Els noms d'usuari no poden contenir el caracter '@'."
|
||||||
|
|
||||||
#: contrib/admin/views/decorators.py:85
|
#: contrib/admin/views/decorators.py:86
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Your e-mail address is not your username. Try '%s' instead."
|
msgid "Your e-mail address is not your username. Try '%s' instead."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas."
|
"La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas."
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:47 contrib/admin/views/doc.py:49
|
#: contrib/admin/views/doc.py:48 contrib/admin/views/doc.py:50
|
||||||
#: contrib/admin/views/doc.py:51
|
#: contrib/admin/views/doc.py:52
|
||||||
msgid "tag:"
|
msgid "tag:"
|
||||||
msgstr "etiqueta:"
|
msgstr "etiqueta:"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:78 contrib/admin/views/doc.py:80
|
#: contrib/admin/views/doc.py:79 contrib/admin/views/doc.py:81
|
||||||
#: contrib/admin/views/doc.py:82
|
#: contrib/admin/views/doc.py:83
|
||||||
msgid "filter:"
|
msgid "filter:"
|
||||||
msgstr "filtre:"
|
msgstr "filtre:"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:136 contrib/admin/views/doc.py:138
|
#: contrib/admin/views/doc.py:137 contrib/admin/views/doc.py:139
|
||||||
#: contrib/admin/views/doc.py:140
|
#: contrib/admin/views/doc.py:141
|
||||||
msgid "view:"
|
msgid "view:"
|
||||||
msgstr "vista:"
|
msgstr "vista:"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:165
|
#: contrib/admin/views/doc.py:166
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "App %r not found"
|
msgid "App %r not found"
|
||||||
msgstr "La aplicació %r no s'ha pogut trobar"
|
msgstr "La aplicació %r no s'ha pogut trobar"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:172
|
#: contrib/admin/views/doc.py:173
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Model %(name)r not found in app %(label)r"
|
msgid "Model %(name)r not found in app %(label)r"
|
||||||
msgstr "El model %(name)r no s'ha trobat en la aplicació %(label)r"
|
msgstr "El model %(name)r no s'ha trobat en la aplicació %(label)r"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:184
|
#: contrib/admin/views/doc.py:185
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "the related `%(label)s.%(type)s` object"
|
msgid "the related `%(label)s.%(type)s` object"
|
||||||
msgstr "el objecte relacionat `%(label)s.%(type)s`"
|
msgstr "el objecte relacionat `%(label)s.%(type)s`"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:184 contrib/admin/views/doc.py:206
|
#: contrib/admin/views/doc.py:185 contrib/admin/views/doc.py:207
|
||||||
#: contrib/admin/views/doc.py:220 contrib/admin/views/doc.py:225
|
#: contrib/admin/views/doc.py:221 contrib/admin/views/doc.py:226
|
||||||
msgid "model:"
|
msgid "model:"
|
||||||
msgstr "model:"
|
msgstr "model:"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:215
|
#: contrib/admin/views/doc.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "related `%(label)s.%(name)s` objects"
|
msgid "related `%(label)s.%(name)s` objects"
|
||||||
msgstr "objectes relacionats `%(label)s.%(name)s`"
|
msgstr "objectes relacionats `%(label)s.%(name)s`"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:220
|
#: contrib/admin/views/doc.py:221
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "all %s"
|
msgid "all %s"
|
||||||
msgstr "tots %s"
|
msgstr "tots %s"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:225
|
#: contrib/admin/views/doc.py:226
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "number of %s"
|
msgid "number of %s"
|
||||||
msgstr "nombre de %s"
|
msgstr "nombre de %s"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:230
|
#: contrib/admin/views/doc.py:231
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Fields on %s objects"
|
msgid "Fields on %s objects"
|
||||||
msgstr "Camps en objectes %s"
|
msgstr "Camps en objectes %s"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:292 contrib/admin/views/doc.py:303
|
#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:304
|
||||||
#: contrib/admin/views/doc.py:305 contrib/admin/views/doc.py:311
|
#: contrib/admin/views/doc.py:306 contrib/admin/views/doc.py:312
|
||||||
#: contrib/admin/views/doc.py:312 contrib/admin/views/doc.py:314
|
#: contrib/admin/views/doc.py:313 contrib/admin/views/doc.py:315
|
||||||
msgid "Integer"
|
msgid "Integer"
|
||||||
msgstr "Enter"
|
msgstr "Enter"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:293
|
#: contrib/admin/views/doc.py:294
|
||||||
msgid "Boolean (Either True or False)"
|
msgid "Boolean (Either True or False)"
|
||||||
msgstr "Booleà (Verdader o Fals)"
|
msgstr "Booleà (Verdader o Fals)"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:294 contrib/admin/views/doc.py:313
|
#: contrib/admin/views/doc.py:295 contrib/admin/views/doc.py:314
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "String (up to %(max_length)s)"
|
msgid "String (up to %(max_length)s)"
|
||||||
msgstr "Cadena (de fins a %(max_length)s)"
|
msgstr "Cadena (de fins a %(max_length)s)"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:295
|
#: contrib/admin/views/doc.py:296
|
||||||
msgid "Comma-separated integers"
|
msgid "Comma-separated integers"
|
||||||
msgstr "Enters separats per comes"
|
msgstr "Enters separats per comes"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:296
|
#: contrib/admin/views/doc.py:297
|
||||||
msgid "Date (without time)"
|
msgid "Date (without time)"
|
||||||
msgstr "Data (sense hora)"
|
msgstr "Data (sense hora)"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:297
|
#: contrib/admin/views/doc.py:298
|
||||||
msgid "Date (with time)"
|
msgid "Date (with time)"
|
||||||
msgstr "Data (amb hora)"
|
msgstr "Data (amb hora)"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:298
|
#: contrib/admin/views/doc.py:299
|
||||||
msgid "Decimal number"
|
msgid "Decimal number"
|
||||||
msgstr "Número decimal"
|
msgstr "Número decimal"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:299
|
#: contrib/admin/views/doc.py:300
|
||||||
msgid "E-mail address"
|
msgid "E-mail address"
|
||||||
msgstr "Adreça de correu electrònic"
|
msgstr "Adreça de correu electrònic"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301
|
#: contrib/admin/views/doc.py:301 contrib/admin/views/doc.py:302
|
||||||
#: contrib/admin/views/doc.py:304
|
#: contrib/admin/views/doc.py:305
|
||||||
msgid "File path"
|
msgid "File path"
|
||||||
msgstr "Ruta del fitxer"
|
msgstr "Ruta del fitxer"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:302
|
#: contrib/admin/views/doc.py:303
|
||||||
msgid "Floating point number"
|
msgid "Floating point number"
|
||||||
msgstr "Número amb punt de coma flotant"
|
msgstr "Número amb punt de coma flotant"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:306 contrib/comments/models.py:85
|
#: contrib/admin/views/doc.py:307 contrib/comments/models.py:85
|
||||||
msgid "IP address"
|
msgid "IP address"
|
||||||
msgstr "Adreça IP"
|
msgstr "Adreça IP"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:308
|
#: contrib/admin/views/doc.py:309
|
||||||
msgid "Boolean (Either True, False or None)"
|
msgid "Boolean (Either True, False or None)"
|
||||||
msgstr "Booleà (Verdader, Fals o 'None' (cap))"
|
msgstr "Booleà (Verdader, Fals o 'None' (cap))"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:309
|
#: contrib/admin/views/doc.py:310
|
||||||
msgid "Relation to parent model"
|
msgid "Relation to parent model"
|
||||||
msgstr "Relació amb el model pare"
|
msgstr "Relació amb el model pare"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:310
|
#: contrib/admin/views/doc.py:311
|
||||||
msgid "Phone number"
|
msgid "Phone number"
|
||||||
msgstr "Número de telèfon"
|
msgstr "Número de telèfon"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:315
|
#: contrib/admin/views/doc.py:316
|
||||||
msgid "Text"
|
msgid "Text"
|
||||||
msgstr "Texte"
|
msgstr "Texte"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:316
|
#: contrib/admin/views/doc.py:317
|
||||||
msgid "Time"
|
msgid "Time"
|
||||||
msgstr "Hora"
|
msgstr "Hora"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:317 contrib/flatpages/models.py:7
|
#: contrib/admin/views/doc.py:318 contrib/flatpages/models.py:7
|
||||||
msgid "URL"
|
msgid "URL"
|
||||||
msgstr "URL"
|
msgstr "URL"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:318
|
#: contrib/admin/views/doc.py:319
|
||||||
msgid "U.S. state (two uppercase letters)"
|
msgid "U.S. state (two uppercase letters)"
|
||||||
msgstr "Estat dels E.U.A. (dos lletres majúscules)"
|
msgstr "Estat dels E.U.A. (dos lletres majúscules)"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:319
|
#: contrib/admin/views/doc.py:320
|
||||||
msgid "XML text"
|
msgid "XML text"
|
||||||
msgstr "Texte XML"
|
msgstr "Texte XML"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:345
|
#: contrib/admin/views/doc.py:346
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%s does not appear to be a urlpattern object"
|
msgid "%s does not appear to be a urlpattern object"
|
||||||
msgstr "%s no sembla ser un objecte 'urlpattern'"
|
msgstr "%s no sembla ser un objecte 'urlpattern'"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:230
|
#: contrib/admin/views/main.py:233
|
||||||
msgid "Site administration"
|
msgid "Site administration"
|
||||||
msgstr "Lloc administratiu"
|
msgstr "Lloc administratiu"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:278 contrib/admin/views/main.py:363
|
#: contrib/admin/views/main.py:280 contrib/admin/views/main.py:365
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "You may add another %s below."
|
msgid "You may add another %s below."
|
||||||
msgstr "Pot afegir un altre %s a baix."
|
msgstr "Pot afegir un altre %s a baix."
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:296
|
#: contrib/admin/views/main.py:298
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Add %s"
|
msgid "Add %s"
|
||||||
msgstr "Afegir %s"
|
msgstr "Afegir %s"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:342
|
#: contrib/admin/views/main.py:344
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Added %s."
|
msgid "Added %s."
|
||||||
msgstr "Agregat %s."
|
msgstr "Agregat %s."
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:342 contrib/admin/views/main.py:344
|
#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346
|
||||||
#: contrib/admin/views/main.py:346 core/validators.py:283
|
#: contrib/admin/views/main.py:348 core/validators.py:283
|
||||||
#: db/models/manipulators.py:309
|
#: db/models/manipulators.py:309
|
||||||
msgid "and"
|
msgid "and"
|
||||||
msgstr "i"
|
msgstr "i"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:344
|
#: contrib/admin/views/main.py:346
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Changed %s."
|
msgid "Changed %s."
|
||||||
msgstr "Modificat %s."
|
msgstr "Modificat %s."
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:346
|
#: contrib/admin/views/main.py:348
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Deleted %s."
|
msgid "Deleted %s."
|
||||||
msgstr "Eliminat %s."
|
msgstr "Eliminat %s."
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:349
|
#: contrib/admin/views/main.py:351
|
||||||
msgid "No fields changed."
|
msgid "No fields changed."
|
||||||
msgstr "Cap camp canviat."
|
msgstr "Cap camp canviat."
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:352
|
#: contrib/admin/views/main.py:354
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The %(name)s \"%(obj)s\" was changed successfully."
|
msgid "The %(name)s \"%(obj)s\" was changed successfully."
|
||||||
msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s."
|
msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s."
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:360
|
#: contrib/admin/views/main.py:362
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
|
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix."
|
"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix."
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:398
|
#: contrib/admin/views/main.py:400
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Change %s"
|
msgid "Change %s"
|
||||||
msgstr "Modificar %s"
|
msgstr "Modificar %s"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:483
|
#: contrib/admin/views/main.py:487
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "One or more %(fieldname)s in %(name)s: %(obj)s"
|
msgid "One or more %(fieldname)s in %(name)s: %(obj)s"
|
||||||
msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s"
|
msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:488
|
#: contrib/admin/views/main.py:492
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "One or more %(fieldname)s in %(name)s:"
|
msgid "One or more %(fieldname)s in %(name)s:"
|
||||||
msgstr "Un o més %(fieldname)s en %(name)s:"
|
msgstr "Un o més %(fieldname)s en %(name)s:"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:520
|
#: contrib/admin/views/main.py:524
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
|
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
|
||||||
msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit."
|
msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit."
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:523
|
#: contrib/admin/views/main.py:527
|
||||||
msgid "Are you sure?"
|
msgid "Are you sure?"
|
||||||
msgstr "Està segur?"
|
msgstr "Està segur?"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:545
|
#: contrib/admin/views/main.py:549
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Change history: %s"
|
msgid "Change history: %s"
|
||||||
msgstr "Modificar històric: %s"
|
msgstr "Modificar històric: %s"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:579
|
#: contrib/admin/views/main.py:583
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Select %s"
|
msgid "Select %s"
|
||||||
msgstr "Seleccioni %s"
|
msgstr "Seleccioni %s"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:579
|
#: contrib/admin/views/main.py:583
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Select %s to change"
|
msgid "Select %s to change"
|
||||||
msgstr "Seleccioni %s per modificar"
|
msgstr "Seleccioni %s per modificar"
|
||||||
|
|
||||||
#: contrib/admin/views/main.py:780
|
#: contrib/admin/views/main.py:784
|
||||||
msgid "Database error"
|
msgid "Database error"
|
||||||
msgstr "Error de/en la base de dades"
|
msgstr "Error de/en la base de dades"
|
||||||
|
|
||||||
@ -1622,72 +1622,72 @@ msgstr "n"
|
|||||||
msgid "rd"
|
msgid "rd"
|
||||||
msgstr "r"
|
msgstr "r"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:50
|
#: contrib/humanize/templatetags/humanize.py:52
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(value).1f million"
|
msgid "%(value).1f million"
|
||||||
msgid_plural "%(value).1f million"
|
msgid_plural "%(value).1f million"
|
||||||
msgstr[0] "%(value).1f milió"
|
msgstr[0] "%(value).1f milió"
|
||||||
msgstr[1] "%(value).1f milions"
|
msgstr[1] "%(value).1f milions"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:53
|
#: contrib/humanize/templatetags/humanize.py:55
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(value).1f billion"
|
msgid "%(value).1f billion"
|
||||||
msgid_plural "%(value).1f billion"
|
msgid_plural "%(value).1f billion"
|
||||||
msgstr[0] "%(value).1f bilió"
|
msgstr[0] "%(value).1f bilió"
|
||||||
msgstr[1] "%(value).1f bilions"
|
msgstr[1] "%(value).1f bilions"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:56
|
#: contrib/humanize/templatetags/humanize.py:58
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(value).1f trillion"
|
msgid "%(value).1f trillion"
|
||||||
msgid_plural "%(value).1f trillion"
|
msgid_plural "%(value).1f trillion"
|
||||||
msgstr[0] "%(value).1f trilió"
|
msgstr[0] "%(value).1f trilió"
|
||||||
msgstr[1] "%(value).1f trilions"
|
msgstr[1] "%(value).1f trilions"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "one"
|
msgid "one"
|
||||||
msgstr "un"
|
msgstr "un"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "two"
|
msgid "two"
|
||||||
msgstr "dos"
|
msgstr "dos"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "three"
|
msgid "three"
|
||||||
msgstr "tres"
|
msgstr "tres"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "four"
|
msgid "four"
|
||||||
msgstr "cuatre"
|
msgstr "cuatre"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "five"
|
msgid "five"
|
||||||
msgstr "cinc"
|
msgstr "cinc"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "six"
|
msgid "six"
|
||||||
msgstr "sis"
|
msgstr "sis"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "seven"
|
msgid "seven"
|
||||||
msgstr "set"
|
msgstr "set"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "eight"
|
msgid "eight"
|
||||||
msgstr "vuit"
|
msgstr "vuit"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:71
|
#: contrib/humanize/templatetags/humanize.py:74
|
||||||
msgid "nine"
|
msgid "nine"
|
||||||
msgstr "nou"
|
msgstr "nou"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:90
|
#: contrib/humanize/templatetags/humanize.py:94
|
||||||
msgid "today"
|
msgid "today"
|
||||||
msgstr "avui"
|
msgstr "avui"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:92
|
#: contrib/humanize/templatetags/humanize.py:96
|
||||||
msgid "tomorrow"
|
msgid "tomorrow"
|
||||||
msgstr "demà"
|
msgstr "demà"
|
||||||
|
|
||||||
#: contrib/humanize/templatetags/humanize.py:94
|
#: contrib/humanize/templatetags/humanize.py:98
|
||||||
msgid "yesterday"
|
msgid "yesterday"
|
||||||
msgstr "ahir"
|
msgstr "ahir"
|
||||||
|
|
||||||
@ -3024,6 +3024,50 @@ msgstr ""
|
|||||||
"Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format "
|
"Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format "
|
||||||
"XXX-XX-XXXX."
|
"XXX-XX-XXXX."
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/forms.py:22
|
||||||
|
msgid "Enter a valid South African ID number"
|
||||||
|
msgstr "Introdueixi un número d'Identitat Sud Africà valid"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/forms.py:57
|
||||||
|
msgid "Enter a valid South African postal code"
|
||||||
|
msgstr "Introdueixi un codi postal Sud Africà vàlid."
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:4
|
||||||
|
msgid "Eastern Cape"
|
||||||
|
msgstr "Eastern Cape"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:5
|
||||||
|
msgid "Free State"
|
||||||
|
msgstr "Free State"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:6
|
||||||
|
msgid "Gauteng"
|
||||||
|
msgstr "Gauteng"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:7
|
||||||
|
msgid "KwaZulu-Natal"
|
||||||
|
msgstr "KwaZulu-Natal"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:8
|
||||||
|
msgid "Limpopo"
|
||||||
|
msgstr "Limpopo"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:9
|
||||||
|
msgid "Mpumalanga"
|
||||||
|
msgstr "Mpumalanga"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:10
|
||||||
|
msgid "Northern Cape"
|
||||||
|
msgstr "Northern Cape"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:11
|
||||||
|
msgid "North West"
|
||||||
|
msgstr "North West"
|
||||||
|
|
||||||
|
#: contrib/localflavor/za/za_provinces.py:12
|
||||||
|
msgid "Western Cape"
|
||||||
|
msgstr "Western Cape"
|
||||||
|
|
||||||
#: contrib/redirects/models.py:7
|
#: contrib/redirects/models.py:7
|
||||||
msgid "redirect from"
|
msgid "redirect from"
|
||||||
msgstr "redreçar des de"
|
msgstr "redreçar des de"
|
||||||
@ -3056,23 +3100,23 @@ msgstr "redreçament"
|
|||||||
msgid "redirects"
|
msgid "redirects"
|
||||||
msgstr "redreçaments"
|
msgstr "redreçaments"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:80
|
#: contrib/sessions/models.py:46
|
||||||
msgid "session key"
|
msgid "session key"
|
||||||
msgstr "clau de la sessió"
|
msgstr "clau de la sessió"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:81
|
#: contrib/sessions/models.py:47
|
||||||
msgid "session data"
|
msgid "session data"
|
||||||
msgstr "dades de la sessió"
|
msgstr "dades de la sessió"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:82
|
#: contrib/sessions/models.py:48
|
||||||
msgid "expire date"
|
msgid "expire date"
|
||||||
msgstr "data de caducitat"
|
msgstr "data de caducitat"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:87
|
#: contrib/sessions/models.py:53
|
||||||
msgid "session"
|
msgid "session"
|
||||||
msgstr "sessió"
|
msgstr "sessió"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:88
|
#: contrib/sessions/models.py:54
|
||||||
msgid "sessions"
|
msgid "sessions"
|
||||||
msgstr "sessions"
|
msgstr "sessions"
|
||||||
|
|
||||||
@ -3141,7 +3185,7 @@ msgstr "No s'admeten caracters no numèrics."
|
|||||||
msgid "This value can't be comprised solely of digits."
|
msgid "This value can't be comprised solely of digits."
|
||||||
msgstr "Aquest valor no pot contenir només dígits."
|
msgstr "Aquest valor no pot contenir només dígits."
|
||||||
|
|
||||||
#: core/validators.py:128 newforms/fields.py:157
|
#: core/validators.py:128 newforms/fields.py:151
|
||||||
msgid "Enter a whole number."
|
msgid "Enter a whole number."
|
||||||
msgstr "Introdueixi un número sencer."
|
msgstr "Introdueixi un número sencer."
|
||||||
|
|
||||||
@ -3170,17 +3214,17 @@ msgstr "Introdueixi una hora vàlida en el format HH:MM."
|
|||||||
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
|
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
|
||||||
msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM."
|
msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM."
|
||||||
|
|
||||||
#: core/validators.py:170 newforms/fields.py:408
|
#: core/validators.py:170 newforms/fields.py:402
|
||||||
msgid "Enter a valid e-mail address."
|
msgid "Enter a valid e-mail address."
|
||||||
msgstr "Introdueixi una adreça de correu vàlida."
|
msgstr "Introdueixi una adreça de correu vàlida."
|
||||||
|
|
||||||
#: core/validators.py:182 core/validators.py:474 newforms/fields.py:438
|
#: core/validators.py:182 core/validators.py:474 newforms/fields.py:432
|
||||||
#: oldforms/__init__.py:686
|
#: oldforms/__init__.py:687
|
||||||
msgid "No file was submitted. Check the encoding type on the form."
|
msgid "No file was submitted. Check the encoding type on the form."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari."
|
"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari."
|
||||||
|
|
||||||
#: core/validators.py:193 newforms/fields.py:462
|
#: core/validators.py:193 newforms/fields.py:456
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||||
"corrupted image."
|
"corrupted image."
|
||||||
@ -3431,7 +3475,7 @@ msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s."
|
|||||||
|
|
||||||
#: db/models/fields/__init__.py:161 db/models/fields/__init__.py:318
|
#: db/models/fields/__init__.py:161 db/models/fields/__init__.py:318
|
||||||
#: db/models/fields/__init__.py:735 db/models/fields/__init__.py:746
|
#: db/models/fields/__init__.py:735 db/models/fields/__init__.py:746
|
||||||
#: newforms/fields.py:45 newforms/models.py:211 oldforms/__init__.py:373
|
#: newforms/fields.py:45 oldforms/__init__.py:374
|
||||||
msgid "This field is required."
|
msgid "This field is required."
|
||||||
msgstr "Aquest camp és obligatori."
|
msgstr "Aquest camp és obligatori."
|
||||||
|
|
||||||
@ -3484,150 +3528,150 @@ msgstr[1] ""
|
|||||||
msgid "Enter a valid value."
|
msgid "Enter a valid value."
|
||||||
msgstr "Introdueixi un valor vàlid."
|
msgstr "Introdueixi un valor vàlid."
|
||||||
|
|
||||||
#: newforms/fields.py:129
|
#: newforms/fields.py:123
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
|
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Asseguris de que el valor té com a màxim %(max)d caràcters (en té %(length)"
|
"Asseguris de que el valor té com a màxim %(max)d caràcters (en té %(length)"
|
||||||
"d)."
|
"d)."
|
||||||
|
|
||||||
#: newforms/fields.py:130
|
#: newforms/fields.py:124
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
|
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Asseguris de que el valor té com a mínim %(min)d caràcters (en té %(length)"
|
"Asseguris de que el valor té com a mínim %(min)d caràcters (en té %(length)"
|
||||||
"d)."
|
"d)."
|
||||||
|
|
||||||
#: newforms/fields.py:158 newforms/fields.py:187 newforms/fields.py:216
|
#: newforms/fields.py:152 newforms/fields.py:181 newforms/fields.py:210
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure this value is less than or equal to %s."
|
msgid "Ensure this value is less than or equal to %s."
|
||||||
msgstr "Aquest valor ha de ser menor o igual a %s."
|
msgstr "Aquest valor ha de ser menor o igual a %s."
|
||||||
|
|
||||||
#: newforms/fields.py:159 newforms/fields.py:188 newforms/fields.py:217
|
#: newforms/fields.py:153 newforms/fields.py:182 newforms/fields.py:211
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure this value is greater than or equal to %s."
|
msgid "Ensure this value is greater than or equal to %s."
|
||||||
msgstr "Asseguris de que aquest valor sigui superior o igual a %s."
|
msgstr "Asseguris de que aquest valor sigui superior o igual a %s."
|
||||||
|
|
||||||
#: newforms/fields.py:186 newforms/fields.py:215
|
#: newforms/fields.py:180 newforms/fields.py:209
|
||||||
msgid "Enter a number."
|
msgid "Enter a number."
|
||||||
msgstr "Introdueixi un número."
|
msgstr "Introdueixi un número."
|
||||||
|
|
||||||
#: newforms/fields.py:218
|
#: newforms/fields.py:212
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure that there are no more than %s digits in total."
|
msgid "Ensure that there are no more than %s digits in total."
|
||||||
msgstr "Asseguris de que no hi ha més de %s dígits en total."
|
msgstr "Asseguris de que no hi ha més de %s dígits en total."
|
||||||
|
|
||||||
#: newforms/fields.py:219
|
#: newforms/fields.py:213
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure that there are no more than %s decimal places."
|
msgid "Ensure that there are no more than %s decimal places."
|
||||||
msgstr "Asseguris de que no hi ha més de %s decimals."
|
msgstr "Asseguris de que no hi ha més de %s decimals."
|
||||||
|
|
||||||
#: newforms/fields.py:220
|
#: newforms/fields.py:214
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure that there are no more than %s digits before the decimal point."
|
msgid "Ensure that there are no more than %s digits before the decimal point."
|
||||||
msgstr "Asseguris de que no hia ha més de %s dígits decimals."
|
msgstr "Asseguris de que no hia ha més de %s dígits decimals."
|
||||||
|
|
||||||
#: newforms/fields.py:268 newforms/fields.py:724
|
#: newforms/fields.py:262 newforms/fields.py:719
|
||||||
msgid "Enter a valid date."
|
msgid "Enter a valid date."
|
||||||
msgstr "Introdueixi una data vàlida."
|
msgstr "Introdueixi una data vàlida."
|
||||||
|
|
||||||
#: newforms/fields.py:301 newforms/fields.py:725
|
#: newforms/fields.py:295 newforms/fields.py:720
|
||||||
msgid "Enter a valid time."
|
msgid "Enter a valid time."
|
||||||
msgstr "Introdueixi una hora vàlida."
|
msgstr "Introdueixi una hora vàlida."
|
||||||
|
|
||||||
#: newforms/fields.py:340
|
#: newforms/fields.py:334
|
||||||
msgid "Enter a valid date/time."
|
msgid "Enter a valid date/time."
|
||||||
msgstr "Introdueixi una data/hora vàlides."
|
msgstr "Introdueixi una data/hora vàlides."
|
||||||
|
|
||||||
#: newforms/fields.py:439
|
#: newforms/fields.py:433
|
||||||
msgid "No file was submitted."
|
msgid "No file was submitted."
|
||||||
msgstr "No s'ha enviat cap fitxer."
|
msgstr "No s'ha enviat cap fitxer."
|
||||||
|
|
||||||
#: newforms/fields.py:440 oldforms/__init__.py:688
|
#: newforms/fields.py:434 oldforms/__init__.py:689
|
||||||
msgid "The submitted file is empty."
|
msgid "The submitted file is empty."
|
||||||
msgstr "El fitxer enviat està buit."
|
msgstr "El fitxer enviat està buit."
|
||||||
|
|
||||||
#: newforms/fields.py:498
|
#: newforms/fields.py:492
|
||||||
msgid "Enter a valid URL."
|
msgid "Enter a valid URL."
|
||||||
msgstr "Introdueixi una URL vàlida."
|
msgstr "Introdueixi una URL vàlida."
|
||||||
|
|
||||||
#: newforms/fields.py:499
|
#: newforms/fields.py:493
|
||||||
msgid "This URL appears to be a broken link."
|
msgid "This URL appears to be a broken link."
|
||||||
msgstr "Aquesta URL sembla ser un enllaç trencat."
|
msgstr "Aquesta URL sembla ser un enllaç trencat."
|
||||||
|
|
||||||
#: newforms/fields.py:560 newforms/models.py:194
|
#: newforms/fields.py:555 newforms/models.py:155
|
||||||
msgid "Select a valid choice. That choice is not one of the available choices."
|
msgid "Select a valid choice. That choice is not one of the available choices."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles."
|
"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles."
|
||||||
|
|
||||||
#: newforms/fields.py:599
|
#: newforms/fields.py:594
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Select a valid choice. %(value)s is not one of the available choices."
|
msgid "Select a valid choice. %(value)s is not one of the available choices."
|
||||||
msgstr "Esculli una opció vàlida. %(value)s no és una de les opcions vàlides."
|
msgstr "Esculli una opció vàlida. %(value)s no és una de les opcions vàlides."
|
||||||
|
|
||||||
#: newforms/fields.py:600 newforms/fields.py:662 newforms/models.py:215
|
#: newforms/fields.py:595 newforms/fields.py:657 newforms/models.py:215
|
||||||
msgid "Enter a list of values."
|
msgid "Enter a list of values."
|
||||||
msgstr "Introdueixi una llista de valors."
|
msgstr "Introdueixi una llista de valors."
|
||||||
|
|
||||||
#: newforms/fields.py:753
|
#: newforms/fields.py:748
|
||||||
msgid "Enter a valid IPv4 address."
|
msgid "Enter a valid IPv4 address."
|
||||||
msgstr "Introdueixi una adreça IPv4 vàlida."
|
msgstr "Introdueixi una adreça IPv4 vàlida."
|
||||||
|
|
||||||
#: newforms/models.py:221
|
#: newforms/models.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Select a valid choice. %s is not one of the available choices."
|
msgid "Select a valid choice. %s is not one of the available choices."
|
||||||
msgstr "Esculli una opció vàlida; %s' no és una de les opcions vàlides."
|
msgstr "Esculli una opció vàlida; %s' no és una de les opcions vàlides."
|
||||||
|
|
||||||
#: oldforms/__init__.py:408
|
#: oldforms/__init__.py:409
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure your text is less than %s character."
|
msgid "Ensure your text is less than %s character."
|
||||||
msgid_plural "Ensure your text is less than %s characters."
|
msgid_plural "Ensure your text is less than %s characters."
|
||||||
msgstr[0] "Asseguris de que el seu texte té menys de %s caracter."
|
msgstr[0] "Asseguris de que el seu texte té menys de %s caracter."
|
||||||
msgstr[1] "Asseguris de que el seu texte té menys de %s caracters."
|
msgstr[1] "Asseguris de que el seu texte té menys de %s caracters."
|
||||||
|
|
||||||
#: oldforms/__init__.py:413
|
#: oldforms/__init__.py:414
|
||||||
msgid "Line breaks are not allowed here."
|
msgid "Line breaks are not allowed here."
|
||||||
msgstr "No es permeten salts de línia."
|
msgstr "No es permeten salts de línia."
|
||||||
|
|
||||||
#: oldforms/__init__.py:511 oldforms/__init__.py:585 oldforms/__init__.py:624
|
#: oldforms/__init__.py:512 oldforms/__init__.py:586 oldforms/__init__.py:625
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Select a valid choice; '%(data)s' is not in %(choices)s."
|
msgid "Select a valid choice; '%(data)s' is not in %(choices)s."
|
||||||
msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s."
|
msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s."
|
||||||
|
|
||||||
#: oldforms/__init__.py:744
|
#: oldforms/__init__.py:745
|
||||||
msgid "Enter a whole number between -32,768 and 32,767."
|
msgid "Enter a whole number between -32,768 and 32,767."
|
||||||
msgstr "Introdueixi un número enter entre -32,768 i 32,767."
|
msgstr "Introdueixi un número enter entre -32,768 i 32,767."
|
||||||
|
|
||||||
#: oldforms/__init__.py:754
|
#: oldforms/__init__.py:755
|
||||||
msgid "Enter a positive number."
|
msgid "Enter a positive number."
|
||||||
msgstr "Introdueixi un número positiu."
|
msgstr "Introdueixi un número positiu."
|
||||||
|
|
||||||
#: oldforms/__init__.py:764
|
#: oldforms/__init__.py:765
|
||||||
msgid "Enter a whole number between 0 and 32,767."
|
msgid "Enter a whole number between 0 and 32,767."
|
||||||
msgstr "Introdueixi un número entre 0 i 32,767."
|
msgstr "Introdueixi un número entre 0 i 32,767."
|
||||||
|
|
||||||
#: template/defaultfilters.py:555
|
#: template/defaultfilters.py:658
|
||||||
msgid "yes,no,maybe"
|
msgid "yes,no,maybe"
|
||||||
msgstr "si,no,potser"
|
msgstr "si,no,potser"
|
||||||
|
|
||||||
#: template/defaultfilters.py:585
|
#: template/defaultfilters.py:689
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(size)d byte"
|
msgid "%(size)d byte"
|
||||||
msgid_plural "%(size)d bytes"
|
msgid_plural "%(size)d bytes"
|
||||||
msgstr[0] "%(size)d byte"
|
msgstr[0] "%(size)d byte"
|
||||||
msgstr[1] "%(size)d bytes"
|
msgstr[1] "%(size)d bytes"
|
||||||
|
|
||||||
#: template/defaultfilters.py:587
|
#: template/defaultfilters.py:691
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%.1f KB"
|
msgid "%.1f KB"
|
||||||
msgstr "%.1f KB"
|
msgstr "%.1f KB"
|
||||||
|
|
||||||
#: template/defaultfilters.py:589
|
#: template/defaultfilters.py:693
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%.1f MB"
|
msgid "%.1f MB"
|
||||||
msgstr "%.1f MB"
|
msgstr "%.1f MB"
|
||||||
|
|
||||||
#: template/defaultfilters.py:590
|
#: template/defaultfilters.py:694
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%.1f GB"
|
msgid "%.1f GB"
|
||||||
msgstr "%.1f GB"
|
msgstr "%.1f GB"
|
||||||
@ -3890,23 +3934,23 @@ msgstr "%(number)d %(type)s"
|
|||||||
msgid ", %(number)d %(type)s"
|
msgid ", %(number)d %(type)s"
|
||||||
msgstr ", %(number)d %(type)s"
|
msgstr ", %(number)d %(type)s"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:395
|
#: utils/translation/trans_real.py:399
|
||||||
msgid "DATE_FORMAT"
|
msgid "DATE_FORMAT"
|
||||||
msgstr "F j, Y"
|
msgstr "F j, Y"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:396
|
#: utils/translation/trans_real.py:400
|
||||||
msgid "DATETIME_FORMAT"
|
msgid "DATETIME_FORMAT"
|
||||||
msgstr "F j, Y, H:i"
|
msgstr "F j, Y, H:i"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:397
|
#: utils/translation/trans_real.py:401
|
||||||
msgid "TIME_FORMAT"
|
msgid "TIME_FORMAT"
|
||||||
msgstr "H:i"
|
msgstr "H:i"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:413
|
#: utils/translation/trans_real.py:417
|
||||||
msgid "YEAR_MONTH_FORMAT"
|
msgid "YEAR_MONTH_FORMAT"
|
||||||
msgstr "j de/d' F del Y"
|
msgstr "j de/d' F del Y"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:414
|
#: utils/translation/trans_real.py:418
|
||||||
msgid "MONTH_DAY_FORMAT"
|
msgid "MONTH_DAY_FORMAT"
|
||||||
msgstr "j de/d' F del Y"
|
msgstr "j de/d' F del Y"
|
||||||
|
|
||||||
@ -3924,43 +3968,3 @@ msgstr "El/La %(verbose_name)s s'ha actualtzat amb èxit."
|
|||||||
#, python-format
|
#, python-format
|
||||||
msgid "The %(verbose_name)s was deleted."
|
msgid "The %(verbose_name)s was deleted."
|
||||||
msgstr "El %(verbose_name)s s'ha eliminat."
|
msgstr "El %(verbose_name)s s'ha eliminat."
|
||||||
|
|
||||||
#~ msgid ""
|
|
||||||
#~ "This comment was posted by a user who has posted fewer than %(count)s "
|
|
||||||
#~ "comment:\n"
|
|
||||||
#~ "\n"
|
|
||||||
#~ "%(text)sThis comment was posted by a user who has posted fewer than %"
|
|
||||||
#~ "(count)s comments:\n"
|
|
||||||
#~ "\n"
|
|
||||||
#~ "%(text)s"
|
|
||||||
#~ msgstr ""
|
|
||||||
#~ "Aquest comentari el va enviar un usuari que ha enviat menys de %(count)s "
|
|
||||||
#~ "comentari:\n"
|
|
||||||
#~ "\n"
|
|
||||||
#~ "%(text)sAquest comentari el va enviar un usuari que ha enviat menys de %"
|
|
||||||
#~ "(count)s comentaris:\n"
|
|
||||||
#~ "\n"
|
|
||||||
#~ "%(text)s"
|
|
||||||
|
|
||||||
#~ msgid "AnonymousUser"
|
|
||||||
#~ msgstr "AnonymousUser"
|
|
||||||
|
|
||||||
#~ msgid ""
|
|
||||||
#~ "Please enter a valid decimal number with a whole part of at most %s digit."
|
|
||||||
#~ "Please enter a valid decimal number with a whole part of at most %s "
|
|
||||||
#~ "digits."
|
|
||||||
#~ msgstr ""
|
|
||||||
#~ "Si us plau, introdueixi un número decimal vàlid amb la part entera amb "
|
|
||||||
#~ "com a màxim %s dígit.Si us plau, introdueixi un número decimal vàlid amb "
|
|
||||||
#~ "la part entera amb com a màxim %s dígits."
|
|
||||||
|
|
||||||
#~ msgid ""
|
|
||||||
#~ "Please enter a valid decimal number with at most %s decimal place.Please "
|
|
||||||
#~ "enter a valid decimal number with at most %s decimal places."
|
|
||||||
#~ msgstr ""
|
|
||||||
#~ "Si us plau, introdueixi un número decimal vàlid amb no més de %s dígit en "
|
|
||||||
#~ "la part decimal.Si us plau, introdueixi un número decimal vàlid amb no "
|
|
||||||
#~ "més de %s dígits en la part decimal."
|
|
||||||
|
|
||||||
#~ msgid "%d milliseconds"
|
|
||||||
#~ msgstr "%d milisegons"
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ def result_headers(cl):
|
|||||||
yield {"text": header,
|
yield {"text": header,
|
||||||
"sortable": True,
|
"sortable": True,
|
||||||
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
||||||
"class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
|
"class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
|
||||||
|
|
||||||
def _boolean_icon(field_val):
|
def _boolean_icon(field_val):
|
||||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||||
|
@ -322,7 +322,7 @@ class AnonymousUser(object):
|
|||||||
id = None
|
id = None
|
||||||
username = ''
|
username = ''
|
||||||
is_staff = False
|
is_staff = False
|
||||||
is_active = True
|
is_active = False
|
||||||
is_superuser = False
|
is_superuser = False
|
||||||
_groups = EmptyManager()
|
_groups = EmptyManager()
|
||||||
_user_permissions = EmptyManager()
|
_user_permissions = EmptyManager()
|
||||||
|
@ -16,9 +16,21 @@ False
|
|||||||
>>> u2 = User.objects.create_user('testuser2', 'test2@example.com')
|
>>> u2 = User.objects.create_user('testuser2', 'test2@example.com')
|
||||||
>>> u2.has_usable_password()
|
>>> u2.has_usable_password()
|
||||||
False
|
False
|
||||||
|
|
||||||
|
>>> u.is_authenticated()
|
||||||
|
True
|
||||||
|
>>> u.is_staff
|
||||||
|
False
|
||||||
|
>>> u.is_active
|
||||||
|
True
|
||||||
|
|
||||||
>>> a = AnonymousUser()
|
>>> a = AnonymousUser()
|
||||||
|
>>> a.is_authenticated()
|
||||||
|
False
|
||||||
>>> a.is_staff
|
>>> a.is_staff
|
||||||
False
|
False
|
||||||
|
>>> a.is_active
|
||||||
|
False
|
||||||
>>> a.groups.all()
|
>>> a.groups.all()
|
||||||
[]
|
[]
|
||||||
>>> a.user_permissions.all()
|
>>> a.user_permissions.all()
|
||||||
|
@ -118,7 +118,7 @@ class GenericRelation(RelatedField, Field):
|
|||||||
return self.object_id_field_name
|
return self.object_id_field_name
|
||||||
|
|
||||||
def m2m_reverse_name(self):
|
def m2m_reverse_name(self):
|
||||||
return self.object_id_field_name
|
return self.model._meta.pk.column
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
super(GenericRelation, self).contribute_to_class(cls, name)
|
super(GenericRelation, self).contribute_to_class(cls, name)
|
||||||
|
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,13 +32,40 @@ 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:
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
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:
|
||||||
|
# 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:
|
else:
|
||||||
return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
|
return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
|
||||||
markdown.is_safe = True
|
markdown.is_safe = True
|
||||||
|
@ -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:
|
||||||
|
# Different versions of docutils return slightly different HTML
|
||||||
|
try:
|
||||||
|
# Docutils v0.4 and earlier
|
||||||
self.assertEqual(rendered, """<p>Paragraph 1</p>
|
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
|
||||||
|
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.backends.base import SessionBase
|
from django.contrib.sessions.backends.base import SessionBase
|
||||||
from django.core.exceptions import SuspiciousOperation
|
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
|
||||||
|
|
||||||
class SessionStore(SessionBase):
|
class SessionStore(SessionBase):
|
||||||
"""
|
"""
|
||||||
Implements a file based session store.
|
Implements a file based session store.
|
||||||
"""
|
"""
|
||||||
def __init__(self, session_key=None):
|
def __init__(self, session_key=None):
|
||||||
self.storage_path = settings.SESSION_FILE_PATH
|
self.storage_path = getattr(settings, "SESSION_FILE_PATH", tempfile.gettempdir())
|
||||||
|
|
||||||
|
# Make sure the storage path is valid.
|
||||||
|
if not os.path.isdir(self.storage_path):
|
||||||
|
raise ImproperlyConfigured("The session storage path %r doesn't exist. "\
|
||||||
|
"Please set your SESSION_FILE_PATH setting "\
|
||||||
|
"to an existing directory in which Django "\
|
||||||
|
"can store session data." % self.storage_path)
|
||||||
|
|
||||||
self.file_prefix = settings.SESSION_COOKIE_NAME
|
self.file_prefix = settings.SESSION_COOKIE_NAME
|
||||||
super(SessionStore, self).__init__(session_key)
|
super(SessionStore, self).__init__(session_key)
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
r"""
|
r"""
|
||||||
|
|
||||||
|
>>> from django.conf import settings
|
||||||
>>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
|
>>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
|
||||||
>>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession
|
>>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession
|
||||||
>>> from django.contrib.sessions.backends.file import SessionStore as FileSession
|
>>> from django.contrib.sessions.backends.file import SessionStore as FileSession
|
||||||
@ -39,6 +40,13 @@ True
|
|||||||
>>> file_session.exists(file_session.session_key)
|
>>> file_session.exists(file_session.session_key)
|
||||||
False
|
False
|
||||||
|
|
||||||
|
# Make sure the file backend checks for a good storage dir
|
||||||
|
>>> settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer"
|
||||||
|
>>> FileSession()
|
||||||
|
Traceback (innermost last):
|
||||||
|
...
|
||||||
|
ImproperlyConfigured: The session storage path '/if/this/directory/exists/you/have/a/weird/computer' doesn't exist. Please set your SESSION_FILE_PATH setting to an existing directory in which Django can store session data.
|
||||||
|
|
||||||
>>> cache_session = CacheSession()
|
>>> cache_session = CacheSession()
|
||||||
>>> cache_session.modified
|
>>> cache_session.modified
|
||||||
False
|
False
|
||||||
@ -66,6 +74,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
|
||||||
|
|
||||||
|
131
django/core/cache/backends/filebased.py
vendored
131
django/core/cache/backends/filebased.py
vendored
@ -1,41 +1,38 @@
|
|||||||
"File-based cache backend"
|
"File-based cache backend"
|
||||||
|
|
||||||
from django.core.cache.backends.simple import CacheClass as SimpleCacheClass
|
import md5
|
||||||
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
|
||||||
|
|
||||||
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)
|
if self.has_key(key):
|
||||||
if timeout is None:
|
return None
|
||||||
timeout = self.default_timeout
|
|
||||||
try:
|
self.set(key, value, timeout)
|
||||||
filelist = os.listdir(self._dir)
|
|
||||||
except (IOError, OSError):
|
|
||||||
self._createdir()
|
|
||||||
filelist = []
|
|
||||||
if len(filelist) > self._max_entries:
|
|
||||||
self._cull(filelist)
|
|
||||||
if os.path.basename(fname) not in filelist:
|
|
||||||
try:
|
|
||||||
f = open(fname, 'wb')
|
|
||||||
now = time.time()
|
|
||||||
pickle.dump(now + timeout, f, 2)
|
|
||||||
pickle.dump(value, f, 2)
|
|
||||||
except (IOError, OSError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
fname = self._key_to_file(key)
|
fname = self._key_to_file(key)
|
||||||
@ -45,7 +42,7 @@ class CacheClass(SimpleCacheClass):
|
|||||||
now = time.time()
|
now = time.time()
|
||||||
if exp < now:
|
if exp < now:
|
||||||
f.close()
|
f.close()
|
||||||
os.remove(fname)
|
self._delete(fname)
|
||||||
else:
|
else:
|
||||||
return pickle.load(f)
|
return pickle.load(f)
|
||||||
except (IOError, OSError, EOFError, pickle.PickleError):
|
except (IOError, OSError, EOFError, pickle.PickleError):
|
||||||
@ -54,40 +51,74 @@ class CacheClass(SimpleCacheClass):
|
|||||||
|
|
||||||
def set(self, key, value, timeout=None):
|
def set(self, key, value, timeout=None):
|
||||||
fname = self._key_to_file(key)
|
fname = self._key_to_file(key)
|
||||||
|
dirname = os.path.dirname(fname)
|
||||||
|
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
timeout = self.default_timeout
|
timeout = self.default_timeout
|
||||||
|
|
||||||
|
self._cull()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
filelist = os.listdir(self._dir)
|
if not os.path.exists(dirname):
|
||||||
except (IOError, OSError):
|
os.makedirs(dirname)
|
||||||
self._createdir()
|
|
||||||
filelist = []
|
|
||||||
if len(filelist) > self._max_entries:
|
|
||||||
self._cull(filelist)
|
|
||||||
try:
|
|
||||||
f = open(fname, 'wb')
|
f = open(fname, 'wb')
|
||||||
now = time.time()
|
now = time.time()
|
||||||
pickle.dump(now + timeout, f, 2)
|
pickle.dump(now + timeout, f, pickle.HIGHEST_PROTOCOL)
|
||||||
pickle.dump(value, f, 2)
|
pickle.dump(value, f, pickle.HIGHEST_PROTOCOL)
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def delete(self, key):
|
def delete(self, key):
|
||||||
try:
|
try:
|
||||||
os.remove(self._key_to_file(key))
|
self._delete(self._key_to_file(key))
|
||||||
|
except (IOError, OSError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _delete(self, fname):
|
||||||
|
os.remove(fname)
|
||||||
|
try:
|
||||||
|
# Remove the 2 subdirs if they're empty
|
||||||
|
dirname = os.path.dirname(fname)
|
||||||
|
os.rmdir(dirname)
|
||||||
|
os.rmdir(os.path.dirname(dirname))
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def has_key(self, key):
|
def has_key(self, key):
|
||||||
return os.path.exists(self._key_to_file(key))
|
fname = self._key_to_file(key)
|
||||||
|
try:
|
||||||
|
f = open(fname, 'rb')
|
||||||
|
exp = pickle.load(f)
|
||||||
|
now = time.time()
|
||||||
|
if exp < now:
|
||||||
|
f.close()
|
||||||
|
self._delete(fname)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
except (IOError, OSError, EOFError, pickle.PickleError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _cull(self):
|
||||||
|
if int(self._num_entries) < self._max_entries:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
filelist = os.listdir(self._dir)
|
||||||
|
except (IOError, OSError):
|
||||||
|
return
|
||||||
|
|
||||||
def _cull(self, filelist):
|
|
||||||
if self._cull_frequency == 0:
|
if self._cull_frequency == 0:
|
||||||
doomed = filelist
|
doomed = filelist
|
||||||
else:
|
else:
|
||||||
doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0]
|
doomed = [os.path.join(self._dir, k) for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0]
|
||||||
for fname in doomed:
|
|
||||||
|
for topdir in doomed:
|
||||||
try:
|
try:
|
||||||
os.remove(os.path.join(self._dir, fname))
|
for root, _, files in os.walk(topdir):
|
||||||
|
for f in files:
|
||||||
|
self._delete(os.path.join(root, f))
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -98,4 +129,22 @@ class CacheClass(SimpleCacheClass):
|
|||||||
raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir
|
raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir
|
||||||
|
|
||||||
def _key_to_file(self, key):
|
def _key_to_file(self, key):
|
||||||
return os.path.join(self._dir, urlquote_plus(key))
|
"""
|
||||||
|
Convert the filename into an md5 string. We'll turn the first couple
|
||||||
|
bits of the path into directory prefixes to be nice to filesystems
|
||||||
|
that have problems with large numbers of files in a directory.
|
||||||
|
|
||||||
|
Thus, a cache key of "foo" gets turnned into a file named
|
||||||
|
``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
|
||||||
|
"""
|
||||||
|
path = md5.new(key.encode('utf-8')).hexdigest()
|
||||||
|
path = os.path.join(path[:2], path[2:4], path[4:])
|
||||||
|
return os.path.join(self._dir, path)
|
||||||
|
|
||||||
|
def _get_num_entries(self):
|
||||||
|
count = 0
|
||||||
|
for _,_,files in os.walk(self._dir):
|
||||||
|
count += len(files)
|
||||||
|
return count
|
||||||
|
_num_entries = property(_get_num_entries)
|
||||||
|
|
||||||
|
85
django/core/cache/backends/locmem.py
vendored
85
django/core/cache/backends/locmem.py
vendored
@ -6,43 +6,54 @@ 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):
|
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.
|
|
||||||
try:
|
try:
|
||||||
|
exp = self._expire_info.get(key)
|
||||||
|
if exp is None or exp <= time.time():
|
||||||
try:
|
try:
|
||||||
super(CacheClass, self).add(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 get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
should_delete = False
|
|
||||||
self._lock.reader_enters()
|
self._lock.reader_enters()
|
||||||
try:
|
try:
|
||||||
now = time.time()
|
|
||||||
exp = self._expire_info.get(key)
|
exp = self._expire_info.get(key)
|
||||||
if exp is None:
|
if exp is None:
|
||||||
return default
|
return default
|
||||||
elif exp < now:
|
elif exp > time.time():
|
||||||
should_delete = True
|
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
return pickle.loads(self._cache[key])
|
return pickle.loads(self._cache[key])
|
||||||
except pickle.PickleError:
|
except pickle.PickleError:
|
||||||
return default
|
return default
|
||||||
finally:
|
finally:
|
||||||
self._lock.reader_leaves()
|
self._lock.reader_leaves()
|
||||||
if should_delete:
|
|
||||||
self._lock.writer_enters()
|
self._lock.writer_enters()
|
||||||
try:
|
try:
|
||||||
del self._cache[key]
|
del self._cache[key]
|
||||||
@ -51,20 +62,66 @@ 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):
|
||||||
|
self._lock.reader_enters()
|
||||||
|
try:
|
||||||
|
exp = self._expire_info.get(key)
|
||||||
|
if exp is None:
|
||||||
|
return False
|
||||||
|
elif exp > time.time():
|
||||||
|
return True
|
||||||
|
finally:
|
||||||
|
self._lock.reader_leaves()
|
||||||
|
|
||||||
|
self._lock.writer_enters()
|
||||||
|
try:
|
||||||
|
del self._cache[key]
|
||||||
|
del self._expire_info[key]
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
self._lock.writer_leaves()
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
@ -10,7 +10,7 @@ from django.core.management.base import BaseCommand, CommandError, handle_defaul
|
|||||||
get_version = django.get_version
|
get_version = django.get_version
|
||||||
|
|
||||||
# A cache of loaded commands, so that call_command
|
# A cache of loaded commands, so that call_command
|
||||||
# doesn't have to reload every time it is called
|
# doesn't have to reload every time it's called.
|
||||||
_commands = None
|
_commands = None
|
||||||
|
|
||||||
def find_commands(management_dir):
|
def find_commands(management_dir):
|
||||||
@ -29,8 +29,8 @@ def find_commands(management_dir):
|
|||||||
|
|
||||||
def find_management_module(app_name):
|
def find_management_module(app_name):
|
||||||
"""
|
"""
|
||||||
Determines the path to the management module for the application named,
|
Determines the path to the management module for the given app_name,
|
||||||
without acutally importing the application or the management module.
|
without actually importing the application or the management module.
|
||||||
|
|
||||||
Raises ImportError if the management module cannot be found for any reason.
|
Raises ImportError if the management module cannot be found for any reason.
|
||||||
"""
|
"""
|
||||||
@ -46,19 +46,19 @@ def find_management_module(app_name):
|
|||||||
def load_command_class(app_name, name):
|
def load_command_class(app_name, name):
|
||||||
"""
|
"""
|
||||||
Given a command name and an application name, returns the Command
|
Given a command name and an application name, returns the Command
|
||||||
class instance. All errors raised by the importation process
|
class instance. All errors raised by the import process
|
||||||
(ImportError, AttributeError) are allowed to propagate.
|
(ImportError, AttributeError) are allowed to propagate.
|
||||||
"""
|
"""
|
||||||
return getattr(__import__('%s.management.commands.%s' % (app_name, name),
|
return getattr(__import__('%s.management.commands.%s' % (app_name, name),
|
||||||
{}, {}, ['Command']), 'Command')()
|
{}, {}, ['Command']), 'Command')()
|
||||||
|
|
||||||
def get_commands():
|
def get_commands(load_user_commands=True, project_directory=None):
|
||||||
"""
|
"""
|
||||||
Returns a dictionary of commands against the application in which
|
Returns a dictionary mapping command names to their callback applications.
|
||||||
those commands can be found. This works by looking for a
|
|
||||||
management.commands package in django.core, and in each installed
|
This works by looking for a management.commands package in django.core, and
|
||||||
application -- if a commands package exists, all commands in that
|
in each installed application -- if a commands package exists, all commands
|
||||||
package are registered.
|
in that package are registered.
|
||||||
|
|
||||||
Core commands are always included. If a settings module has been
|
Core commands are always included. If a settings module has been
|
||||||
specified, user-defined commands will also be included, the
|
specified, user-defined commands will also be included, the
|
||||||
@ -73,34 +73,22 @@ def get_commands():
|
|||||||
startapp command), the instantiated module can be placed in the
|
startapp command), the instantiated module can be placed in the
|
||||||
dictionary in place of the application name.
|
dictionary in place of the application name.
|
||||||
|
|
||||||
The dictionary is cached on the first call, and reused on subsequent
|
The dictionary is cached on the first call and reused on subsequent
|
||||||
calls.
|
calls.
|
||||||
"""
|
"""
|
||||||
global _commands
|
global _commands
|
||||||
if _commands is None:
|
if _commands is None:
|
||||||
_commands = dict([(name, 'django.core')
|
_commands = dict([(name, 'django.core') for name in find_commands(__path__[0])])
|
||||||
for name in find_commands(__path__[0])])
|
|
||||||
# Get commands from all installed apps.
|
|
||||||
try:
|
|
||||||
from django.conf import settings
|
|
||||||
apps = settings.INSTALLED_APPS
|
|
||||||
except (AttributeError, EnvironmentError):
|
|
||||||
apps = []
|
|
||||||
|
|
||||||
for app_name in apps:
|
if load_user_commands:
|
||||||
|
# Get commands from all installed apps.
|
||||||
|
from django.conf import settings
|
||||||
|
for app_name in settings.INSTALLED_APPS:
|
||||||
try:
|
try:
|
||||||
path = find_management_module(app_name)
|
path = find_management_module(app_name)
|
||||||
_commands.update(dict([(name, app_name)
|
_commands.update(dict([(name, app_name) for name in find_commands(path)]))
|
||||||
for name in find_commands(path)]))
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass # No management module - ignore this app
|
pass # No management module -- ignore this app.
|
||||||
|
|
||||||
# Try to determine the project directory
|
|
||||||
try:
|
|
||||||
from django.conf import settings
|
|
||||||
project_directory = setup_environ(__import__(settings.SETTINGS_MODULE))
|
|
||||||
except (AttributeError, EnvironmentError, ImportError):
|
|
||||||
project_directory = None
|
|
||||||
|
|
||||||
if project_directory:
|
if project_directory:
|
||||||
# Remove the "startproject" command from self.commands, because
|
# Remove the "startproject" command from self.commands, because
|
||||||
@ -157,18 +145,18 @@ class ManagementUtility(object):
|
|||||||
def __init__(self, argv=None):
|
def __init__(self, argv=None):
|
||||||
self.argv = argv or sys.argv[:]
|
self.argv = argv or sys.argv[:]
|
||||||
self.prog_name = os.path.basename(self.argv[0])
|
self.prog_name = os.path.basename(self.argv[0])
|
||||||
|
self.project_directory = None
|
||||||
|
self.user_commands = False
|
||||||
|
|
||||||
def main_help_text(self):
|
def main_help_text(self):
|
||||||
"""
|
"""
|
||||||
Returns the script's main help text, as a string.
|
Returns the script's main help text, as a string.
|
||||||
"""
|
"""
|
||||||
usage = ['%s <subcommand> [options] [args]' % self.prog_name]
|
usage = ['%s <subcommand> [options] [args]' % self.prog_name]
|
||||||
usage.append('Django command line tool,'
|
usage.append('Django command line tool, version %s' % django.get_version())
|
||||||
' version %s' % django.get_version())
|
usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name)
|
||||||
usage.append("Type '%s help <subcommand>' for help on a specific"
|
|
||||||
" subcommand." % self.prog_name)
|
|
||||||
usage.append('Available subcommands:')
|
usage.append('Available subcommands:')
|
||||||
commands = get_commands().keys()
|
commands = get_commands(self.user_commands, self.project_directory).keys()
|
||||||
commands.sort()
|
commands.sort()
|
||||||
for cmd in commands:
|
for cmd in commands:
|
||||||
usage.append(' %s' % cmd)
|
usage.append(' %s' % cmd)
|
||||||
@ -178,18 +166,18 @@ class ManagementUtility(object):
|
|||||||
"""
|
"""
|
||||||
Tries to fetch the given subcommand, printing a message with the
|
Tries to fetch the given subcommand, printing a message with the
|
||||||
appropriate command called from the command line (usually
|
appropriate command called from the command line (usually
|
||||||
django-admin.py or manage.py) if it can't be found.
|
"django-admin.py" or "manage.py") if it can't be found.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
app_name = get_commands()[subcommand]
|
app_name = get_commands(self.user_commands, self.project_directory)[subcommand]
|
||||||
if isinstance(app_name, BaseCommand):
|
if isinstance(app_name, BaseCommand):
|
||||||
# If the command is already loaded, use it directly.
|
# If the command is already loaded, use it directly.
|
||||||
klass = app_name
|
klass = app_name
|
||||||
else:
|
else:
|
||||||
klass = load_command_class(app_name, subcommand)
|
klass = load_command_class(app_name, subcommand)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
sys.stderr.write("Unknown command: %r\nType '%s help' for"
|
sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
|
||||||
" usage.\n" % (subcommand, self.prog_name))
|
(subcommand, self.prog_name))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return klass
|
return klass
|
||||||
|
|
||||||
@ -201,8 +189,7 @@ class ManagementUtility(object):
|
|||||||
# Preprocess options to extract --settings and --pythonpath.
|
# Preprocess options to extract --settings and --pythonpath.
|
||||||
# These options could affect the commands that are available, so they
|
# These options could affect the commands that are available, so they
|
||||||
# must be processed early.
|
# must be processed early.
|
||||||
parser = LaxOptionParser(version=get_version(),
|
parser = LaxOptionParser(version=get_version(), option_list=BaseCommand.option_list)
|
||||||
option_list=BaseCommand.option_list)
|
|
||||||
try:
|
try:
|
||||||
options, args = parser.parse_args(self.argv)
|
options, args = parser.parse_args(self.argv)
|
||||||
handle_default_options(options)
|
handle_default_options(options)
|
||||||
@ -242,6 +229,8 @@ class ProjectManagementUtility(ManagementUtility):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, argv, project_directory):
|
def __init__(self, argv, project_directory):
|
||||||
super(ProjectManagementUtility, self).__init__(argv)
|
super(ProjectManagementUtility, self).__init__(argv)
|
||||||
|
self.project_directory = project_directory
|
||||||
|
self.user_commands = True
|
||||||
|
|
||||||
def setup_environ(settings_mod):
|
def setup_environ(settings_mod):
|
||||||
"""
|
"""
|
||||||
@ -254,6 +243,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))
|
||||||
@ -261,8 +252,7 @@ def setup_environ(settings_mod):
|
|||||||
sys.path.pop()
|
sys.path.pop()
|
||||||
|
|
||||||
# Set DJANGO_SETTINGS_MODULE appropriately.
|
# Set DJANGO_SETTINGS_MODULE appropriately.
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name,
|
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
|
||||||
settings_name)
|
|
||||||
return project_directory
|
return project_directory
|
||||||
|
|
||||||
def execute_from_command_line(argv=None):
|
def execute_from_command_line(argv=None):
|
||||||
|
@ -58,7 +58,7 @@ class Command(BaseCommand):
|
|||||||
else:
|
else:
|
||||||
formats = []
|
formats = []
|
||||||
|
|
||||||
if verbosity > 0:
|
if verbosity > 2:
|
||||||
if formats:
|
if formats:
|
||||||
print "Loading '%s' fixtures..." % fixture_name
|
print "Loading '%s' fixtures..." % fixture_name
|
||||||
else:
|
else:
|
||||||
@ -106,7 +106,7 @@ class Command(BaseCommand):
|
|||||||
return
|
return
|
||||||
fixture.close()
|
fixture.close()
|
||||||
except:
|
except:
|
||||||
if verbosity > 1:
|
if verbosity > 2:
|
||||||
print "No %s fixture '%s' in %s." % \
|
print "No %s fixture '%s' in %s." % \
|
||||||
(format, fixture_name, humanize(fixture_dir))
|
(format, fixture_name, humanize(fixture_dir))
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ class Command(BaseCommand):
|
|||||||
transaction.leave_transaction_management()
|
transaction.leave_transaction_management()
|
||||||
|
|
||||||
if count[0] == 0:
|
if count[0] == 0:
|
||||||
if verbosity > 0:
|
if verbosity > 2:
|
||||||
print "No fixtures found."
|
print "No fixtures found."
|
||||||
else:
|
else:
|
||||||
if verbosity > 0:
|
if verbosity > 0:
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -72,10 +72,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:
|
||||||
@ -103,10 +107,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.
|
||||||
|
@ -5,6 +5,7 @@ Requires PyYaml (http://pyyaml.org/), but that's checked for in __init__.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
from django.db import models
|
||||||
from django.core.serializers.python import Serializer as PythonSerializer
|
from django.core.serializers.python import Serializer as PythonSerializer
|
||||||
from django.core.serializers.python import Deserializer as PythonDeserializer
|
from django.core.serializers.python import Deserializer as PythonDeserializer
|
||||||
try:
|
try:
|
||||||
@ -17,10 +18,23 @@ class Serializer(PythonSerializer):
|
|||||||
"""
|
"""
|
||||||
Convert a queryset to YAML.
|
Convert a queryset to YAML.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def handle_field(self, obj, field):
|
||||||
|
# A nasty special case: base YAML doesn't support serialization of time
|
||||||
|
# types (as opposed to dates or datetimes, which it does support). Since
|
||||||
|
# we want to use the "safe" serializer for better interoperability, we
|
||||||
|
# need to do something with those pesky times. Converting 'em to strings
|
||||||
|
# isn't perfect, but it's better than a "!!python/time" type which would
|
||||||
|
# halt deserialization under any other language.
|
||||||
|
if isinstance(field, models.TimeField) and getattr(obj, field.name) is not None:
|
||||||
|
self._current[field.name] = str(getattr(obj, field.name))
|
||||||
|
else:
|
||||||
|
super(Serializer, self).handle_field(obj, field)
|
||||||
|
|
||||||
def end_serialization(self):
|
def end_serialization(self):
|
||||||
self.options.pop('stream', None)
|
self.options.pop('stream', None)
|
||||||
self.options.pop('fields', None)
|
self.options.pop('fields', None)
|
||||||
yaml.dump(self.objects, self.stream, **self.options)
|
yaml.safe_dump(self.objects, self.stream, **self.options)
|
||||||
|
|
||||||
def getvalue(self):
|
def getvalue(self):
|
||||||
return self.stream.getvalue()
|
return self.stream.getvalue()
|
||||||
|
@ -34,8 +34,8 @@ except ImportError, e:
|
|||||||
raise # If there's some other error, this must be an error in Django itself.
|
raise # If there's some other error, this must be an error in Django itself.
|
||||||
|
|
||||||
def _import_database_module(import_path='', module_name=''):
|
def _import_database_module(import_path='', module_name=''):
|
||||||
"""Lazyily import a database module when requested."""
|
"""Lazily import a database module when requested."""
|
||||||
return __import__('%s%s.%s' % (_import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
|
return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
|
||||||
|
|
||||||
# We don't want to import the introspect/creation modules unless
|
# We don't want to import the introspect/creation modules unless
|
||||||
# someone asks for 'em, so lazily load them on demmand.
|
# someone asks for 'em, so lazily load them on demmand.
|
||||||
|
@ -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):
|
||||||
@ -445,24 +446,31 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
|||||||
charset = 'utf-8'
|
charset = 'utf-8'
|
||||||
|
|
||||||
def _format_params(self, params):
|
def _format_params(self, params):
|
||||||
sz_kwargs = {}
|
|
||||||
if isinstance(params, dict):
|
if isinstance(params, dict):
|
||||||
result = {}
|
result = {}
|
||||||
charset = self.charset
|
charset = self.charset
|
||||||
for key, value in params.items():
|
for key, value in params.items():
|
||||||
result[smart_str(key, charset)] = smart_str(value, charset)
|
result[smart_str(key, charset)] = smart_str(value, charset)
|
||||||
if hasattr(value, 'oracle_type'): sz_kwargs[key] = value.oracle_type()
|
|
||||||
else:
|
|
||||||
result = {}
|
|
||||||
for i in xrange(len(params)):
|
|
||||||
key = 'arg%d' % i
|
|
||||||
result[key] = smart_str(params[i], self.charset, True)
|
|
||||||
if hasattr(params[i], 'oracle_type'): sz_kwargs[key] = params[i].oracle_type()
|
|
||||||
|
|
||||||
# If any of the parameters had an `oracle_type` method, then we set
|
|
||||||
# the inputsizes for those parameters using the returned type
|
|
||||||
if sz_kwargs: self.setinputsizes(**sz_kwargs)
|
|
||||||
return result
|
return result
|
||||||
|
else:
|
||||||
|
return tuple([smart_str(p, self.charset, True) for p in params])
|
||||||
|
|
||||||
|
def _guess_input_sizes(self, params_list):
|
||||||
|
# Mark any string parameter greater than 4000 characters as an NCLOB.
|
||||||
|
if isinstance(params_list[0], dict):
|
||||||
|
sizes = {}
|
||||||
|
iterators = [params.iteritems() for params in params_list]
|
||||||
|
else:
|
||||||
|
sizes = [None] * len(params_list[0])
|
||||||
|
iterators = [enumerate(params) for params in params_list]
|
||||||
|
for iterator in iterators:
|
||||||
|
for key, value in iterator:
|
||||||
|
if isinstance(value, basestring) and len(value) > 4000:
|
||||||
|
sizes[key] = Database.NCLOB
|
||||||
|
if isinstance(sizes, dict):
|
||||||
|
self.setinputsizes(**sizes)
|
||||||
|
else:
|
||||||
|
self.setinputsizes(*sizes)
|
||||||
|
|
||||||
def execute(self, query, params=None):
|
def execute(self, query, params=None):
|
||||||
if params is None:
|
if params is None:
|
||||||
@ -477,6 +485,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
|||||||
if query.endswith(';') or query.endswith('/'):
|
if query.endswith(';') or query.endswith('/'):
|
||||||
query = query[:-1]
|
query = query[:-1]
|
||||||
query = smart_str(query, self.charset) % tuple(args)
|
query = smart_str(query, self.charset) % tuple(args)
|
||||||
|
self._guess_input_sizes([params])
|
||||||
return Database.Cursor.execute(self, query, params)
|
return Database.Cursor.execute(self, query, params)
|
||||||
|
|
||||||
def executemany(self, query, params=None):
|
def executemany(self, query, params=None):
|
||||||
@ -493,6 +502,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
|||||||
query = query[:-1]
|
query = query[:-1]
|
||||||
query = smart_str(query, self.charset) % tuple(args)
|
query = smart_str(query, self.charset) % tuple(args)
|
||||||
new_param_list = [self._format_params(i) for i in params]
|
new_param_list = [self._format_params(i) for i in params]
|
||||||
|
self._guess_input_sizes(new_param_list)
|
||||||
return Database.Cursor.executemany(self, query, new_param_list)
|
return Database.Cursor.executemany(self, query, new_param_list)
|
||||||
|
|
||||||
def fetchone(self):
|
def fetchone(self):
|
||||||
|
@ -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:
|
||||||
|
@ -104,7 +104,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
|
||||||
|
@ -29,7 +29,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
|
||||||
@ -152,7 +152,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
|
||||||
@ -186,7 +186,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):
|
||||||
|
@ -331,7 +331,7 @@ class HttpResponse(object):
|
|||||||
chunk = self._iterator.next()
|
chunk = self._iterator.next()
|
||||||
if isinstance(chunk, unicode):
|
if isinstance(chunk, unicode):
|
||||||
chunk = chunk.encode(self._charset)
|
chunk = chunk.encode(self._charset)
|
||||||
return chunk
|
return str(chunk)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if hasattr(self._container, 'close'):
|
if hasattr(self._container, 'close'):
|
||||||
|
@ -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,22 +40,43 @@ 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
|
||||||
|
if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
|
||||||
|
try:
|
||||||
|
urlresolvers.resolve(request.path)
|
||||||
|
except urlresolvers.Resolver404:
|
||||||
new_url[1] = new_url[1] + '/'
|
new_url[1] = new_url[1] + '/'
|
||||||
if settings.DEBUG and request.method == 'POST':
|
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])
|
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
|
||||||
|
try:
|
||||||
|
urlresolvers.resolve(new_url[1])
|
||||||
|
except urlresolvers.Resolver404:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
if new_url[0]:
|
if new_url[0]:
|
||||||
newurl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', new_url[0], urlquote(new_url[1]))
|
newurl = "%s://%s%s" % (
|
||||||
|
request.is_secure() and 'https' or 'http',
|
||||||
|
new_url[0], urlquote(new_url[1]))
|
||||||
else:
|
else:
|
||||||
newurl = urlquote(new_url[1])
|
newurl = urlquote(new_url[1])
|
||||||
if request.GET:
|
if request.GET:
|
||||||
|
@ -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/)'
|
||||||
|
|
||||||
@ -539,8 +533,8 @@ class BooleanField(Field):
|
|||||||
"""Returns a Python boolean object."""
|
"""Returns a Python boolean object."""
|
||||||
super(BooleanField, self).clean(value)
|
super(BooleanField, self).clean(value)
|
||||||
# Explicitly check for the string 'False', which is what a hidden field
|
# Explicitly check for the string 'False', which is what a hidden field
|
||||||
# will submit for False (since bool("True") == True we don't need to
|
# will submit for False. Because bool("True") == True, we don't need to
|
||||||
# handle that explicitly).
|
# handle that explicitly.
|
||||||
if value == 'False':
|
if value == 'False':
|
||||||
return False
|
return False
|
||||||
return bool(value)
|
return bool(value)
|
||||||
|
@ -3,16 +3,20 @@ 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, EMPTY_VALUES
|
from fields import Field, ChoiceField, EMPTY_VALUES
|
||||||
from widgets import Select, SelectMultiple, MultipleHiddenInput
|
from widgets import Select, SelectMultiple, 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',
|
||||||
'ModelChoiceField', 'ModelMultipleChoiceField'
|
'ModelChoiceField', 'ModelMultipleChoiceField'
|
||||||
)
|
)
|
||||||
@ -80,6 +84,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:
|
||||||
@ -107,6 +114,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 = []
|
||||||
@ -132,6 +142,160 @@ 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 different Meta model than 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 and base_model is not opts.model:
|
||||||
|
raise ImproperlyConfigured('%s defines a different model than its parent.' % 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, data=None, files=None, auto_id='id_%s', prefix=None,
|
||||||
|
initial=None, error_class=ErrorList, label_suffix=':', instance=None):
|
||||||
|
opts = self._meta
|
||||||
|
if instance is None:
|
||||||
|
# if we didn't get an instance, instantiate a new one
|
||||||
|
self.instance = opts.model()
|
||||||
|
object_data = {}
|
||||||
|
else:
|
||||||
|
self.instance = instance
|
||||||
|
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
|
||||||
@ -142,7 +306,7 @@ class QuerySetIterator(object):
|
|||||||
if self.empty_label is not None:
|
if self.empty_label is not None:
|
||||||
yield (u"", self.empty_label)
|
yield (u"", self.empty_label)
|
||||||
for obj in self.queryset:
|
for obj in self.queryset:
|
||||||
yield (obj._get_pk_val(), smart_unicode(obj))
|
yield (obj.pk, smart_unicode(obj))
|
||||||
# Clear the QuerySet cache if required.
|
# Clear the QuerySet cache if required.
|
||||||
if not self.cache_choices:
|
if not self.cache_choices:
|
||||||
self.queryset._result_cache = None
|
self.queryset._result_cache = None
|
||||||
|
@ -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.
|
||||||
|
@ -43,7 +43,11 @@ def stringfilter(func):
|
|||||||
|
|
||||||
|
|
||||||
def addslashes(value):
|
def addslashes(value):
|
||||||
"""Adds slashes - useful for passing strings to JavaScript, for example."""
|
"""
|
||||||
|
Adds slashes before quotes. Useful for escaping strings in CSV, for
|
||||||
|
example. Less useful for escaping JavaScript; use the ``escapejs``
|
||||||
|
filter instead.
|
||||||
|
"""
|
||||||
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
||||||
addslashes.is_safe = True
|
addslashes.is_safe = True
|
||||||
addslashes = stringfilter(addslashes)
|
addslashes = stringfilter(addslashes)
|
||||||
@ -54,6 +58,25 @@ def capfirst(value):
|
|||||||
capfirst.is_safe=True
|
capfirst.is_safe=True
|
||||||
capfirst = stringfilter(capfirst)
|
capfirst = stringfilter(capfirst)
|
||||||
|
|
||||||
|
_js_escapes = (
|
||||||
|
('\\', '\\\\'),
|
||||||
|
('"', '\\"'),
|
||||||
|
("'", "\\'"),
|
||||||
|
('\n', '\\n'),
|
||||||
|
('\r', '\\r'),
|
||||||
|
('\b', '\\b'),
|
||||||
|
('\f', '\\f'),
|
||||||
|
('\t', '\\t'),
|
||||||
|
('\v', '\\v'),
|
||||||
|
('</', '<\\/'),
|
||||||
|
)
|
||||||
|
def escapejs(value):
|
||||||
|
"""Backslash-escapes characters for use in JavaScript strings."""
|
||||||
|
for bad, good in _js_escapes:
|
||||||
|
value = value.replace(bad, good)
|
||||||
|
return value
|
||||||
|
escapejs = stringfilter(escapejs)
|
||||||
|
|
||||||
def fix_ampersands(value):
|
def fix_ampersands(value):
|
||||||
"""Replaces ampersands with ``&`` entities."""
|
"""Replaces ampersands with ``&`` entities."""
|
||||||
from django.utils.html import fix_ampersands
|
from django.utils.html import fix_ampersands
|
||||||
@ -766,6 +789,7 @@ register.filter(dictsort)
|
|||||||
register.filter(dictsortreversed)
|
register.filter(dictsortreversed)
|
||||||
register.filter(divisibleby)
|
register.filter(divisibleby)
|
||||||
register.filter(escape)
|
register.filter(escape)
|
||||||
|
register.filter(escapejs)
|
||||||
register.filter(filesizeformat)
|
register.filter(filesizeformat)
|
||||||
register.filter(first)
|
register.filter(first)
|
||||||
register.filter(fix_ampersands)
|
register.filter(fix_ampersands)
|
||||||
|
@ -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):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"HTML utilities suitable for global use."
|
"""HTML utilities suitable for global use."""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
@ -8,11 +8,11 @@ from django.utils.encoding import force_unicode
|
|||||||
from django.utils.functional import allow_lazy
|
from django.utils.functional import allow_lazy
|
||||||
from django.utils.http import urlquote
|
from django.utils.http import urlquote
|
||||||
|
|
||||||
# Configuration for urlize() function
|
# Configuration for urlize() function.
|
||||||
LEADING_PUNCTUATION = ['(', '<', '<']
|
LEADING_PUNCTUATION = ['(', '<', '<']
|
||||||
TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '>']
|
TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '>']
|
||||||
|
|
||||||
# list of possible strings used for bullets in bulleted lists
|
# List of possible strings used for bullets in bulleted lists.
|
||||||
DOTS = ['·', '*', '\xe2\x80\xa2', '•', '•', '•']
|
DOTS = ['·', '*', '\xe2\x80\xa2', '•', '•', '•']
|
||||||
|
|
||||||
unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
|
unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
|
||||||
@ -28,7 +28,7 @@ trailing_empty_content_re = re.compile(r'(?:<p>(?: |\s|<br \/>)*?</p>\s*)+\
|
|||||||
del x # Temporary variable
|
del x # Temporary variable
|
||||||
|
|
||||||
def escape(html):
|
def escape(html):
|
||||||
"Return the given HTML with ampersands, quotes and carets encoded."
|
"""Returns the given HTML with ampersands, quotes and carets encoded."""
|
||||||
return mark_safe(force_unicode(html).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", '''))
|
return mark_safe(force_unicode(html).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", '''))
|
||||||
escape = allow_lazy(escape, unicode)
|
escape = allow_lazy(escape, unicode)
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ def conditional_escape(html):
|
|||||||
return escape(html)
|
return escape(html)
|
||||||
|
|
||||||
def linebreaks(value, autoescape=False):
|
def linebreaks(value, autoescape=False):
|
||||||
"Converts newlines into <p> and <br />s"
|
"""Converts newlines into <p> and <br />s."""
|
||||||
value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines
|
value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines
|
||||||
paras = re.split('\n{2,}', value)
|
paras = re.split('\n{2,}', value)
|
||||||
if autoescape:
|
if autoescape:
|
||||||
@ -53,28 +53,28 @@ def linebreaks(value, autoescape=False):
|
|||||||
linebreaks = allow_lazy(linebreaks, unicode)
|
linebreaks = allow_lazy(linebreaks, unicode)
|
||||||
|
|
||||||
def strip_tags(value):
|
def strip_tags(value):
|
||||||
"Return the given HTML with all tags stripped."
|
"""Returns the given HTML with all tags stripped."""
|
||||||
return re.sub(r'<[^>]*?>', '', force_unicode(value))
|
return re.sub(r'<[^>]*?>', '', force_unicode(value))
|
||||||
strip_tags = allow_lazy(strip_tags)
|
strip_tags = allow_lazy(strip_tags)
|
||||||
|
|
||||||
def strip_spaces_between_tags(value):
|
def strip_spaces_between_tags(value):
|
||||||
"Return the given HTML with spaces between tags removed."
|
"""Returns the given HTML with spaces between tags removed."""
|
||||||
return re.sub(r'>\s+<', '><', force_unicode(value))
|
return re.sub(r'>\s+<', '><', force_unicode(value))
|
||||||
strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, unicode)
|
strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, unicode)
|
||||||
|
|
||||||
def strip_entities(value):
|
def strip_entities(value):
|
||||||
"Return the given HTML with all entities (&something;) stripped."
|
"""Returns the given HTML with all entities (&something;) stripped."""
|
||||||
return re.sub(r'&(?:\w+|#\d+);', '', force_unicode(value))
|
return re.sub(r'&(?:\w+|#\d+);', '', force_unicode(value))
|
||||||
strip_entities = allow_lazy(strip_entities, unicode)
|
strip_entities = allow_lazy(strip_entities, unicode)
|
||||||
|
|
||||||
def fix_ampersands(value):
|
def fix_ampersands(value):
|
||||||
"Return the given HTML with all unencoded ampersands encoded correctly."
|
"""Returns the given HTML with all unencoded ampersands encoded correctly."""
|
||||||
return unencoded_ampersands_re.sub('&', force_unicode(value))
|
return unencoded_ampersands_re.sub('&', force_unicode(value))
|
||||||
fix_ampersands = allow_lazy(fix_ampersands, unicode)
|
fix_ampersands = allow_lazy(fix_ampersands, unicode)
|
||||||
|
|
||||||
def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
|
def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
|
||||||
"""
|
"""
|
||||||
Convert any URLs in text into clickable links.
|
Converts any URLs in text into clickable links.
|
||||||
|
|
||||||
Works on http://, https://, and www. links. Links can have trailing
|
Works on http://, https://, and www. links. Links can have trailing
|
||||||
punctuation (periods, commas, close-parens) and leading punctuation
|
punctuation (periods, commas, close-parens) and leading punctuation
|
||||||
@ -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,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
Utilities for providing backwards compatibility for the maxlength argument,
|
Utilities for providing backwards compatibility for the maxlength argument,
|
||||||
which has been replaced by max_length, see ticket #2101.
|
which has been replaced by max_length. See ticket #2101.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
@ -15,17 +15,15 @@ def legacy_maxlength(max_length, maxlength):
|
|||||||
"""
|
"""
|
||||||
Consolidates max_length and maxlength, providing backwards compatibilty
|
Consolidates max_length and maxlength, providing backwards compatibilty
|
||||||
for the legacy "maxlength" argument.
|
for the legacy "maxlength" argument.
|
||||||
|
|
||||||
If one of max_length or maxlength is given, then that value is returned.
|
If one of max_length or maxlength is given, then that value is returned.
|
||||||
If both are given, a TypeError is raised.
|
If both are given, a TypeError is raised. If maxlength is used at all, a
|
||||||
If maxlength is used at all, a deprecation warning is issued.
|
deprecation warning is issued.
|
||||||
"""
|
"""
|
||||||
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.", DeprecationWarning, stacklevel=3)
|
||||||
PendingDeprecationWarning,
|
|
||||||
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 cannot take both the max_length argument and the legacy maxlength argument.")
|
||||||
" argument and the legacy maxlength argument.")
|
|
||||||
max_length = maxlength
|
max_length = maxlength
|
||||||
return max_length
|
return max_length
|
||||||
|
|
||||||
@ -34,6 +32,7 @@ def remove_maxlength(func):
|
|||||||
A decorator to be used on a class's __init__ that provides backwards
|
A decorator to be used on a class's __init__ that provides backwards
|
||||||
compatibilty for the legacy "maxlength" keyword argument, i.e.
|
compatibilty for the legacy "maxlength" keyword argument, i.e.
|
||||||
name = models.CharField(maxlength=20)
|
name = models.CharField(maxlength=20)
|
||||||
|
|
||||||
It does this by changing the passed "maxlength" keyword argument
|
It does this by changing the passed "maxlength" keyword argument
|
||||||
(if it exists) into a "max_length" keyword argument.
|
(if it exists) into a "max_length" keyword argument.
|
||||||
"""
|
"""
|
||||||
@ -58,7 +57,6 @@ class LegacyMaxlength(type):
|
|||||||
Metaclass for providing backwards compatibility support for the
|
Metaclass for providing backwards compatibility support for the
|
||||||
"maxlength" keyword argument.
|
"maxlength" keyword argument.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(cls, name, bases, attrs):
|
def __init__(cls, name, bases, attrs):
|
||||||
super(LegacyMaxlength, cls).__init__(name, bases, attrs)
|
super(LegacyMaxlength, cls).__init__(name, bases, attrs)
|
||||||
# Decorate the class's __init__ to remove any maxlength keyword.
|
# Decorate the class's __init__ to remove any maxlength keyword.
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template import Template, Context, TemplateDoesNotExist
|
from django.template import Template, Context, TemplateDoesNotExist
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.http import HttpResponseServerError, HttpResponseNotFound
|
from django.http import HttpResponseServerError, HttpResponseNotFound
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_unicode
|
||||||
import os, re, sys
|
|
||||||
|
|
||||||
HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST')
|
HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST')
|
||||||
|
|
||||||
@ -131,7 +134,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 +147,8 @@ 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(),
|
||||||
|
'sys_path' : sys.path,
|
||||||
'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,
|
||||||
@ -229,8 +234,8 @@ TECHNICAL_500_TEMPLATE = """
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<meta name="robots" content="NONE,NOARCHIVE" />
|
<meta name="robots" content="NONE,NOARCHIVE">
|
||||||
<title>{{ exception_type }} at {{ request.path|escape }}</title>
|
<title>{{ exception_type }} at {{ request.path|escape }}</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
html * { padding:0; margin:0; }
|
html * { padding:0; margin:0; }
|
||||||
@ -275,6 +280,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">
|
||||||
//<!--
|
//<!--
|
||||||
@ -365,6 +372,10 @@ TECHNICAL_500_TEMPLATE = """
|
|||||||
<th>Python Version:</th>
|
<th>Python Version:</th>
|
||||||
<td>{{ sys_version_info }}</td>
|
<td>{{ sys_version_info }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Python Path:</th>
|
||||||
|
<td>{{ sys_path }}</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% if unicode_hint %}
|
{% if unicode_hint %}
|
||||||
@ -409,9 +420,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 +465,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>
|
||||||
|
<input type="submit" value="Share this traceback on public Web site">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="requestinfo">
|
<div id="requestinfo">
|
||||||
@ -602,9 +635,9 @@ TECHNICAL_404_TEMPLATE = """
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
<title>Page not found at {{ request.path|escape }}</title>
|
<title>Page not found at {{ request.path|escape }}</title>
|
||||||
<meta name="robots" content="NONE,NOARCHIVE" />
|
<meta name="robots" content="NONE,NOARCHIVE">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
html * { padding:0; margin:0; }
|
html * { padding:0; margin:0; }
|
||||||
body * { padding:10px 20px; }
|
body * { padding:10px 20px; }
|
||||||
|
@ -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,15 @@ 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. In some cases, a ticket might get moved to the "Someday/Maybe" state.
|
||||||
patch. If the patch is complete, it'll be marked as "ready for checkin" so
|
This means the ticket is an enhancement request that we might consider
|
||||||
that a core developer knows to review and check in the patches.
|
adding to the framework if an excellent patch is submitted. These
|
||||||
|
tickets are not a 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 +660,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
|
||||||
@ -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 set up 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,18 @@ 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.
|
||||||
|
|
||||||
|
**Remember:** If your custom field needs the ``to_python()`` method to be
|
||||||
|
called when it is created, you should be using `The SubfieldBase metaclass`_
|
||||||
|
mentioned earlier. Otherwise ``to_python()`` won't be called automatically.
|
||||||
|
|
||||||
``get_db_prep_save(self, value)``
|
``get_db_prep_save(self, value)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -377,7 +392,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 +404,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 +413,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 +474,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 +485,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 +504,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 +526,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 +542,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 +563,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,11 @@ 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.
|
||||||
|
|
||||||
|
**New in the Django development version:** Use the ``DEFAULT_TABLESPACE`` and
|
||||||
|
``DEFAULT_INDEX_TABLESPACE`` settings to specify default values for the
|
||||||
|
db_tablespace options. These are useful for setting a tablespace for the
|
||||||
|
built-in Django apps and other applications whose code you cannot control.
|
||||||
|
|
||||||
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,15 @@ 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
|
||||||
will not require permission to do so. If you will be using Django's
|
``ALTER TABLE`` privileges during ``syncdb`` but won't issue
|
||||||
`testing framework`_ with data fixtures, Django will need permission
|
``ALTER TABLE`` statements on a table once ``syncdb`` has created it.
|
||||||
to create a temporary test database.
|
|
||||||
|
If you're using Django's `testing framework`_ to test database queries,
|
||||||
|
Django will need permission to create a 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 behavior of ``APPEND_SLASH`` has
|
||||||
|
changed slightly in the development version. It didn't used to check to see
|
||||||
|
whether the pattern was matched in the URLconf.
|
||||||
|
|
||||||
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
|
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``
|
||||||
|
313
docs/modelforms.txt
Normal file
313
docs/modelforms.txt
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
==========================
|
||||||
|
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.
|
||||||
|
>>> form = ArticleForm()
|
||||||
|
|
||||||
|
# Creating a form to change an existing article.
|
||||||
|
>>> article = Article.objects.get(pk=1)
|
||||||
|
>>> form = ArticleForm(instance=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`` can accept an existing
|
||||||
|
model instance as the keyword argument ``instance``; if this is
|
||||||
|
supplied, ``save()`` will update that instance. If it's not supplied,
|
||||||
|
``save()`` will create a new instance of the specified model::
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
# Create a form to edit an existing Article.
|
||||||
|
>>> a = Article.objects.get(pk=1)
|
||||||
|
>>> f = ArticleForm(instance=a)
|
||||||
|
|
||||||
|
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.
|
||||||
|
>>> 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.
|
||||||
|
>>> 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(request.POST, instance=instance)
|
||||||
|
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
|
@ -756,13 +756,14 @@ 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>
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
<dt>{{ field.label_tag }}{{ field.label }}{% if field.field.required %}*{% endif %}</dt>
|
<dt>{{ field.label_tag }}{% if field.field.required %}*{% endif %}</dt>
|
||||||
<dd>{{ field }}</dd>
|
<dd>{{ field }}</dd>
|
||||||
{% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %}
|
{% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %}
|
||||||
{% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %}
|
{% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %}
|
||||||
@ -772,10 +773,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
|
||||||
--------------------------------
|
--------------------------------
|
||||||
@ -1105,9 +1107,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('')
|
||||||
@ -1115,14 +1117,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
|
||||||
----------------------
|
----------------------
|
||||||
@ -1766,421 +1770,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.
|
|
||||||
|
|
||||||
More coming soon
|
More coming soon
|
||||||
================
|
================
|
||||||
|
@ -555,12 +555,15 @@ 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 that 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.
|
||||||
|
|
||||||
The 500 (server error) view
|
The 500 (server error) view
|
||||||
@ -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,26 @@ 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
|
||||||
|
------------------
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Default: ``''`` (Empty string)
|
||||||
|
|
||||||
|
Default tablespace to use for models that don't specify one, if the
|
||||||
|
backend supports it.
|
||||||
|
|
||||||
|
DEFAULT_INDEX_TABLESPACE
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Default: ``''`` (Empty string)
|
||||||
|
|
||||||
|
Default tablespace to use for indexes on fields that don't specify
|
||||||
|
one, if the backend supports it.
|
||||||
|
|
||||||
DISALLOWED_USER_AGENTS
|
DISALLOWED_USER_AGENTS
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -1129,12 +1170,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
|
||||||
|
|
||||||
|
@ -234,6 +234,7 @@ request to the URL ``/rss/beats/0613/``:
|
|||||||
``get_object()`` method, passing it the bits. In this case, bits is
|
``get_object()`` method, passing it the bits. In this case, bits is
|
||||||
``['0613']``. For a request to ``/rss/beats/0613/foo/bar/``, bits would
|
``['0613']``. For a request to ``/rss/beats/0613/foo/bar/``, bits would
|
||||||
be ``['0613', 'foo', 'bar']``.
|
be ``['0613', 'foo', 'bar']``.
|
||||||
|
|
||||||
* ``get_object()`` is responsible for retrieving the given beat, from the
|
* ``get_object()`` is responsible for retrieving the given beat, from the
|
||||||
given ``bits``. In this case, it uses the Django database API to retrieve
|
given ``bits``. In this case, it uses the Django database API to retrieve
|
||||||
the beat. Note that ``get_object()`` should raise
|
the beat. Note that ``get_object()`` should raise
|
||||||
@ -243,6 +244,7 @@ request to the URL ``/rss/beats/0613/``:
|
|||||||
raises ``Beat.DoesNotExist`` on failure, and ``Beat.DoesNotExist`` is a
|
raises ``Beat.DoesNotExist`` on failure, and ``Beat.DoesNotExist`` is a
|
||||||
subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
|
subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
|
||||||
``get_object()`` tells Django to produce a 404 error for that request.
|
``get_object()`` tells Django to produce a 404 error for that request.
|
||||||
|
|
||||||
* To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
|
* To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
|
||||||
Django uses the ``title()``, ``link()`` and ``description()`` methods. In
|
Django uses the ``title()``, ``link()`` and ``description()`` methods. In
|
||||||
the previous example, they were simple string class attributes, but this
|
the previous example, they were simple string class attributes, but this
|
||||||
@ -258,9 +260,9 @@ request to the URL ``/rss/beats/0613/``:
|
|||||||
Inside the ``link()`` method, we handle the possibility that ``obj``
|
Inside the ``link()`` method, we handle the possibility that ``obj``
|
||||||
might be ``None``, which can occur when the URL isn't fully specified. In
|
might be ``None``, which can occur when the URL isn't fully specified. In
|
||||||
some cases, you might want to do something else in this case, which would
|
some cases, you might want to do something else in this case, which would
|
||||||
mean you'd need to check for ``obj`` existing in other methods as well
|
mean you'd need to check for ``obj`` existing in other methods as well.
|
||||||
(the ``link()`` method is called very early in the feed generation
|
(The ``link()`` method is called very early in the feed generation
|
||||||
process, so is a good place to bail out early).
|
process, so it's a good place to bail out early.)
|
||||||
|
|
||||||
* Finally, note that ``items()`` in this example also takes the ``obj``
|
* Finally, note that ``items()`` in this example also takes the ``obj``
|
||||||
argument. The algorithm for ``items`` is the same as described in the
|
argument. The algorithm for ``items`` is the same as described in the
|
||||||
@ -603,8 +605,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
|
||||||
@ -509,17 +565,17 @@ autoescape
|
|||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
Control the current auto-escaping behaviour. This tag takes either ``on`` or
|
Control the current auto-escaping behavior. This tag takes either ``on`` or
|
||||||
``off`` as an argument and that determines whether auto-escaping is in effect
|
``off`` as an argument and that determines whether auto-escaping is in effect
|
||||||
inside the block.
|
inside the block.
|
||||||
|
|
||||||
When auto-escaping is in effect, all variable content has HTML escaping applied
|
When auto-escaping is in effect, all variable content has HTML escaping applied
|
||||||
to it before placing the result into the output (but after any filters have
|
to it before placing the result into the output (but after any filters have
|
||||||
been applied). This is equivalent to manually applying the ``escape`` filter
|
been applied). This is equivalent to manually applying the ``escape`` filter
|
||||||
attached to each variable.
|
to each variable.
|
||||||
|
|
||||||
The only exceptions are variables that are already marked as 'safe' from
|
The only exceptions are variables that are already marked as "safe" from
|
||||||
escaping, either by the code that populated the variable, or because it has
|
escaping, either by the code that populated the variable, or because it has had
|
||||||
the ``safe`` or ``escape`` filters applied.
|
the ``safe`` or ``escape`` filters applied.
|
||||||
|
|
||||||
block
|
block
|
||||||
@ -1171,8 +1227,10 @@ Adds the arg to the value.
|
|||||||
addslashes
|
addslashes
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
Adds slashes. Useful for passing strings to JavaScript, for example.
|
Adds slashes before quotes. Useful for escaping strings in CSV, for example.
|
||||||
|
|
||||||
|
**New in Django development version**: for escaping data in JavaScript strings,
|
||||||
|
use the `escapejs` filter instead.
|
||||||
|
|
||||||
capfirst
|
capfirst
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
@ -1230,11 +1288,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
|
||||||
@ -1246,6 +1304,15 @@ applied to the result will only result in one round of escaping being done. So
|
|||||||
it is safe to use this function even in auto-escaping environments. If you want
|
it is safe to use this function even in auto-escaping environments. If you want
|
||||||
multiple escaping passes to be applied, use the ``force_escape`` filter.
|
multiple escaping passes to be applied, use the ``force_escape`` filter.
|
||||||
|
|
||||||
|
escapejs
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Escapes characters for use in JavaScript strings. This does *not* make the
|
||||||
|
string safe for use in HTML, but does protect you from syntax errors when using
|
||||||
|
templates to generate JavaScript/JSON.
|
||||||
|
|
||||||
filesizeformat
|
filesizeformat
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -1268,36 +1335,36 @@ floatformat
|
|||||||
When used without an argument, rounds a floating-point number to one decimal
|
When used without an argument, rounds a floating-point number to one decimal
|
||||||
place -- but only if there's a decimal part to be displayed. For example:
|
place -- but only if there's a decimal part to be displayed. For example:
|
||||||
|
|
||||||
======== ======================= ======
|
============ =========================== ========
|
||||||
value Template Output
|
``value`` Template Output
|
||||||
======== ======================= ======
|
============ =========================== ========
|
||||||
34.23234 {{ value|floatformat }} 34.2
|
``34.23234`` ``{{ value|floatformat }}`` ``34.2``
|
||||||
34.00000 {{ value|floatformat }} 34
|
``34.00000`` ``{{ value|floatformat }}`` ``34``
|
||||||
34.26000 {{ value|floatformat }} 34.3
|
``34.26000`` ``{{ value|floatformat }}`` ``34.3``
|
||||||
======== ======================= ======
|
============ =========================== ========
|
||||||
|
|
||||||
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
|
||||||
======== ========================= ======
|
============ ============================= ==========
|
||||||
34.23234 {{ value|floatformat:3 }} 34.232
|
``34.23234`` ``{{ value|floatformat:3 }}`` ``34.232``
|
||||||
34.00000 {{ value|floatformat:3 }} 34.000
|
``34.00000`` ``{{ value|floatformat:3 }}`` ``34.000``
|
||||||
34.26000 {{ value|floatformat:3 }} 34.260
|
``34.26000`` ``{{ value|floatformat:3 }}`` ``34.260``
|
||||||
======== ========================= ======
|
============ ============================= ==========
|
||||||
|
|
||||||
If the argument passed to ``floatformat`` is negative, it will round a number
|
If the argument passed to ``floatformat`` is negative, it will round a number
|
||||||
to that many decimal places -- but only if there's a decimal part to be
|
to that many decimal places -- but only if there's a decimal part to be
|
||||||
displayed. For example:
|
displayed. For example:
|
||||||
|
|
||||||
======== ============================ ======
|
============ ================================ ==========
|
||||||
value Template Output
|
``value`` Template Output
|
||||||
======== ============================ ======
|
============ ================================ ==========
|
||||||
34.23234 {{ value|floatformat:"-3" }} 34.232
|
``34.23234`` ``{{ value|floatformat:"-3" }}`` ``34.232``
|
||||||
34.00000 {{ value|floatformat:"-3" }} 34
|
``34.00000`` ``{{ value|floatformat:"-3" }}`` ``34``
|
||||||
34.26000 {{ value|floatformat:"-3" }} 34.260
|
``34.26000`` ``{{ value|floatformat:"-3" }}`` ``34.260``
|
||||||
======== ============================ ======
|
============ ================================ ==========
|
||||||
|
|
||||||
Using ``floatformat`` with no argument is equivalent to using ``floatformat``
|
Using ``floatformat`` with no argument is equivalent to using ``floatformat``
|
||||||
with an argument of ``-1``.
|
with an argument of ``-1``.
|
||||||
|
@ -727,52 +727,63 @@ 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.
|
||||||
|
|
||||||
|
Template filter code falls into one of two situations:
|
||||||
|
|
||||||
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
||||||
``'``, ``"`` or ``&``) into the result that were not already present. In
|
``'``, ``"`` or ``&``) into the result that were not already present. In
|
||||||
this case, you can let Django take care of all the auto-escaping handling
|
this case, you can let Django take care of all the auto-escaping
|
||||||
for you. All you need to do is put the ``is_safe`` attribute on your
|
handling for you. All you need to do is put the ``is_safe`` attribute on
|
||||||
filter function and set it to ``True``. This attribute tells Django that
|
your filter function and set it to ``True``, like so::
|
||||||
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
|
@register.filter
|
||||||
escape it, if necessary. The reason ``is_safe`` is necessary is because
|
def myfilter(value):
|
||||||
there are plenty of normal string operations that will turn a ``SafeData``
|
return value
|
||||||
object back into a normal ``str`` or ``unicode`` object and, rather than
|
myfilter.is_safe = True
|
||||||
try to catch them all, which would be very difficult, Django repairs the
|
|
||||||
damage after the filter has completed.
|
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
|
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 into
|
end of any input. Since this introduces no dangerous HTML characters to
|
||||||
the result (aside from any that were already present), you should mark
|
the result (aside from any that were already present), you should mark
|
||||||
your filter with ``is_safe``::
|
your filter with ``is_safe``::
|
||||||
|
|
||||||
@ -785,43 +796,42 @@ situations:
|
|||||||
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
|
||||||
@ -838,10 +848,10 @@ situations:
|
|||||||
|
|
||||||
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
|
||||||
@ -851,9 +861,9 @@ situations:
|
|||||||
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()
|
||||||
"""}
|
"""}
|
||||||
|
@ -132,4 +132,8 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
|
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
|
||||||
[(u'clearish', <ContentType: mineral>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
|
[(u'clearish', <ContentType: mineral>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
|
||||||
|
|
||||||
|
>>> ctype = ContentType.objects.get_for_model(lion)
|
||||||
|
>>> Animal.objects.filter(tags__content_type=ctype)
|
||||||
|
[<Animal: Platypus>]
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -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")
|
||||||
|
@ -108,6 +108,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.
|
||||||
@ -191,4 +195,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,20 +55,124 @@ 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 a different model than its parent.
|
||||||
|
|
||||||
|
>>> 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.
|
||||||
|
|
||||||
|
This one is OK since the subclass specifies the same model as the parent.
|
||||||
|
|
||||||
|
>>> class SubCategoryForm(CategoryForm):
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
|
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
... class Meta:
|
||||||
|
... model = Category
|
||||||
>>> f = CategoryForm()
|
>>> f = CategoryForm()
|
||||||
>>> 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>
|
||||||
@ -184,7 +256,9 @@ 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):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
>>> f = ArticleForm(auto_id=False)
|
>>> f = ArticleForm(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>
|
||||||
@ -214,28 +288,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):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
|
... fields = ('headline','pub_date')
|
||||||
>>> f = PartialArticleForm(auto_id=False)
|
>>> f = PartialArticleForm(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(auto_id=False, instance=w)
|
||||||
>>> 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 +312,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(auto_id=False, instance=art)
|
||||||
>>> 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 +337,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({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}, instance=art)
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> test_art = f.save()
|
>>> test_art = f.save()
|
||||||
@ -278,8 +349,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({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False, instance=art)
|
||||||
>>> 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 +373,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(auto_id=False, instance=new_art)
|
||||||
>>> 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>
|
||||||
@ -324,7 +400,7 @@ Add some categories and test the many-to-many form output.
|
|||||||
</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({'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']}, instance=new_art)
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
1
|
1
|
||||||
@ -334,7 +410,7 @@ Add some categories and test the many-to-many form output.
|
|||||||
|
|
||||||
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({'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.'}, instance=new_art)
|
||||||
>>> new_art = f.save()
|
>>> new_art = f.save()
|
||||||
>>> new_art.id
|
>>> new_art.id
|
||||||
1
|
1
|
||||||
@ -343,7 +419,9 @@ 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):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
>>> f = ArticleForm({'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()
|
||||||
@ -354,7 +432,9 @@ 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):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
|
>>> f = ArticleForm({'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()
|
||||||
@ -366,7 +446,9 @@ 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):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
|
>>> f = ArticleForm({'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 +468,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 +480,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({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat)
|
||||||
>>> 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,7 +489,9 @@ 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):
|
||||||
|
... class Meta:
|
||||||
|
... model = Article
|
||||||
>>> f = ArticleForm(auto_id=False)
|
>>> f = ArticleForm(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>
|
||||||
@ -609,60 +693,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):
|
||||||
|
... class Meta:
|
||||||
|
... model = PhoneNumber
|
||||||
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
>>> f = PhoneNumberForm({'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).
|
||||||
@ -218,3 +218,41 @@ None
|
|||||||
3.4
|
3.4
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
__test__['YAML'] = """
|
||||||
|
# Create some data:
|
||||||
|
|
||||||
|
>>> articles = Article.objects.all().order_by("id")[:2]
|
||||||
|
>>> from django.core import serializers
|
||||||
|
|
||||||
|
# test if serial
|
||||||
|
|
||||||
|
>>> serialized = serializers.serialize("yaml", articles)
|
||||||
|
>>> print serialized
|
||||||
|
- fields:
|
||||||
|
author: 2
|
||||||
|
categories: [3, 1]
|
||||||
|
headline: Just kidding; I love TV poker
|
||||||
|
pub_date: 2006-06-16 11:00:00
|
||||||
|
model: serializers.article
|
||||||
|
pk: 1
|
||||||
|
- fields:
|
||||||
|
author: 1
|
||||||
|
categories: [2, 3]
|
||||||
|
headline: Time to reform copyright
|
||||||
|
pub_date: 2006-06-16 13:00:11
|
||||||
|
model: serializers.article
|
||||||
|
pk: 2
|
||||||
|
<BLANKLINE>
|
||||||
|
|
||||||
|
>>> obs = list(serializers.deserialize("yaml", serialized))
|
||||||
|
>>> for i in obs:
|
||||||
|
... print i
|
||||||
|
<DeserializedObject: Just kidding; I love TV poker>
|
||||||
|
<DeserializedObject: Time to reform copyright>
|
||||||
|
|
||||||
|
"""
|
||||||
|
except ImportError: pass
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
56
tests/regressiontests/cache/tests.py
vendored
56
tests/regressiontests/cache/tests.py
vendored
@ -3,8 +3,8 @@
|
|||||||
# Unit tests for cache framework
|
# Unit tests for cache framework
|
||||||
# Uses whatever cache backend is set in the test settings file.
|
# Uses whatever cache backend is set in the test settings file.
|
||||||
|
|
||||||
import time, unittest
|
import time
|
||||||
|
import unittest
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
@ -72,12 +72,20 @@ 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
|
cache.set('expire1', 'very quickly', 1)
|
||||||
cache.set('expire', 'very quickly', 1)
|
cache.set('expire2', 'very quickly', 1)
|
||||||
|
cache.set('expire3', 'very quickly', 1)
|
||||||
|
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
self.assertEqual(cache.get("expire"), None)
|
self.assertEqual(cache.get("expire1"), None)
|
||||||
|
|
||||||
|
cache.add("expire2", "newvalue")
|
||||||
|
self.assertEqual(cache.get("expire2"), "newvalue")
|
||||||
|
self.assertEqual(cache.has_key("expire3"), False)
|
||||||
|
|
||||||
def test_unicode(self):
|
def test_unicode(self):
|
||||||
stuff = {
|
stuff = {
|
||||||
@ -90,6 +98,44 @@ class Cache(unittest.TestCase):
|
|||||||
cache.set(key, value)
|
cache.set(key, value)
|
||||||
self.assertEqual(cache.get(key), value)
|
self.assertEqual(cache.get(key), value)
|
||||||
|
|
||||||
|
import os
|
||||||
|
import md5
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
from django.core.cache.backends.filebased import CacheClass as FileCache
|
||||||
|
|
||||||
|
class FileBasedCacheTests(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
Specific test cases for the file-based cache.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.dirname = tempfile.mktemp()
|
||||||
|
os.mkdir(self.dirname)
|
||||||
|
self.cache = FileCache(self.dirname, {})
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
shutil.rmtree(self.dirname)
|
||||||
|
|
||||||
|
def test_hashing(self):
|
||||||
|
"""Test that keys are hashed into subdirectories correctly"""
|
||||||
|
self.cache.set("foo", "bar")
|
||||||
|
keyhash = md5.new("foo").hexdigest()
|
||||||
|
keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
|
||||||
|
self.assert_(os.path.exists(keypath))
|
||||||
|
|
||||||
|
def test_subdirectory_removal(self):
|
||||||
|
"""
|
||||||
|
Make sure that the created subdirectories are correctly removed when empty.
|
||||||
|
"""
|
||||||
|
self.cache.set("foo", "bar")
|
||||||
|
keyhash = md5.new("foo").hexdigest()
|
||||||
|
keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
|
||||||
|
self.assert_(os.path.exists(keypath))
|
||||||
|
|
||||||
|
self.cache.delete("foo")
|
||||||
|
self.assert_(not os.path.exists(keypath))
|
||||||
|
self.assert_(not os.path.exists(os.path.dirname(keypath)))
|
||||||
|
self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath))))
|
||||||
|
|
||||||
class CacheUtils(unittest.TestCase):
|
class CacheUtils(unittest.TestCase):
|
||||||
"""TestCase for django.utils.cache functions."""
|
"""TestCase for django.utils.cache functions."""
|
||||||
|
@ -49,6 +49,18 @@ u'\\\\ : backslashes, too'
|
|||||||
>>> capfirst(u'hello world')
|
>>> capfirst(u'hello world')
|
||||||
u'Hello world'
|
u'Hello world'
|
||||||
|
|
||||||
|
>>> escapejs(u'"double quotes" and \'single quotes\'')
|
||||||
|
u'\\"double quotes\\" and \\\'single quotes\\\''
|
||||||
|
|
||||||
|
>>> escapejs(ur'\ : backslashes, too')
|
||||||
|
u'\\\\ : backslashes, too'
|
||||||
|
|
||||||
|
>>> escapejs(u'and lots of whitespace: \r\n\t\v\f\b')
|
||||||
|
u'and lots of whitespace: \\r\\n\\t\\v\\f\\b'
|
||||||
|
|
||||||
|
>>> escapejs(ur'<script>and this</script>')
|
||||||
|
u'<script>and this<\\/script>'
|
||||||
|
|
||||||
>>> fix_ampersands(u'Jack & Jill & Jeroboam')
|
>>> fix_ampersands(u'Jack & Jill & Jeroboam')
|
||||||
u'Jack & Jill & Jeroboam'
|
u'Jack & Jill & Jeroboam'
|
||||||
|
|
||||||
|
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
|
||||||
@ -50,6 +51,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,
|
||||||
'util_tests': util_tests,
|
'util_tests': util_tests,
|
||||||
'widgets_tests': widgets_tests,
|
'widgets_tests': widgets_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__ = {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user