mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +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>
|
||||
Pete Crosier <pete.crosier@gmail.com>
|
||||
Matt Croydon <http://www.postneo.com/>
|
||||
Leah Culver <leah@pownce.com>
|
||||
flavio.curella@gmail.com
|
||||
Jure Cuhalev <gandalf@owca.info>
|
||||
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>
|
||||
gandalf@owca.info
|
||||
Marc Garcia <marc.garcia@accopensys.com>
|
||||
Andy Gayton <andy-django@thecablelounge.com>
|
||||
Baishampayan Ghose
|
||||
Dimitris Glezos <dimitris@glezos.com>
|
||||
glin@seznam.cz
|
||||
@ -171,6 +173,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
junzhang.jn@gmail.com
|
||||
Antti Kaihola <http://akaihola.blogspot.com/>
|
||||
Nagy Károly <charlie@rendszergazda.com>
|
||||
Erik Karulf <erik@karulf.com>
|
||||
Ben Dean Kawamura <ben.dean.kawamura@gmail.com>
|
||||
Ian G. Kelly <ian.g.kelly@gmail.com>
|
||||
Thomas Kerpe <thomas@kerpe.net>
|
||||
@ -190,6 +193,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Joseph Kocherhans
|
||||
konrad@gwu.edu
|
||||
knox <christobzr@gmail.com>
|
||||
David Krauth
|
||||
kurtiss@meetro.com
|
||||
lakin.wecker@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>
|
||||
limodou
|
||||
Philip Lindborg <philip.lindborg@gmail.com>
|
||||
Trey Long <trey@ktrl.com>
|
||||
msaelices <msaelices@gmail.com>
|
||||
Matt McClanahan <http://mmcc.cx/>
|
||||
Martin Maney <http://www.chipy.org/Martin_Maney>
|
||||
@ -251,6 +256,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Gustavo Picon
|
||||
Luke Plant <http://lukeplant.me.uk/>
|
||||
plisk
|
||||
Mihai Preda <mihai_preda@yahoo.com>
|
||||
Daniel Poelzleithner <http://poelzi.org/>
|
||||
polpak@yahoo.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>
|
||||
Brian Ray <http://brianray.chipy.org/>
|
||||
remco@diji.biz
|
||||
David Reynolds <david@reynoldsfamily.org.uk>
|
||||
rhettg@gmail.com
|
||||
ricardojbarrios@gmail.com
|
||||
Matt Riggott
|
||||
@ -281,6 +288,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Pete Shinners <pete@shinners.org>
|
||||
jason.sidabras@gmail.com
|
||||
Jozko Skrablin <jozko.skrablin@gmail.com>
|
||||
Ben Slavin <benjamin.slavin@gmail.com>
|
||||
SmileyChris <smileychris@gmail.com>
|
||||
smurf@smurf.noris.de
|
||||
Vsevolod Solovyov
|
||||
|
@ -52,7 +52,7 @@ class LazySettings(object):
|
||||
if not settings_module: # If it's set but is an empty string.
|
||||
raise 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)
|
||||
|
||||
@ -63,7 +63,7 @@ class LazySettings(object):
|
||||
argument must support attribute access (__getattr__)).
|
||||
"""
|
||||
if self._target != None:
|
||||
raise EnvironmentError, 'Settings already configured.'
|
||||
raise RuntimeError, 'Settings already configured.'
|
||||
holder = UserSettingsHolder(default_settings)
|
||||
for name, value in options.items():
|
||||
setattr(holder, name, value)
|
||||
@ -82,7 +82,7 @@ class Settings(object):
|
||||
try:
|
||||
mod = __import__(self.SETTINGS_MODULE, {}, {}, [''])
|
||||
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
|
||||
# as strings.
|
||||
|
@ -253,6 +253,10 @@ TRANSACTIONS_MANAGED = False
|
||||
from django import 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 #
|
||||
##############
|
||||
@ -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
|
||||
# possible values.
|
||||
CACHE_BACKEND = 'simple://'
|
||||
CACHE_BACKEND = 'locmem://'
|
||||
CACHE_MIDDLEWARE_KEY_PREFIX = ''
|
||||
CACHE_MIDDLEWARE_SECONDS = 600
|
||||
|
||||
|
Binary file not shown.
@ -5,9 +5,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2007-11-05 17:33+0100\n"
|
||||
"PO-Revision-Date: 2007-11-05 17:34+0100\n"
|
||||
"Last-Translator: Marc Garcia <marc.garcia@accopensys.com>\n"
|
||||
"POT-Creation-Date: 2007-12-02 22:26+0100\n"
|
||||
"PO-Revision-Date: 2007-12-02 22:32+0100\n"
|
||||
"Last-Translator: Marc Fargas <telenieko@telenieko.com>\n"
|
||||
"Language-Team: <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -194,7 +194,7 @@ msgstr "Xinés simplificat"
|
||||
msgid "Traditional Chinese"
|
||||
msgstr "Xinés tradicional"
|
||||
|
||||
#: contrib/admin/filterspecs.py:42
|
||||
#: contrib/admin/filterspecs.py:44
|
||||
#, python-format
|
||||
msgid ""
|
||||
"<h3>By %s:</h3>\n"
|
||||
@ -203,71 +203,71 @@ msgstr ""
|
||||
"<h3>Per %s:</h3>\n"
|
||||
"<ul>\n"
|
||||
|
||||
#: contrib/admin/filterspecs.py:72 contrib/admin/filterspecs.py:90
|
||||
#: contrib/admin/filterspecs.py:145 contrib/admin/filterspecs.py:171
|
||||
#: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:92
|
||||
#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173
|
||||
msgid "All"
|
||||
msgstr "Tots"
|
||||
|
||||
#: contrib/admin/filterspecs.py:111
|
||||
#: contrib/admin/filterspecs.py:113
|
||||
msgid "Any date"
|
||||
msgstr "Qualsevol data"
|
||||
|
||||
#: contrib/admin/filterspecs.py:112
|
||||
#: contrib/admin/filterspecs.py:114
|
||||
msgid "Today"
|
||||
msgstr "Avui"
|
||||
|
||||
#: contrib/admin/filterspecs.py:115
|
||||
#: contrib/admin/filterspecs.py:117
|
||||
msgid "Past 7 days"
|
||||
msgstr "Últims 7 dies"
|
||||
|
||||
#: contrib/admin/filterspecs.py:117
|
||||
#: contrib/admin/filterspecs.py:119
|
||||
msgid "This month"
|
||||
msgstr "Aquest mes"
|
||||
|
||||
#: contrib/admin/filterspecs.py:119
|
||||
#: contrib/admin/filterspecs.py:121
|
||||
msgid "This year"
|
||||
msgstr "Aquest any"
|
||||
|
||||
#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221
|
||||
#: oldforms/__init__.py:591
|
||||
#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231
|
||||
#: oldforms/__init__.py:592
|
||||
msgid "Yes"
|
||||
msgstr "Si"
|
||||
|
||||
#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221
|
||||
#: oldforms/__init__.py:591
|
||||
#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231
|
||||
#: oldforms/__init__.py:592
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: contrib/admin/filterspecs.py:152 newforms/widgets.py:221
|
||||
#: oldforms/__init__.py:591
|
||||
#: contrib/admin/filterspecs.py:154 newforms/widgets.py:231
|
||||
#: oldforms/__init__.py:592
|
||||
msgid "Unknown"
|
||||
msgstr "Desconegut"
|
||||
|
||||
#: contrib/admin/models.py:17
|
||||
#: contrib/admin/models.py:18
|
||||
msgid "action time"
|
||||
msgstr "moment de l'acció"
|
||||
|
||||
#: contrib/admin/models.py:20
|
||||
#: contrib/admin/models.py:21
|
||||
msgid "object id"
|
||||
msgstr "id del objecte"
|
||||
|
||||
#: contrib/admin/models.py:21
|
||||
#: contrib/admin/models.py:22
|
||||
msgid "object repr"
|
||||
msgstr "'repr' de l'objecte"
|
||||
|
||||
#: contrib/admin/models.py:22
|
||||
#: contrib/admin/models.py:23
|
||||
msgid "action flag"
|
||||
msgstr "marca de l'acció"
|
||||
|
||||
#: contrib/admin/models.py:23
|
||||
#: contrib/admin/models.py:24
|
||||
msgid "change message"
|
||||
msgstr "missatge del canvi"
|
||||
|
||||
#: contrib/admin/models.py:26
|
||||
#: contrib/admin/models.py:27
|
||||
msgid "log entry"
|
||||
msgstr "entrada del registre"
|
||||
|
||||
#: contrib/admin/models.py:27
|
||||
#: contrib/admin/models.py:28
|
||||
msgid "log entries"
|
||||
msgstr "entrades del registre"
|
||||
|
||||
@ -469,7 +469,7 @@ msgid "Password:"
|
||||
msgstr "Contrasenya:"
|
||||
|
||||
#: contrib/admin/templates/admin/login.html:25
|
||||
#: contrib/admin/views/decorators.py:24
|
||||
#: contrib/admin/views/decorators.py:25
|
||||
msgid "Log in"
|
||||
msgstr "Iniciar sessió"
|
||||
|
||||
@ -769,17 +769,17 @@ msgstr "Actualment:"
|
||||
msgid "Change:"
|
||||
msgstr "Modificar:"
|
||||
|
||||
#: contrib/admin/templatetags/admin_list.py:254
|
||||
#: contrib/admin/templatetags/admin_list.py:257
|
||||
msgid "All 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
|
||||
msgid "The %(name)s \"%(obj)s\" was added successfully."
|
||||
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/main.py:354
|
||||
#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:271
|
||||
#: contrib/admin/views/main.py:356
|
||||
msgid "You may edit it again below."
|
||||
msgstr "Pot editar-lo de nou abaix."
|
||||
|
||||
@ -796,7 +796,7 @@ msgstr "Canvi de clau exitós"
|
||||
msgid "Change password: %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 ""
|
||||
"Please enter a correct username and password. Note that both fields are case-"
|
||||
"sensitive."
|
||||
@ -804,7 +804,7 @@ msgstr ""
|
||||
"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."
|
||||
|
||||
#: contrib/admin/views/decorators.py:62
|
||||
#: contrib/admin/views/decorators.py:63
|
||||
msgid ""
|
||||
"Please log in again, because your session has expired. Don't worry: Your "
|
||||
"submission has been saved."
|
||||
@ -812,7 +812,7 @@ msgstr ""
|
||||
"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es "
|
||||
"preocupi, el seu enviament està emmagatzemat."
|
||||
|
||||
#: contrib/admin/views/decorators.py:69
|
||||
#: contrib/admin/views/decorators.py:70
|
||||
msgid ""
|
||||
"Looks like your browser isn't configured to accept cookies. Please enable "
|
||||
"cookies, reload this page, and try again."
|
||||
@ -821,247 +821,247 @@ msgstr ""
|
||||
"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta "
|
||||
"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."
|
||||
msgstr "Els noms d'usuari no poden contenir el caracter '@'."
|
||||
|
||||
#: contrib/admin/views/decorators.py:85
|
||||
#: contrib/admin/views/decorators.py:86
|
||||
#, python-format
|
||||
msgid "Your e-mail address is not your username. Try '%s' instead."
|
||||
msgstr ""
|
||||
"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:51
|
||||
#: contrib/admin/views/doc.py:48 contrib/admin/views/doc.py:50
|
||||
#: contrib/admin/views/doc.py:52
|
||||
msgid "tag:"
|
||||
msgstr "etiqueta:"
|
||||
|
||||
#: contrib/admin/views/doc.py:78 contrib/admin/views/doc.py:80
|
||||
#: contrib/admin/views/doc.py:82
|
||||
#: contrib/admin/views/doc.py:79 contrib/admin/views/doc.py:81
|
||||
#: contrib/admin/views/doc.py:83
|
||||
msgid "filter:"
|
||||
msgstr "filtre:"
|
||||
|
||||
#: contrib/admin/views/doc.py:136 contrib/admin/views/doc.py:138
|
||||
#: contrib/admin/views/doc.py:140
|
||||
#: contrib/admin/views/doc.py:137 contrib/admin/views/doc.py:139
|
||||
#: contrib/admin/views/doc.py:141
|
||||
msgid "view:"
|
||||
msgstr "vista:"
|
||||
|
||||
#: contrib/admin/views/doc.py:165
|
||||
#: contrib/admin/views/doc.py:166
|
||||
#, python-format
|
||||
msgid "App %r not found"
|
||||
msgstr "La aplicació %r no s'ha pogut trobar"
|
||||
|
||||
#: contrib/admin/views/doc.py:172
|
||||
#: contrib/admin/views/doc.py:173
|
||||
#, python-format
|
||||
msgid "Model %(name)r not found in app %(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
|
||||
msgid "the related `%(label)s.%(type)s` object"
|
||||
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:220 contrib/admin/views/doc.py:225
|
||||
#: contrib/admin/views/doc.py:185 contrib/admin/views/doc.py:207
|
||||
#: contrib/admin/views/doc.py:221 contrib/admin/views/doc.py:226
|
||||
msgid "model:"
|
||||
msgstr "model:"
|
||||
|
||||
#: contrib/admin/views/doc.py:215
|
||||
#: contrib/admin/views/doc.py:216
|
||||
#, python-format
|
||||
msgid "related `%(label)s.%(name)s` objects"
|
||||
msgstr "objectes relacionats `%(label)s.%(name)s`"
|
||||
|
||||
#: contrib/admin/views/doc.py:220
|
||||
#: contrib/admin/views/doc.py:221
|
||||
#, python-format
|
||||
msgid "all %s"
|
||||
msgstr "tots %s"
|
||||
|
||||
#: contrib/admin/views/doc.py:225
|
||||
#: contrib/admin/views/doc.py:226
|
||||
#, python-format
|
||||
msgid "number of %s"
|
||||
msgstr "nombre de %s"
|
||||
|
||||
#: contrib/admin/views/doc.py:230
|
||||
#: contrib/admin/views/doc.py:231
|
||||
#, python-format
|
||||
msgid "Fields on %s objects"
|
||||
msgstr "Camps en objectes %s"
|
||||
|
||||
#: contrib/admin/views/doc.py:292 contrib/admin/views/doc.py:303
|
||||
#: contrib/admin/views/doc.py:305 contrib/admin/views/doc.py:311
|
||||
#: contrib/admin/views/doc.py:312 contrib/admin/views/doc.py:314
|
||||
#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:304
|
||||
#: contrib/admin/views/doc.py:306 contrib/admin/views/doc.py:312
|
||||
#: contrib/admin/views/doc.py:313 contrib/admin/views/doc.py:315
|
||||
msgid "Integer"
|
||||
msgstr "Enter"
|
||||
|
||||
#: contrib/admin/views/doc.py:293
|
||||
#: contrib/admin/views/doc.py:294
|
||||
msgid "Boolean (Either True or False)"
|
||||
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
|
||||
msgid "String (up to %(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"
|
||||
msgstr "Enters separats per comes"
|
||||
|
||||
#: contrib/admin/views/doc.py:296
|
||||
#: contrib/admin/views/doc.py:297
|
||||
msgid "Date (without time)"
|
||||
msgstr "Data (sense hora)"
|
||||
|
||||
#: contrib/admin/views/doc.py:297
|
||||
#: contrib/admin/views/doc.py:298
|
||||
msgid "Date (with time)"
|
||||
msgstr "Data (amb hora)"
|
||||
|
||||
#: contrib/admin/views/doc.py:298
|
||||
#: contrib/admin/views/doc.py:299
|
||||
msgid "Decimal number"
|
||||
msgstr "Número decimal"
|
||||
|
||||
#: contrib/admin/views/doc.py:299
|
||||
#: contrib/admin/views/doc.py:300
|
||||
msgid "E-mail address"
|
||||
msgstr "Adreça de correu electrònic"
|
||||
|
||||
#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301
|
||||
#: contrib/admin/views/doc.py:304
|
||||
#: contrib/admin/views/doc.py:301 contrib/admin/views/doc.py:302
|
||||
#: contrib/admin/views/doc.py:305
|
||||
msgid "File path"
|
||||
msgstr "Ruta del fitxer"
|
||||
|
||||
#: contrib/admin/views/doc.py:302
|
||||
#: contrib/admin/views/doc.py:303
|
||||
msgid "Floating point number"
|
||||
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"
|
||||
msgstr "Adreça IP"
|
||||
|
||||
#: contrib/admin/views/doc.py:308
|
||||
#: contrib/admin/views/doc.py:309
|
||||
msgid "Boolean (Either True, False or None)"
|
||||
msgstr "Booleà (Verdader, Fals o 'None' (cap))"
|
||||
|
||||
#: contrib/admin/views/doc.py:309
|
||||
#: contrib/admin/views/doc.py:310
|
||||
msgid "Relation to parent model"
|
||||
msgstr "Relació amb el model pare"
|
||||
|
||||
#: contrib/admin/views/doc.py:310
|
||||
#: contrib/admin/views/doc.py:311
|
||||
msgid "Phone number"
|
||||
msgstr "Número de telèfon"
|
||||
|
||||
#: contrib/admin/views/doc.py:315
|
||||
#: contrib/admin/views/doc.py:316
|
||||
msgid "Text"
|
||||
msgstr "Texte"
|
||||
|
||||
#: contrib/admin/views/doc.py:316
|
||||
#: contrib/admin/views/doc.py:317
|
||||
msgid "Time"
|
||||
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"
|
||||
msgstr "URL"
|
||||
|
||||
#: contrib/admin/views/doc.py:318
|
||||
#: contrib/admin/views/doc.py:319
|
||||
msgid "U.S. state (two uppercase letters)"
|
||||
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"
|
||||
msgstr "Texte XML"
|
||||
|
||||
#: contrib/admin/views/doc.py:345
|
||||
#: contrib/admin/views/doc.py:346
|
||||
#, python-format
|
||||
msgid "%s does not appear to be a urlpattern object"
|
||||
msgstr "%s no sembla ser un objecte 'urlpattern'"
|
||||
|
||||
#: contrib/admin/views/main.py:230
|
||||
#: contrib/admin/views/main.py:233
|
||||
msgid "Site administration"
|
||||
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
|
||||
msgid "You may add another %s below."
|
||||
msgstr "Pot afegir un altre %s a baix."
|
||||
|
||||
#: contrib/admin/views/main.py:296
|
||||
#: contrib/admin/views/main.py:298
|
||||
#, python-format
|
||||
msgid "Add %s"
|
||||
msgstr "Afegir %s"
|
||||
|
||||
#: contrib/admin/views/main.py:342
|
||||
#: contrib/admin/views/main.py:344
|
||||
#, python-format
|
||||
msgid "Added %s."
|
||||
msgstr "Agregat %s."
|
||||
|
||||
#: contrib/admin/views/main.py:342 contrib/admin/views/main.py:344
|
||||
#: contrib/admin/views/main.py:346 core/validators.py:283
|
||||
#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346
|
||||
#: contrib/admin/views/main.py:348 core/validators.py:283
|
||||
#: db/models/manipulators.py:309
|
||||
msgid "and"
|
||||
msgstr "i"
|
||||
|
||||
#: contrib/admin/views/main.py:344
|
||||
#: contrib/admin/views/main.py:346
|
||||
#, python-format
|
||||
msgid "Changed %s."
|
||||
msgstr "Modificat %s."
|
||||
|
||||
#: contrib/admin/views/main.py:346
|
||||
#: contrib/admin/views/main.py:348
|
||||
#, python-format
|
||||
msgid "Deleted %s."
|
||||
msgstr "Eliminat %s."
|
||||
|
||||
#: contrib/admin/views/main.py:349
|
||||
#: contrib/admin/views/main.py:351
|
||||
msgid "No fields changed."
|
||||
msgstr "Cap camp canviat."
|
||||
|
||||
#: contrib/admin/views/main.py:352
|
||||
#: contrib/admin/views/main.py:354
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was changed successfully."
|
||||
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
|
||||
msgid ""
|
||||
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
|
||||
msgstr ""
|
||||
"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
|
||||
msgid "Change %s"
|
||||
msgstr "Modificar %s"
|
||||
|
||||
#: contrib/admin/views/main.py:483
|
||||
#: contrib/admin/views/main.py:487
|
||||
#, python-format
|
||||
msgid "One or more %(fieldname)s in %(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
|
||||
msgid "One or more %(fieldname)s in %(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
|
||||
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
|
||||
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?"
|
||||
msgstr "Està segur?"
|
||||
|
||||
#: contrib/admin/views/main.py:545
|
||||
#: contrib/admin/views/main.py:549
|
||||
#, python-format
|
||||
msgid "Change history: %s"
|
||||
msgstr "Modificar històric: %s"
|
||||
|
||||
#: contrib/admin/views/main.py:579
|
||||
#: contrib/admin/views/main.py:583
|
||||
#, python-format
|
||||
msgid "Select %s"
|
||||
msgstr "Seleccioni %s"
|
||||
|
||||
#: contrib/admin/views/main.py:579
|
||||
#: contrib/admin/views/main.py:583
|
||||
#, python-format
|
||||
msgid "Select %s to change"
|
||||
msgstr "Seleccioni %s per modificar"
|
||||
|
||||
#: contrib/admin/views/main.py:780
|
||||
#: contrib/admin/views/main.py:784
|
||||
msgid "Database error"
|
||||
msgstr "Error de/en la base de dades"
|
||||
|
||||
@ -1622,72 +1622,72 @@ msgstr "n"
|
||||
msgid "rd"
|
||||
msgstr "r"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:50
|
||||
#: contrib/humanize/templatetags/humanize.py:52
|
||||
#, python-format
|
||||
msgid "%(value).1f million"
|
||||
msgid_plural "%(value).1f million"
|
||||
msgstr[0] "%(value).1f milió"
|
||||
msgstr[1] "%(value).1f milions"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:53
|
||||
#: contrib/humanize/templatetags/humanize.py:55
|
||||
#, python-format
|
||||
msgid "%(value).1f billion"
|
||||
msgid_plural "%(value).1f billion"
|
||||
msgstr[0] "%(value).1f bilió"
|
||||
msgstr[1] "%(value).1f bilions"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:56
|
||||
#: contrib/humanize/templatetags/humanize.py:58
|
||||
#, python-format
|
||||
msgid "%(value).1f trillion"
|
||||
msgid_plural "%(value).1f trillion"
|
||||
msgstr[0] "%(value).1f trilió"
|
||||
msgstr[1] "%(value).1f trilions"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "one"
|
||||
msgstr "un"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "two"
|
||||
msgstr "dos"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "three"
|
||||
msgstr "tres"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "four"
|
||||
msgstr "cuatre"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "five"
|
||||
msgstr "cinc"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "six"
|
||||
msgstr "sis"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "seven"
|
||||
msgstr "set"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "eight"
|
||||
msgstr "vuit"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:71
|
||||
#: contrib/humanize/templatetags/humanize.py:74
|
||||
msgid "nine"
|
||||
msgstr "nou"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:90
|
||||
#: contrib/humanize/templatetags/humanize.py:94
|
||||
msgid "today"
|
||||
msgstr "avui"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:92
|
||||
#: contrib/humanize/templatetags/humanize.py:96
|
||||
msgid "tomorrow"
|
||||
msgstr "demà"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:94
|
||||
#: contrib/humanize/templatetags/humanize.py:98
|
||||
msgid "yesterday"
|
||||
msgstr "ahir"
|
||||
|
||||
@ -3024,6 +3024,50 @@ msgstr ""
|
||||
"Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format "
|
||||
"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
|
||||
msgid "redirect from"
|
||||
msgstr "redreçar des de"
|
||||
@ -3056,23 +3100,23 @@ msgstr "redreçament"
|
||||
msgid "redirects"
|
||||
msgstr "redreçaments"
|
||||
|
||||
#: contrib/sessions/models.py:80
|
||||
#: contrib/sessions/models.py:46
|
||||
msgid "session key"
|
||||
msgstr "clau de la sessió"
|
||||
|
||||
#: contrib/sessions/models.py:81
|
||||
#: contrib/sessions/models.py:47
|
||||
msgid "session data"
|
||||
msgstr "dades de la sessió"
|
||||
|
||||
#: contrib/sessions/models.py:82
|
||||
#: contrib/sessions/models.py:48
|
||||
msgid "expire date"
|
||||
msgstr "data de caducitat"
|
||||
|
||||
#: contrib/sessions/models.py:87
|
||||
#: contrib/sessions/models.py:53
|
||||
msgid "session"
|
||||
msgstr "sessió"
|
||||
|
||||
#: contrib/sessions/models.py:88
|
||||
#: contrib/sessions/models.py:54
|
||||
msgid "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."
|
||||
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."
|
||||
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."
|
||||
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."
|
||||
msgstr "Introdueixi una adreça de correu vàlida."
|
||||
|
||||
#: core/validators.py:182 core/validators.py:474 newforms/fields.py:438
|
||||
#: oldforms/__init__.py:686
|
||||
#: core/validators.py:182 core/validators.py:474 newforms/fields.py:432
|
||||
#: oldforms/__init__.py:687
|
||||
msgid "No file was submitted. Check the encoding type on the form."
|
||||
msgstr ""
|
||||
"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 ""
|
||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||
"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: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."
|
||||
msgstr "Aquest camp és obligatori."
|
||||
|
||||
@ -3484,150 +3528,150 @@ msgstr[1] ""
|
||||
msgid "Enter a valid value."
|
||||
msgstr "Introdueixi un valor vàlid."
|
||||
|
||||
#: newforms/fields.py:129
|
||||
#: newforms/fields.py:123
|
||||
#, python-format
|
||||
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
|
||||
msgstr ""
|
||||
"Asseguris de que el valor té com a màxim %(max)d caràcters (en té %(length)"
|
||||
"d)."
|
||||
|
||||
#: newforms/fields.py:130
|
||||
#: newforms/fields.py:124
|
||||
#, python-format
|
||||
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
|
||||
msgstr ""
|
||||
"Asseguris de que el valor té com a mínim %(min)d caràcters (en té %(length)"
|
||||
"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
|
||||
msgid "Ensure this value is less than or equal to %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
|
||||
msgid "Ensure this value is greater than or equal to %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."
|
||||
msgstr "Introdueixi un número."
|
||||
|
||||
#: newforms/fields.py:218
|
||||
#: newforms/fields.py:212
|
||||
#, python-format
|
||||
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."
|
||||
|
||||
#: newforms/fields.py:219
|
||||
#: newforms/fields.py:213
|
||||
#, python-format
|
||||
msgid "Ensure that there are no more than %s decimal places."
|
||||
msgstr "Asseguris de que no hi ha més de %s decimals."
|
||||
|
||||
#: newforms/fields.py:220
|
||||
#: newforms/fields.py:214
|
||||
#, python-format
|
||||
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."
|
||||
|
||||
#: newforms/fields.py:268 newforms/fields.py:724
|
||||
#: newforms/fields.py:262 newforms/fields.py:719
|
||||
msgid "Enter a valid date."
|
||||
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."
|
||||
msgstr "Introdueixi una hora vàlida."
|
||||
|
||||
#: newforms/fields.py:340
|
||||
#: newforms/fields.py:334
|
||||
msgid "Enter a valid date/time."
|
||||
msgstr "Introdueixi una data/hora vàlides."
|
||||
|
||||
#: newforms/fields.py:439
|
||||
#: newforms/fields.py:433
|
||||
msgid "No file was submitted."
|
||||
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."
|
||||
msgstr "El fitxer enviat està buit."
|
||||
|
||||
#: newforms/fields.py:498
|
||||
#: newforms/fields.py:492
|
||||
msgid "Enter a valid URL."
|
||||
msgstr "Introdueixi una URL vàlida."
|
||||
|
||||
#: newforms/fields.py:499
|
||||
#: newforms/fields.py:493
|
||||
msgid "This URL appears to be a broken link."
|
||||
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."
|
||||
msgstr ""
|
||||
"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles."
|
||||
|
||||
#: newforms/fields.py:599
|
||||
#: newforms/fields.py:594
|
||||
#, python-format
|
||||
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."
|
||||
|
||||
#: 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."
|
||||
msgstr "Introdueixi una llista de valors."
|
||||
|
||||
#: newforms/fields.py:753
|
||||
#: newforms/fields.py:748
|
||||
msgid "Enter a valid IPv4 address."
|
||||
msgstr "Introdueixi una adreça IPv4 vàlida."
|
||||
|
||||
#: newforms/models.py:221
|
||||
#: newforms/models.py:216
|
||||
#, python-format
|
||||
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."
|
||||
|
||||
#: oldforms/__init__.py:408
|
||||
#: oldforms/__init__.py:409
|
||||
#, python-format
|
||||
msgid "Ensure your text is less than %s character."
|
||||
msgid_plural "Ensure your text is less than %s characters."
|
||||
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."
|
||||
|
||||
#: oldforms/__init__.py:413
|
||||
#: oldforms/__init__.py:414
|
||||
msgid "Line breaks are not allowed here."
|
||||
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
|
||||
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."
|
||||
|
||||
#: oldforms/__init__.py:744
|
||||
#: oldforms/__init__.py:745
|
||||
msgid "Enter a whole number between -32,768 and 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."
|
||||
msgstr "Introdueixi un número positiu."
|
||||
|
||||
#: oldforms/__init__.py:764
|
||||
#: oldforms/__init__.py:765
|
||||
msgid "Enter a whole number between 0 and 32,767."
|
||||
msgstr "Introdueixi un número entre 0 i 32,767."
|
||||
|
||||
#: template/defaultfilters.py:555
|
||||
#: template/defaultfilters.py:658
|
||||
msgid "yes,no,maybe"
|
||||
msgstr "si,no,potser"
|
||||
|
||||
#: template/defaultfilters.py:585
|
||||
#: template/defaultfilters.py:689
|
||||
#, python-format
|
||||
msgid "%(size)d byte"
|
||||
msgid_plural "%(size)d bytes"
|
||||
msgstr[0] "%(size)d byte"
|
||||
msgstr[1] "%(size)d bytes"
|
||||
|
||||
#: template/defaultfilters.py:587
|
||||
#: template/defaultfilters.py:691
|
||||
#, python-format
|
||||
msgid "%.1f KB"
|
||||
msgstr "%.1f KB"
|
||||
|
||||
#: template/defaultfilters.py:589
|
||||
#: template/defaultfilters.py:693
|
||||
#, python-format
|
||||
msgid "%.1f MB"
|
||||
msgstr "%.1f MB"
|
||||
|
||||
#: template/defaultfilters.py:590
|
||||
#: template/defaultfilters.py:694
|
||||
#, python-format
|
||||
msgid "%.1f GB"
|
||||
msgstr "%.1f GB"
|
||||
@ -3890,23 +3934,23 @@ msgstr "%(number)d %(type)s"
|
||||
msgid ", %(number)d %(type)s"
|
||||
msgstr ", %(number)d %(type)s"
|
||||
|
||||
#: utils/translation/trans_real.py:395
|
||||
#: utils/translation/trans_real.py:399
|
||||
msgid "DATE_FORMAT"
|
||||
msgstr "F j, Y"
|
||||
|
||||
#: utils/translation/trans_real.py:396
|
||||
#: utils/translation/trans_real.py:400
|
||||
msgid "DATETIME_FORMAT"
|
||||
msgstr "F j, Y, H:i"
|
||||
|
||||
#: utils/translation/trans_real.py:397
|
||||
#: utils/translation/trans_real.py:401
|
||||
msgid "TIME_FORMAT"
|
||||
msgstr "H:i"
|
||||
|
||||
#: utils/translation/trans_real.py:413
|
||||
#: utils/translation/trans_real.py:417
|
||||
msgid "YEAR_MONTH_FORMAT"
|
||||
msgstr "j de/d' F del Y"
|
||||
|
||||
#: utils/translation/trans_real.py:414
|
||||
#: utils/translation/trans_real.py:418
|
||||
msgid "MONTH_DAY_FORMAT"
|
||||
msgstr "j de/d' F del Y"
|
||||
|
||||
@ -3924,43 +3968,3 @@ msgstr "El/La %(verbose_name)s s'ha actualtzat amb èxit."
|
||||
#, python-format
|
||||
msgid "The %(verbose_name)s was deleted."
|
||||
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
|
||||
# Catalan translation for the django-admin JS files.
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Antoni Aloy <antoni.aloy@trespams.com>, 2007.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: djangojs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2007-05-20 18:25+0200\n"
|
||||
"PO-Revision-Date: 2007-06-25 17:47+0200\n"
|
||||
"Last-Translator: Marc Fargas <telenieko@telenieko.com>\n"
|
||||
"Language-Team: <es@li.org>\n"
|
||||
"PO-Revision-Date: 2007-12-01 12:06+0100\n"
|
||||
"Last-Translator: Antoni Aloy <antoni.aloy@trespams.com>\n"
|
||||
"Language-Team: español <ca@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\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"
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:33
|
||||
@ -51,8 +53,7 @@ msgstr "Deseleccionar tots"
|
||||
msgid ""
|
||||
"January February March April May June July August September October November "
|
||||
"December"
|
||||
msgstr ""
|
||||
"Febrer Març Abril Maig Juny Juliol Agost Setembre Octubre Novembre Desembre"
|
||||
msgstr "Gener Febrer Març Abril Maig Juny Juliol Agost Setembre Octubre Novembre Desembre"
|
||||
|
||||
#: contrib/admin/media/js/dateparse.js:33
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
||||
@ -117,3 +118,4 @@ msgstr "Mostrar"
|
||||
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
|
||||
msgid "Hide"
|
||||
msgstr "Ocultar"
|
||||
|
||||
|
@ -114,7 +114,7 @@ def result_headers(cl):
|
||||
yield {"text": header,
|
||||
"sortable": True,
|
||||
"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):
|
||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||
|
@ -322,7 +322,7 @@ class AnonymousUser(object):
|
||||
id = None
|
||||
username = ''
|
||||
is_staff = False
|
||||
is_active = True
|
||||
is_active = False
|
||||
is_superuser = False
|
||||
_groups = EmptyManager()
|
||||
_user_permissions = EmptyManager()
|
||||
|
@ -16,9 +16,21 @@ False
|
||||
>>> u2 = User.objects.create_user('testuser2', 'test2@example.com')
|
||||
>>> u2.has_usable_password()
|
||||
False
|
||||
|
||||
>>> u.is_authenticated()
|
||||
True
|
||||
>>> u.is_staff
|
||||
False
|
||||
>>> u.is_active
|
||||
True
|
||||
|
||||
>>> a = AnonymousUser()
|
||||
>>> a.is_authenticated()
|
||||
False
|
||||
>>> a.is_staff
|
||||
False
|
||||
>>> a.is_active
|
||||
False
|
||||
>>> a.groups.all()
|
||||
[]
|
||||
>>> a.user_permissions.all()
|
||||
|
@ -16,18 +16,18 @@ class GenericForeignKey(object):
|
||||
Provides a generic relation to any object through content-type/object-id
|
||||
fields.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, ct_field="content_type", fk_field="object_id"):
|
||||
self.ct_field = ct_field
|
||||
self.fk_field = fk_field
|
||||
|
||||
|
||||
def contribute_to_class(self, cls, name):
|
||||
# Make sure the fields exist (these raise FieldDoesNotExist,
|
||||
# Make sure the fields exist (these raise FieldDoesNotExist,
|
||||
# which is a fine error to raise here)
|
||||
self.name = name
|
||||
self.model = cls
|
||||
self.cache_attr = "_%s_cache" % name
|
||||
|
||||
|
||||
# For some reason I don't totally understand, using weakrefs here doesn't work.
|
||||
dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False)
|
||||
|
||||
@ -35,18 +35,18 @@ class GenericForeignKey(object):
|
||||
setattr(cls, name, self)
|
||||
|
||||
def instance_pre_init(self, signal, sender, args, kwargs):
|
||||
# Handle initalizing an object with the generic FK instaed of
|
||||
# content-type/object-id fields.
|
||||
# Handle initalizing an object with the generic FK instaed of
|
||||
# content-type/object-id fields.
|
||||
if self.name in kwargs:
|
||||
value = kwargs.pop(self.name)
|
||||
kwargs[self.ct_field] = self.get_content_type(value)
|
||||
kwargs[self.fk_field] = value._get_pk_val()
|
||||
|
||||
|
||||
def get_content_type(self, obj):
|
||||
# Convenience function using get_model avoids a circular import when using this model
|
||||
ContentType = get_model("contenttypes", "contenttype")
|
||||
return ContentType.objects.get_for_model(obj)
|
||||
|
||||
|
||||
def __get__(self, instance, instance_type=None):
|
||||
if instance is None:
|
||||
raise AttributeError, u"%s must be accessed via instance" % self.name
|
||||
@ -77,21 +77,21 @@ class GenericForeignKey(object):
|
||||
setattr(instance, self.ct_field, ct)
|
||||
setattr(instance, self.fk_field, fk)
|
||||
setattr(instance, self.cache_attr, value)
|
||||
|
||||
|
||||
class GenericRelation(RelatedField, Field):
|
||||
"""Provides an accessor to generic related objects (i.e. comments)"""
|
||||
|
||||
def __init__(self, to, **kwargs):
|
||||
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
|
||||
kwargs['rel'] = GenericRel(to,
|
||||
kwargs['rel'] = GenericRel(to,
|
||||
related_name=kwargs.pop('related_name', None),
|
||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||
symmetrical=kwargs.pop('symmetrical', True))
|
||||
|
||||
|
||||
# Override content-type/object-id field names on the related class
|
||||
self.object_id_field_name = kwargs.pop("object_id_field", "object_id")
|
||||
self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
|
||||
|
||||
self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
|
||||
|
||||
kwargs['blank'] = True
|
||||
kwargs['editable'] = False
|
||||
kwargs['serialize'] = False
|
||||
@ -116,9 +116,9 @@ class GenericRelation(RelatedField, Field):
|
||||
|
||||
def m2m_column_name(self):
|
||||
return self.object_id_field_name
|
||||
|
||||
|
||||
def m2m_reverse_name(self):
|
||||
return self.object_id_field_name
|
||||
return self.model._meta.pk.column
|
||||
|
||||
def contribute_to_class(self, cls, name):
|
||||
super(GenericRelation, self).contribute_to_class(cls, name)
|
||||
@ -131,13 +131,13 @@ class GenericRelation(RelatedField, Field):
|
||||
|
||||
def contribute_to_related_class(self, cls, related):
|
||||
pass
|
||||
|
||||
|
||||
def set_attributes_from_rel(self):
|
||||
pass
|
||||
|
||||
def get_internal_type(self):
|
||||
return "ManyToManyField"
|
||||
|
||||
|
||||
class ReverseGenericRelatedObjectsDescriptor(object):
|
||||
"""
|
||||
This class provides the functionality that makes the related-object
|
||||
@ -193,12 +193,12 @@ def create_generic_related_manager(superclass):
|
||||
Factory function for a manager that subclasses 'superclass' (which is a
|
||||
Manager) and adds behavior for generic related objects.
|
||||
"""
|
||||
|
||||
|
||||
class GenericRelatedObjectManager(superclass):
|
||||
def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None,
|
||||
join_table=None, source_col_name=None, target_col_name=None, content_type=None,
|
||||
content_type_field_name=None, object_id_field_name=None):
|
||||
|
||||
|
||||
super(GenericRelatedObjectManager, self).__init__()
|
||||
self.core_filters = core_filters or {}
|
||||
self.model = model
|
||||
@ -212,10 +212,10 @@ def create_generic_related_manager(superclass):
|
||||
self.content_type_field_name = content_type_field_name
|
||||
self.object_id_field_name = object_id_field_name
|
||||
self.pk_val = self.instance._get_pk_val()
|
||||
|
||||
|
||||
def get_query_set(self):
|
||||
query = {
|
||||
'%s__pk' % self.content_type_field_name : self.content_type.id,
|
||||
'%s__pk' % self.content_type_field_name : self.content_type.id,
|
||||
'%s__exact' % self.object_id_field_name : self.pk_val,
|
||||
}
|
||||
return superclass.get_query_set(self).filter(**query)
|
||||
|
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
|
||||
"""
|
||||
|
||||
from django.newforms.fields import RegexField
|
||||
from django.newforms.fields import RegexField, Select
|
||||
from django.utils.translation import ugettext
|
||||
|
||||
class UKPostcodeField(RegexField):
|
||||
@ -17,3 +17,19 @@ class UKPostcodeField(RegexField):
|
||||
max_length=None, min_length=None,
|
||||
error_message=ugettext(u'Enter a postcode. A space is required between the two postcode parts.'),
|
||||
*args, **kwargs)
|
||||
|
||||
class UKCountySelect(Select):
|
||||
"""
|
||||
A Select widget that uses a list of UK Counties/Regions as its choices.
|
||||
"""
|
||||
def __init__(self, attrs=None):
|
||||
from uk_regions import UK_REGION_CHOICES
|
||||
super(UKCountySelect, self).__init__(attrs, choices=UK_REGION_CHOICES)
|
||||
|
||||
class UKNationSelect(Select):
|
||||
"""
|
||||
A Select widget that uses a list of UK Nations as its choices.
|
||||
"""
|
||||
def __init__(self, attrs=None):
|
||||
from uk_regions import UK_NATIONS_CHOICES
|
||||
super(UKNationSelect, self).__init__(attrs, choices=UK_NATIONS_CHOICES)
|
||||
|
97
django/contrib/localflavor/uk/uk_regions.py
Normal file
97
django/contrib/localflavor/uk/uk_regions.py
Normal file
@ -0,0 +1,97 @@
|
||||
"""
|
||||
Sources:
|
||||
English regions: http://www.statistics.gov.uk/geography/downloads/31_10_01_REGION_names_and_codes_12_00.xls
|
||||
Northern Ireland regions: http://en.wikipedia.org/wiki/List_of_Irish_counties_by_area
|
||||
Welsh regions: http://en.wikipedia.org/wiki/Preserved_counties_of_Wales
|
||||
Scottish regions: http://en.wikipedia.org/wiki/Regions_and_districts_of_Scotland
|
||||
"""
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
ENGLAND_REGION_CHOICES = (
|
||||
("Bedfordshire", _("Bedfordshire")),
|
||||
("Buckinghamshire", _("Buckinghamshire")),
|
||||
("Cambridgeshire", ("Cambridgeshire")),
|
||||
("Cheshire", _("Cheshire")),
|
||||
("Cornwall and Isles of Scilly", _("Cornwall and Isles of Scilly")),
|
||||
("Cumbria", _("Cumbria")),
|
||||
("Derbyshire", _("Derbyshire")),
|
||||
("Devon", _("Devon")),
|
||||
("Dorset", _("Dorset")),
|
||||
("Durham", _("Durham")),
|
||||
("East Sussex", _("East Sussex")),
|
||||
("Essex", _("Essex")),
|
||||
("Gloucestershire", _("Gloucestershire")),
|
||||
("Greater London", _("Greater London")),
|
||||
("Greater Manchester", _("Greater Manchester")),
|
||||
("Hampshire", _("Hampshire")),
|
||||
("Hertfordshire", _("Hertfordshire")),
|
||||
("Kent", _("Kent")),
|
||||
("Lancashire", _("Lancashire")),
|
||||
("Leicestershire", _("Leicestershire")),
|
||||
("Lincolnshire", _("Lincolnshire")),
|
||||
("Merseyside", _("Merseyside")),
|
||||
("Norfolk", _("Norfolk")),
|
||||
("North Yorkshire", _("North Yorkshire")),
|
||||
("Northamptonshire", _("Northamptonshire")),
|
||||
("Northumberland", _("Northumberland")),
|
||||
("Nottinghamshire", _("Nottinghamshire")),
|
||||
("Oxfordshire", _("Oxfordshire")),
|
||||
("Shropshire", _("Shropshire")),
|
||||
("Somerset", _("Somerset")),
|
||||
("South Yorkshire", _("South Yorkshire")),
|
||||
("Staffordshire", _("Staffordshire")),
|
||||
("Suffolk", _("Suffolk")),
|
||||
("Surrey", _("Surrey")),
|
||||
("Tyne and Wear", _("Tyne and Wear")),
|
||||
("Warwickshire", _("Warwickshire")),
|
||||
("West Midlands", _("West Midlands")),
|
||||
("West Sussex", _("West Sussex")),
|
||||
("West Yorkshire", _("West Yorkshire")),
|
||||
("Wiltshire", _("Wiltshire")),
|
||||
("Worcestershire", _("Worcestershire")),
|
||||
)
|
||||
|
||||
NORTHERN_IRELAND_REGION_CHOICES = (
|
||||
("County Antrim", _("County Antrim")),
|
||||
("County Armagh", _("County Armagh")),
|
||||
("County Down", _("County Down")),
|
||||
("County Fermanagh", _("County Down")),
|
||||
("County Londonderry", _("County Londonderry")),
|
||||
("County Tyrone", _("County Tyrone")),
|
||||
)
|
||||
|
||||
WALES_REGION_CHOICES = (
|
||||
("Clwyd", _("Clwyd")),
|
||||
("Dyfed", _("Dyfed")),
|
||||
("Gwent", _("Gwent")),
|
||||
("Gwynedd", _("Gwynedd")),
|
||||
("Mid Glamorgan", _("Mid Glamorgan")),
|
||||
("Powys", _("Powys")),
|
||||
("South Glamorgan", _("South Glamorgan")),
|
||||
("West Glamorgan", _("West Glamorgan")),
|
||||
)
|
||||
|
||||
SCOTTISH_REGION_CHOICES = (
|
||||
("Borders", _("Borders")),
|
||||
("Central Scotland", _("Central Scotland")),
|
||||
("Dumfries and Galloway", _("Dumfries and Galloway")),
|
||||
("Fife", _("Fife")),
|
||||
("Grampian", _("Grampian")),
|
||||
("Highland", _("Highland")),
|
||||
("Lothian", _("Lothian")),
|
||||
("Orkney Islands", _("Orkney Islands")),
|
||||
("Shetland Islands", _("Shetland Islands")),
|
||||
("Strathclyde", _("Strathclyde")),
|
||||
("Tayside", _("Tayside")),
|
||||
("Western Isles", _("Western Isles")),
|
||||
)
|
||||
|
||||
UK_NATIONS_CHOICES = (
|
||||
("England", _("England")),
|
||||
("Northern Ireland", _("Northern Ireland")),
|
||||
("Scotland", _("Scotland")),
|
||||
("Wales", _("Wales")),
|
||||
)
|
||||
|
||||
UK_REGION_CHOICES = ENGLAND_REGION_CHOICES + NORTHERN_IRELAND_REGION_CHOICES + WALES_REGION_CHOICES + SCOTTISH_REGION_CHOICES
|
||||
|
0
django/contrib/localflavor/za/__init__.py
Normal file
0
django/contrib/localflavor/za/__init__.py
Normal file
57
django/contrib/localflavor/za/forms.py
Normal file
57
django/contrib/localflavor/za/forms.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""
|
||||
South Africa-specific Form helpers
|
||||
"""
|
||||
|
||||
from django.newforms import ValidationError
|
||||
from django.newforms.fields import Field, RegexField, EMPTY_VALUES
|
||||
from django.utils.checksums import luhn
|
||||
from django.utils.translation import gettext as _
|
||||
import re
|
||||
from datetime import date
|
||||
|
||||
id_re = re.compile(r'^(?P<yy>\d\d)(?P<mm>\d\d)(?P<dd>\d\d)(?P<mid>\d{4})(?P<end>\d{3})')
|
||||
|
||||
class ZAIDField(Field):
|
||||
"""A form field for South African ID numbers -- the checksum is validated
|
||||
using the Luhn checksum, and uses a simlistic (read: not entirely accurate)
|
||||
check for the birthdate
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ZAIDField, self).__init__()
|
||||
self.error_message = _(u'Enter a valid South African ID number')
|
||||
|
||||
def clean(self, value):
|
||||
# strip spaces and dashes
|
||||
value = value.strip().replace(' ', '').replace('-', '')
|
||||
|
||||
super(ZAIDField, self).clean(value)
|
||||
|
||||
if value in EMPTY_VALUES:
|
||||
return u''
|
||||
|
||||
match = re.match(id_re, value)
|
||||
|
||||
if not match:
|
||||
raise ValidationError(self.error_message)
|
||||
|
||||
g = match.groupdict()
|
||||
|
||||
try:
|
||||
# The year 2000 is conveniently a leapyear.
|
||||
# This algorithm will break in xx00 years which aren't leap years
|
||||
# There is no way to guess the century of a ZA ID number
|
||||
d = date(int(g['yy']) + 2000, int(g['mm']), int(g['dd']))
|
||||
except ValueError:
|
||||
raise ValidationError(self.error_message)
|
||||
|
||||
if not luhn(value):
|
||||
raise ValidationError(self.error_message)
|
||||
|
||||
return value
|
||||
|
||||
class ZAPostCodeField(RegexField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ZAPostCodeField, self).__init__(r'^\d{4}$',
|
||||
max_length=None, min_length=None,
|
||||
error_message=_(u'Enter a valid South African postal code'))
|
13
django/contrib/localflavor/za/za_provinces.py
Normal file
13
django/contrib/localflavor/za/za_provinces.py
Normal file
@ -0,0 +1,13 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
PROVINCE_CHOICES = (
|
||||
('EC', _('Eastern Cape')),
|
||||
('FS', _('Free State')),
|
||||
('GP', _('Gauteng')),
|
||||
('KN', _('KwaZulu-Natal')),
|
||||
('LP', _('Limpopo')),
|
||||
('MP', _('Mpumalanga')),
|
||||
('NC', _('Northern Cape')),
|
||||
('NW', _('North West')),
|
||||
('WC', _('Western Cape')),
|
||||
)
|
@ -32,7 +32,23 @@ def textile(value):
|
||||
return mark_safe(force_unicode(textile.textile(smart_str(value), encoding='utf-8', output='utf-8')))
|
||||
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:
|
||||
import markdown
|
||||
except ImportError:
|
||||
@ -40,7 +56,18 @@ def markdown(value):
|
||||
raise template.TemplateSyntaxError, "Error in {% markdown %} filter: The Python markdown library isn't installed."
|
||||
return force_unicode(value)
|
||||
else:
|
||||
return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
|
||||
# markdown.version was first added in 1.6b. The only version of markdown
|
||||
# to fully support extensions before 1.6b was the shortlived 1.6a.
|
||||
if hasattr(markdown, 'version'):
|
||||
extensions = [e for e in arg.split(",") if e]
|
||||
if len(extensions) > 0 and extensions[0] == "safe":
|
||||
extensions = extensions[1:]
|
||||
safe_mode = True
|
||||
else:
|
||||
safe_mode = False
|
||||
return mark_safe(force_unicode(markdown.markdown(smart_str(value), extensions, safe_mode=safe_mode)))
|
||||
else:
|
||||
return mark_safe(force_unicode(markdown.markdown(smart_str(value))))
|
||||
markdown.is_safe = True
|
||||
|
||||
def restructuredtext(value):
|
||||
|
@ -61,8 +61,15 @@ Paragraph 2 with a link_
|
||||
t = Template("{{ rest_content|restructuredtext }}")
|
||||
rendered = t.render(Context(locals())).strip()
|
||||
if docutils:
|
||||
self.assertEqual(rendered, """<p>Paragraph 1</p>
|
||||
# Different versions of docutils return slightly different HTML
|
||||
try:
|
||||
# Docutils v0.4 and earlier
|
||||
self.assertEqual(rendered, """<p>Paragraph 1</p>
|
||||
<p>Paragraph 2 with a <a class="reference" href="http://www.example.com/">link</a></p>""")
|
||||
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:
|
||||
self.assertEqual(rendered, rest_content)
|
||||
|
||||
|
@ -51,6 +51,14 @@ class SessionBase(object):
|
||||
self.modified = self.modified or key in self._session
|
||||
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):
|
||||
self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE
|
||||
|
||||
|
@ -1,14 +1,23 @@
|
||||
import os
|
||||
import tempfile
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.base import SessionBase
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
|
||||
|
||||
class SessionStore(SessionBase):
|
||||
"""
|
||||
Implements a file based session store.
|
||||
"""
|
||||
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
|
||||
super(SessionStore, self).__init__(session_key)
|
||||
|
||||
|
@ -18,40 +18,6 @@ class SessionManager(models.Manager):
|
||||
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
|
||||
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):
|
||||
s = self.model(session_key, self.encode(session_dict), expire_date)
|
||||
if session_dict:
|
||||
|
@ -1,5 +1,6 @@
|
||||
r"""
|
||||
|
||||
>>> from django.conf import settings
|
||||
>>> 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.file import SessionStore as FileSession
|
||||
@ -39,6 +40,13 @@ True
|
||||
>>> file_session.exists(file_session.session_key)
|
||||
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.modified
|
||||
False
|
||||
@ -66,6 +74,11 @@ False
|
||||
>>> s.accessed, s.modified
|
||||
(True, False)
|
||||
|
||||
>>> s.setdefault('foo', 'bar')
|
||||
'bar'
|
||||
>>> s.setdefault('foo', 'baz')
|
||||
'bar'
|
||||
|
||||
>>> s.accessed = False # Reset the accessed flag
|
||||
|
||||
>>> 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 = {
|
||||
# name for use in settings file --> name of module in "backends" directory
|
||||
'memcached': 'memcached',
|
||||
'simple': 'simple',
|
||||
'locmem': 'locmem',
|
||||
'file': 'filebased',
|
||||
'db': 'db',
|
||||
'dummy': 'dummy',
|
||||
}
|
||||
|
||||
DEPRECATED_BACKENDS = {
|
||||
# deprecated backend --> replacement module
|
||||
'simple': 'locmem',
|
||||
}
|
||||
|
||||
def get_cache(backend_uri):
|
||||
if backend_uri.find(':') == -1:
|
||||
raise InvalidCacheBackendError, "Backend URI must start with scheme://"
|
||||
scheme, rest = backend_uri.split(':', 1)
|
||||
if not rest.startswith('//'):
|
||||
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:
|
||||
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"
|
||||
|
||||
from django.core.cache.backends.simple import CacheClass as SimpleCacheClass
|
||||
from django.utils.http import urlquote_plus
|
||||
import md5
|
||||
import os, time
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
from django.core.cache.backends.base import BaseCache
|
||||
|
||||
class CacheClass(SimpleCacheClass):
|
||||
class CacheClass(BaseCache):
|
||||
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
|
||||
if not os.path.exists(self._dir):
|
||||
self._createdir()
|
||||
SimpleCacheClass.__init__(self, dir, params)
|
||||
del self._cache
|
||||
del self._expire_info
|
||||
|
||||
def add(self, key, value, timeout=None):
|
||||
fname = self._key_to_file(key)
|
||||
if timeout is None:
|
||||
timeout = self.default_timeout
|
||||
try:
|
||||
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
|
||||
if self.has_key(key):
|
||||
return None
|
||||
|
||||
self.set(key, value, timeout)
|
||||
|
||||
def get(self, key, default=None):
|
||||
fname = self._key_to_file(key)
|
||||
@ -45,7 +42,7 @@ class CacheClass(SimpleCacheClass):
|
||||
now = time.time()
|
||||
if exp < now:
|
||||
f.close()
|
||||
os.remove(fname)
|
||||
self._delete(fname)
|
||||
else:
|
||||
return pickle.load(f)
|
||||
except (IOError, OSError, EOFError, pickle.PickleError):
|
||||
@ -54,40 +51,74 @@ class CacheClass(SimpleCacheClass):
|
||||
|
||||
def set(self, key, value, timeout=None):
|
||||
fname = self._key_to_file(key)
|
||||
dirname = os.path.dirname(fname)
|
||||
|
||||
if timeout is None:
|
||||
timeout = self.default_timeout
|
||||
|
||||
self._cull()
|
||||
|
||||
try:
|
||||
filelist = os.listdir(self._dir)
|
||||
except (IOError, OSError):
|
||||
self._createdir()
|
||||
filelist = []
|
||||
if len(filelist) > self._max_entries:
|
||||
self._cull(filelist)
|
||||
try:
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
|
||||
f = open(fname, 'wb')
|
||||
now = time.time()
|
||||
pickle.dump(now + timeout, f, 2)
|
||||
pickle.dump(value, f, 2)
|
||||
pickle.dump(now + timeout, f, pickle.HIGHEST_PROTOCOL)
|
||||
pickle.dump(value, f, pickle.HIGHEST_PROTOCOL)
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
|
||||
def delete(self, key):
|
||||
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):
|
||||
pass
|
||||
|
||||
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, filelist):
|
||||
def _cull(self):
|
||||
if int(self._num_entries) < self._max_entries:
|
||||
return
|
||||
|
||||
try:
|
||||
filelist = os.listdir(self._dir)
|
||||
except (IOError, OSError):
|
||||
return
|
||||
|
||||
if self._cull_frequency == 0:
|
||||
doomed = filelist
|
||||
else:
|
||||
doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0]
|
||||
for fname in doomed:
|
||||
doomed = [os.path.join(self._dir, k) for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0]
|
||||
|
||||
for topdir in doomed:
|
||||
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):
|
||||
pass
|
||||
|
||||
@ -98,4 +129,22 @@ class CacheClass(SimpleCacheClass):
|
||||
raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir
|
||||
|
||||
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)
|
||||
|
||||
|
105
django/core/cache/backends/locmem.py
vendored
105
django/core/cache/backends/locmem.py
vendored
@ -6,65 +6,122 @@ try:
|
||||
except ImportError:
|
||||
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
|
||||
|
||||
class CacheClass(SimpleCacheClass):
|
||||
def __init__(self, host, params):
|
||||
SimpleCacheClass.__init__(self, host, params)
|
||||
class CacheClass(BaseCache):
|
||||
def __init__(self, _, 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()
|
||||
|
||||
def add(self, key, value, timeout=None):
|
||||
self._lock.writer_enters()
|
||||
# Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
|
||||
try:
|
||||
try:
|
||||
super(CacheClass, self).add(key, pickle.dumps(value), timeout)
|
||||
except pickle.PickleError:
|
||||
pass
|
||||
exp = self._expire_info.get(key)
|
||||
if exp is None or exp <= time.time():
|
||||
try:
|
||||
self._set(key, pickle.dumps(value), timeout)
|
||||
except pickle.PickleError:
|
||||
pass
|
||||
finally:
|
||||
self._lock.writer_leaves()
|
||||
|
||||
def get(self, key, default=None):
|
||||
should_delete = False
|
||||
self._lock.reader_enters()
|
||||
try:
|
||||
now = time.time()
|
||||
exp = self._expire_info.get(key)
|
||||
if exp is None:
|
||||
return default
|
||||
elif exp < now:
|
||||
should_delete = True
|
||||
else:
|
||||
elif exp > time.time():
|
||||
try:
|
||||
return pickle.loads(self._cache[key])
|
||||
except pickle.PickleError:
|
||||
return default
|
||||
finally:
|
||||
self._lock.reader_leaves()
|
||||
if should_delete:
|
||||
self._lock.writer_enters()
|
||||
try:
|
||||
del self._cache[key]
|
||||
del self._expire_info[key]
|
||||
return default
|
||||
finally:
|
||||
self._lock.writer_leaves()
|
||||
self._lock.writer_enters()
|
||||
try:
|
||||
del self._cache[key]
|
||||
del self._expire_info[key]
|
||||
return default
|
||||
finally:
|
||||
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):
|
||||
self._lock.writer_enters()
|
||||
# Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
|
||||
try:
|
||||
try:
|
||||
super(CacheClass, self).set(key, pickle.dumps(value), timeout)
|
||||
self._set(key, pickle.dumps(value), timeout)
|
||||
except pickle.PickleError:
|
||||
pass
|
||||
finally:
|
||||
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):
|
||||
self._lock.writer_enters()
|
||||
try:
|
||||
SimpleCacheClass.delete(self, key)
|
||||
self._delete(key)
|
||||
finally:
|
||||
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"
|
||||
silent_variable_failure = True
|
||||
|
||||
class MultipleObjectsReturned(Exception):
|
||||
"The query returned multiple objects when only one was expected."
|
||||
pass
|
||||
|
||||
class SuspiciousOperation(Exception):
|
||||
"The user did something suspicious"
|
||||
pass
|
||||
|
@ -1,7 +1,8 @@
|
||||
import sys
|
||||
|
||||
from django import http
|
||||
from django.core import signals
|
||||
from django.dispatch import dispatcher
|
||||
from django import http
|
||||
import sys
|
||||
|
||||
class BaseHandler(object):
|
||||
# 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.handlers.base import BaseHandler
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils import datastructures
|
||||
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
|
||||
# settings) until after ModPythonHandler has been called; otherwise os.environ
|
||||
|
@ -5,12 +5,12 @@ try:
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
from django.core.handlers.base import BaseHandler
|
||||
from django import http
|
||||
from django.core import signals
|
||||
from django.core.handlers.base import BaseHandler
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils import datastructures
|
||||
from django.utils.encoding import force_unicode
|
||||
from django import http
|
||||
|
||||
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
STATUS_CODE_TEXT = {
|
||||
|
@ -10,7 +10,7 @@ from django.core.management.base import BaseCommand, CommandError, handle_defaul
|
||||
get_version = django.get_version
|
||||
|
||||
# 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
|
||||
|
||||
def find_commands(management_dir):
|
||||
@ -29,8 +29,8 @@ def find_commands(management_dir):
|
||||
|
||||
def find_management_module(app_name):
|
||||
"""
|
||||
Determines the path to the management module for the application named,
|
||||
without acutally importing the application or the management module.
|
||||
Determines the path to the management module for the given app_name,
|
||||
without actually importing the application or the management module.
|
||||
|
||||
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):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
return getattr(__import__('%s.management.commands.%s' % (app_name, name),
|
||||
{}, {}, ['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
|
||||
those commands can be found. This works by looking for a
|
||||
management.commands package in django.core, and in each installed
|
||||
application -- if a commands package exists, all commands in that
|
||||
package are registered.
|
||||
Returns a dictionary mapping command names to their callback applications.
|
||||
|
||||
This works by looking for a management.commands package in django.core, and
|
||||
in each installed application -- if a commands package exists, all commands
|
||||
in that package are registered.
|
||||
|
||||
Core commands are always included. If a settings module has been
|
||||
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
|
||||
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.
|
||||
"""
|
||||
global _commands
|
||||
if _commands is None:
|
||||
_commands = dict([(name, 'django.core')
|
||||
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 = []
|
||||
_commands = dict([(name, 'django.core') for name in find_commands(__path__[0])])
|
||||
|
||||
for app_name in apps:
|
||||
try:
|
||||
path = find_management_module(app_name)
|
||||
_commands.update(dict([(name, app_name)
|
||||
for name in find_commands(path)]))
|
||||
except ImportError:
|
||||
pass # No management module - ignore this app
|
||||
|
||||
# Try to determine the project directory
|
||||
try:
|
||||
if load_user_commands:
|
||||
# Get commands from all installed apps.
|
||||
from django.conf import settings
|
||||
project_directory = setup_environ(__import__(settings.SETTINGS_MODULE))
|
||||
except (AttributeError, EnvironmentError, ImportError):
|
||||
project_directory = None
|
||||
for app_name in settings.INSTALLED_APPS:
|
||||
try:
|
||||
path = find_management_module(app_name)
|
||||
_commands.update(dict([(name, app_name) for name in find_commands(path)]))
|
||||
except ImportError:
|
||||
pass # No management module -- ignore this app.
|
||||
|
||||
if project_directory:
|
||||
# Remove the "startproject" command from self.commands, because
|
||||
@ -157,18 +145,18 @@ class ManagementUtility(object):
|
||||
def __init__(self, argv=None):
|
||||
self.argv = argv or sys.argv[:]
|
||||
self.prog_name = os.path.basename(self.argv[0])
|
||||
self.project_directory = None
|
||||
self.user_commands = False
|
||||
|
||||
def main_help_text(self):
|
||||
"""
|
||||
Returns the script's main help text, as a string.
|
||||
"""
|
||||
usage = ['%s <subcommand> [options] [args]' % self.prog_name]
|
||||
usage.append('Django command line tool,'
|
||||
' version %s' % django.get_version())
|
||||
usage.append("Type '%s help <subcommand>' for help on a specific"
|
||||
" subcommand." % self.prog_name)
|
||||
usage.append('Django command line tool, version %s' % django.get_version())
|
||||
usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name)
|
||||
usage.append('Available subcommands:')
|
||||
commands = get_commands().keys()
|
||||
commands = get_commands(self.user_commands, self.project_directory).keys()
|
||||
commands.sort()
|
||||
for cmd in commands:
|
||||
usage.append(' %s' % cmd)
|
||||
@ -178,18 +166,18 @@ class ManagementUtility(object):
|
||||
"""
|
||||
Tries to fetch the given subcommand, printing a message with the
|
||||
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:
|
||||
app_name = get_commands()[subcommand]
|
||||
app_name = get_commands(self.user_commands, self.project_directory)[subcommand]
|
||||
if isinstance(app_name, BaseCommand):
|
||||
# If the command is already loaded, use it directly.
|
||||
klass = app_name
|
||||
else:
|
||||
klass = load_command_class(app_name, subcommand)
|
||||
except KeyError:
|
||||
sys.stderr.write("Unknown command: %r\nType '%s help' for"
|
||||
" usage.\n" % (subcommand, self.prog_name))
|
||||
sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
|
||||
(subcommand, self.prog_name))
|
||||
sys.exit(1)
|
||||
return klass
|
||||
|
||||
@ -201,8 +189,7 @@ class ManagementUtility(object):
|
||||
# Preprocess options to extract --settings and --pythonpath.
|
||||
# These options could affect the commands that are available, so they
|
||||
# must be processed early.
|
||||
parser = LaxOptionParser(version=get_version(),
|
||||
option_list=BaseCommand.option_list)
|
||||
parser = LaxOptionParser(version=get_version(), option_list=BaseCommand.option_list)
|
||||
try:
|
||||
options, args = parser.parse_args(self.argv)
|
||||
handle_default_options(options)
|
||||
@ -242,6 +229,8 @@ class ProjectManagementUtility(ManagementUtility):
|
||||
"""
|
||||
def __init__(self, argv, project_directory):
|
||||
super(ProjectManagementUtility, self).__init__(argv)
|
||||
self.project_directory = project_directory
|
||||
self.user_commands = True
|
||||
|
||||
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
|
||||
# "myproject", this code would add "/path/to/myproject" to sys.path.
|
||||
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)
|
||||
settings_name = os.path.splitext(settings_filename)[0]
|
||||
sys.path.append(os.path.join(project_directory, os.pardir))
|
||||
@ -261,8 +252,7 @@ def setup_environ(settings_mod):
|
||||
sys.path.pop()
|
||||
|
||||
# Set DJANGO_SETTINGS_MODULE appropriately.
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name,
|
||||
settings_name)
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
|
||||
return project_directory
|
||||
|
||||
def execute_from_command_line(argv=None):
|
||||
|
@ -58,7 +58,7 @@ class Command(BaseCommand):
|
||||
else:
|
||||
formats = []
|
||||
|
||||
if verbosity > 0:
|
||||
if verbosity > 2:
|
||||
if formats:
|
||||
print "Loading '%s' fixtures..." % fixture_name
|
||||
else:
|
||||
@ -106,7 +106,7 @@ class Command(BaseCommand):
|
||||
return
|
||||
fixture.close()
|
||||
except:
|
||||
if verbosity > 1:
|
||||
if verbosity > 2:
|
||||
print "No %s fixture '%s' in %s." % \
|
||||
(format, fixture_name, humanize(fixture_dir))
|
||||
|
||||
@ -122,7 +122,7 @@ class Command(BaseCommand):
|
||||
transaction.leave_transaction_management()
|
||||
|
||||
if count[0] == 0:
|
||||
if verbosity > 0:
|
||||
if verbosity > 2:
|
||||
print "No fixtures found."
|
||||
else:
|
||||
if verbosity > 0:
|
||||
|
@ -33,8 +33,9 @@ class Command(NoArgsCommand):
|
||||
for app_name in settings.INSTALLED_APPS:
|
||||
try:
|
||||
__import__(app_name + '.management', {}, {}, [''])
|
||||
except ImportError:
|
||||
pass
|
||||
except ImportError, exc:
|
||||
if not exc.args[0].startswith('No module named management'):
|
||||
raise
|
||||
|
||||
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
|
||||
# existing fields, m2m fields, m2m related objects or related objects
|
||||
if f.rel:
|
||||
rel_opts = f.rel.to._meta
|
||||
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_query_name = f.related_query_name()
|
||||
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):
|
||||
# Check to see if the related m2m field will clash with any
|
||||
# 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():
|
||||
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_query_name = f.related_query_name()
|
||||
# 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
|
||||
from django.db import models
|
||||
from django.core.serializers.python import Serializer as PythonSerializer
|
||||
from django.core.serializers.python import Deserializer as PythonDeserializer
|
||||
try:
|
||||
@ -17,10 +18,23 @@ class Serializer(PythonSerializer):
|
||||
"""
|
||||
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):
|
||||
self.options.pop('stream', 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):
|
||||
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.
|
||||
|
||||
def _import_database_module(import_path='', module_name=''):
|
||||
"""Lazyily import a database module when requested."""
|
||||
return __import__('%s%s.%s' % (_import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
|
||||
"""Lazily import a database module when requested."""
|
||||
return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
|
||||
|
||||
# We don't want to import the introspect/creation modules unless
|
||||
# someone asks for 'em, so lazily load them on demmand.
|
||||
|
@ -47,7 +47,8 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||
BEGIN
|
||||
SELECT %(sq_name)s.nextval
|
||||
INTO :new.%(col_name)s FROM dual;
|
||||
END;/""" % locals()
|
||||
END;
|
||||
/""" % locals()
|
||||
return sequence_sql, trigger_sql
|
||||
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
@ -445,24 +446,31 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
charset = 'utf-8'
|
||||
|
||||
def _format_params(self, params):
|
||||
sz_kwargs = {}
|
||||
if isinstance(params, dict):
|
||||
result = {}
|
||||
charset = self.charset
|
||||
for key, value in params.items():
|
||||
result[smart_str(key, charset)] = smart_str(value, charset)
|
||||
if hasattr(value, 'oracle_type'): sz_kwargs[key] = value.oracle_type()
|
||||
return result
|
||||
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()
|
||||
return tuple([smart_str(p, self.charset, True) for p in params])
|
||||
|
||||
# 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
|
||||
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):
|
||||
if params is None:
|
||||
@ -477,11 +485,12 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
if query.endswith(';') or query.endswith('/'):
|
||||
query = query[:-1]
|
||||
query = smart_str(query, self.charset) % tuple(args)
|
||||
self._guess_input_sizes([params])
|
||||
return Database.Cursor.execute(self, query, params)
|
||||
|
||||
def executemany(self, query, params=None):
|
||||
try:
|
||||
args = [(':arg%d' % i) for i in range(len(params[0]))]
|
||||
args = [(':arg%d' % i) for i in range(len(params[0]))]
|
||||
except (IndexError, TypeError):
|
||||
# No params given, nothing to do
|
||||
return None
|
||||
@ -493,6 +502,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
query = query[:-1]
|
||||
query = smart_str(query, self.charset) % tuple(args)
|
||||
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)
|
||||
|
||||
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.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
|
||||
from django.utils.safestring import SafeUnicode
|
||||
try:
|
||||
import psycopg2 as Database
|
||||
import psycopg2.extensions
|
||||
@ -17,6 +18,7 @@ DatabaseError = Database.DatabaseError
|
||||
IntegrityError = Database.IntegrityError
|
||||
|
||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||
psycopg2.extensions.register_adapter(SafeUnicode, psycopg2.extensions.QuotedString)
|
||||
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
needs_datetime_string_cast = False
|
||||
|
@ -1,7 +1,7 @@
|
||||
import django.db.models.manipulators
|
||||
import django.db.models.manager
|
||||
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.related import OneToOneRel, ManyToOneRel
|
||||
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.add_to_class('_meta', Options(attrs.pop('Meta', None)))
|
||||
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
|
||||
for base in bases:
|
||||
|
@ -104,7 +104,7 @@ class Field(object):
|
||||
self.radio_admin = radio_admin
|
||||
self.help_text = help_text
|
||||
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.
|
||||
self.db_index = db_index
|
||||
|
@ -29,7 +29,7 @@ class Options(object):
|
||||
self.object_name, self.app_label = None, None
|
||||
self.get_latest_by = None
|
||||
self.order_with_respect_to = None
|
||||
self.db_tablespace = None
|
||||
self.db_tablespace = settings.DEFAULT_TABLESPACE
|
||||
self.admin = None
|
||||
self.meta = meta
|
||||
self.pk = None
|
||||
@ -152,7 +152,7 @@ class Options(object):
|
||||
rel_objs = []
|
||||
for klass in get_models():
|
||||
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))
|
||||
self._all_related_objects = rel_objs
|
||||
return rel_objs
|
||||
@ -186,7 +186,7 @@ class Options(object):
|
||||
rel_objs = []
|
||||
for klass in get_models():
|
||||
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))
|
||||
if app_cache_ready():
|
||||
self._all_related_many_to_many_objects = rel_objs
|
||||
|
@ -261,7 +261,8 @@ class _QuerySet(object):
|
||||
obj_list = list(clone)
|
||||
if len(obj_list) < 1:
|
||||
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]
|
||||
|
||||
def create(self, **kwargs):
|
||||
|
@ -331,7 +331,7 @@ class HttpResponse(object):
|
||||
chunk = self._iterator.next()
|
||||
if isinstance(chunk, unicode):
|
||||
chunk = chunk.encode(self._charset)
|
||||
return chunk
|
||||
return str(chunk)
|
||||
|
||||
def close(self):
|
||||
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
|
||||
# everything else, including all response middleware. Think of them as
|
||||
# "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.
|
||||
|
||||
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
|
||||
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.
|
||||
"""
|
||||
if 'Location' in response and request.get_host():
|
||||
@ -31,4 +31,3 @@ def conditional_content_removal(request, response):
|
||||
if request.method == 'HEAD':
|
||||
response.content = ''
|
||||
return response
|
||||
|
||||
|
@ -5,6 +5,7 @@ from django.conf import settings
|
||||
from django import http
|
||||
from django.core.mail import mail_managers
|
||||
from django.utils.http import urlquote
|
||||
from django.core import urlresolvers
|
||||
|
||||
class CommonMiddleware(object):
|
||||
"""
|
||||
@ -16,6 +17,12 @@ class CommonMiddleware(object):
|
||||
this middleware appends missing slashes and/or prepends missing
|
||||
"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
|
||||
the entire page content and Not Modified responses will be returned
|
||||
appropriately.
|
||||
@ -33,27 +40,48 @@ class CommonMiddleware(object):
|
||||
if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
|
||||
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()
|
||||
old_url = [host, request.path]
|
||||
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]
|
||||
# Append a slash if append_slash is set and the URL doesn't have a
|
||||
# trailing slash or a file extension.
|
||||
if settings.APPEND_SLASH and (not old_url[1].endswith('/')) and ('.' not in old_url[1].split('/')[-1]):
|
||||
new_url[1] = new_url[1] + '/'
|
||||
if settings.DEBUG and request.method == 'POST':
|
||||
raise RuntimeError, "You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to %s%s (note the trailing slash), or set APPEND_SLASH=False in your Django settings." % (new_url[0], new_url[1])
|
||||
|
||||
# Append a slash if APPEND_SLASH is set and the URL doesn't have a
|
||||
# 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] + '/'
|
||||
if settings.DEBUG and request.method == 'POST':
|
||||
raise RuntimeError, (""
|
||||
"You called this URL via POST, but the URL doesn't end "
|
||||
"in a slash and you have APPEND_SLASH set. Django can't "
|
||||
"redirect to the slash URL while maintaining POST data. "
|
||||
"Change your form to point to %s%s (note the trailing "
|
||||
"slash), or set APPEND_SLASH=False in your Django "
|
||||
"settings.") % (new_url[0], new_url[1])
|
||||
|
||||
if new_url != old_url:
|
||||
# Redirect
|
||||
if new_url[0]:
|
||||
newurl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', new_url[0], urlquote(new_url[1]))
|
||||
# Redirect if the target url exists
|
||||
try:
|
||||
urlresolvers.resolve(new_url[1])
|
||||
except urlresolvers.Resolver404:
|
||||
pass
|
||||
else:
|
||||
newurl = urlquote(new_url[1])
|
||||
if request.GET:
|
||||
newurl += '?' + request.GET.urlencode()
|
||||
return http.HttpResponsePermanentRedirect(newurl)
|
||||
if new_url[0]:
|
||||
newurl = "%s://%s%s" % (
|
||||
request.is_secure() and 'https' or 'http',
|
||||
new_url[0], urlquote(new_url[1]))
|
||||
else:
|
||||
newurl = urlquote(new_url[1])
|
||||
if request.GET:
|
||||
newurl += '?' + request.GET.urlencode()
|
||||
return http.HttpResponsePermanentRedirect(newurl)
|
||||
|
||||
return None
|
||||
|
||||
|
@ -83,21 +83,15 @@ class Field(object):
|
||||
self.creation_counter = Field.creation_counter
|
||||
Field.creation_counter += 1
|
||||
|
||||
self.error_messages = self._build_error_messages(error_messages)
|
||||
|
||||
def _build_error_messages(self, extra_error_messages):
|
||||
error_messages = {}
|
||||
|
||||
def get_default_error_messages(klass):
|
||||
def set_class_error_messages(messages, klass):
|
||||
for base_class in klass.__bases__:
|
||||
get_default_error_messages(base_class)
|
||||
if hasattr(klass, 'default_error_messages'):
|
||||
error_messages.update(klass.default_error_messages)
|
||||
set_class_error_messages(messages, base_class)
|
||||
messages.update(getattr(klass, 'default_error_messages', {}))
|
||||
|
||||
get_default_error_messages(self.__class__)
|
||||
if extra_error_messages:
|
||||
error_messages.update(extra_error_messages)
|
||||
return error_messages
|
||||
messages = {}
|
||||
set_class_error_messages(messages, self.__class__)
|
||||
messages.update(error_messages or {})
|
||||
self.error_messages = messages
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
@ -415,7 +409,7 @@ class EmailField(RegexField):
|
||||
try:
|
||||
from django.conf import settings
|
||||
URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
|
||||
except (ImportError, EnvironmentError):
|
||||
except ImportError:
|
||||
# It's OK if Django settings aren't configured.
|
||||
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||
|
||||
@ -539,8 +533,8 @@ class BooleanField(Field):
|
||||
"""Returns a Python boolean object."""
|
||||
super(BooleanField, self).clean(value)
|
||||
# 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
|
||||
# handle that explicitly).
|
||||
# will submit for False. Because bool("True") == True, we don't need to
|
||||
# handle that explicitly.
|
||||
if value == 'False':
|
||||
return False
|
||||
return bool(value)
|
||||
|
@ -3,16 +3,20 @@ Helper functions for creating Form classes from Django models
|
||||
and database field objects.
|
||||
"""
|
||||
|
||||
from warnings import warn
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import smart_unicode
|
||||
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 fields import Field, ChoiceField, EMPTY_VALUES
|
||||
from widgets import Select, SelectMultiple, MultipleHiddenInput
|
||||
|
||||
__all__ = (
|
||||
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
|
||||
'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
|
||||
'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
|
||||
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
|
||||
field_list = []
|
||||
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
|
||||
instance with the given kwargs (i.e. 'initial').
|
||||
"""
|
||||
warn("form_for_instance is deprecated, use ModelForm instead.",
|
||||
PendingDeprecationWarning,
|
||||
stacklevel=3)
|
||||
model = instance.__class__
|
||||
opts = model._meta
|
||||
field_list = []
|
||||
@ -132,6 +142,160 @@ def form_for_fields(field_list):
|
||||
for f in field_list if f.editable])
|
||||
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):
|
||||
def __init__(self, queryset, empty_label, cache_choices):
|
||||
self.queryset = queryset
|
||||
@ -142,7 +306,7 @@ class QuerySetIterator(object):
|
||||
if self.empty_label is not None:
|
||||
yield (u"", self.empty_label)
|
||||
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.
|
||||
if not self.cache_choices:
|
||||
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
|
||||
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.
|
||||
"""
|
||||
queryset = _get_queryset(klass)
|
||||
|
@ -23,12 +23,14 @@ class Context(object):
|
||||
yield d
|
||||
|
||||
def push(self):
|
||||
self.dicts = [{}] + self.dicts
|
||||
d = {}
|
||||
self.dicts = [d] + self.dicts
|
||||
return d
|
||||
|
||||
def pop(self):
|
||||
if len(self.dicts) == 1:
|
||||
raise ContextPopException
|
||||
del self.dicts[0]
|
||||
return self.dicts.pop(0)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"Set a variable in the current context"
|
||||
@ -62,6 +64,7 @@ class Context(object):
|
||||
def update(self, other_dict):
|
||||
"Like dict.update(). Pushes an entire dictionary's keys and values onto the context."
|
||||
self.dicts = [other_dict] + self.dicts
|
||||
return other_dict
|
||||
|
||||
# This is a function rather than module-level procedural code because we only
|
||||
# want it to execute if somebody uses RequestContext.
|
||||
|
@ -43,7 +43,11 @@ def stringfilter(func):
|
||||
|
||||
|
||||
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("'", "\\'")
|
||||
addslashes.is_safe = True
|
||||
addslashes = stringfilter(addslashes)
|
||||
@ -54,6 +58,25 @@ def capfirst(value):
|
||||
capfirst.is_safe=True
|
||||
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):
|
||||
"""Replaces ampersands with ``&`` entities."""
|
||||
from django.utils.html import fix_ampersands
|
||||
@ -696,7 +719,7 @@ filesizeformat.is_safe = True
|
||||
|
||||
def pluralize(value, arg=u's'):
|
||||
"""
|
||||
Returns a plural suffix if the value is not 1. By default, 's' is used as
|
||||
Returns a plural suffix if the value is not 1. By default, 's' is used as
|
||||
the suffix:
|
||||
|
||||
* If value is 0, vote{{ value|pluralize }} displays "0 votes".
|
||||
@ -766,6 +789,7 @@ register.filter(dictsort)
|
||||
register.filter(dictsortreversed)
|
||||
register.filter(divisibleby)
|
||||
register.filter(escape)
|
||||
register.filter(escapejs)
|
||||
register.filter(filesizeformat)
|
||||
register.filter(first)
|
||||
register.filter(fix_ampersands)
|
||||
|
@ -354,8 +354,19 @@ class _OutputRedirectingPdb(pdb.Pdb):
|
||||
"""
|
||||
def __init__(self, out):
|
||||
self.__out = out
|
||||
self.__debugger_used = False
|
||||
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):
|
||||
# Redirect stdout to the given stream.
|
||||
save_stdout = sys.stdout
|
||||
|
@ -1,6 +1,6 @@
|
||||
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.
|
||||
"""
|
||||
def __init__(self, *dicts):
|
||||
@ -215,7 +215,7 @@ class MultiValueDict(dict):
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""
|
||||
Returns the last data value for the passed key. If key doesn't exist
|
||||
Returns the last data value for the passed key. If key doesn't exist
|
||||
or value is an empty list, then default is returned.
|
||||
"""
|
||||
try:
|
||||
@ -228,7 +228,7 @@ class MultiValueDict(dict):
|
||||
|
||||
def getlist(self, key):
|
||||
"""
|
||||
Returns the list of values for the passed key. If key doesn't exist,
|
||||
Returns the list of values for the passed key. If key doesn't exist,
|
||||
then an empty list is returned.
|
||||
"""
|
||||
try:
|
||||
|
@ -1,4 +1,4 @@
|
||||
"HTML utilities suitable for global use."
|
||||
"""HTML utilities suitable for global use."""
|
||||
|
||||
import re
|
||||
import string
|
||||
@ -8,11 +8,11 @@ from django.utils.encoding import force_unicode
|
||||
from django.utils.functional import allow_lazy
|
||||
from django.utils.http import urlquote
|
||||
|
||||
# Configuration for urlize() function
|
||||
# Configuration for urlize() function.
|
||||
LEADING_PUNCTUATION = ['(', '<', '<']
|
||||
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', '•', '•', '•']
|
||||
|
||||
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
|
||||
|
||||
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("'", '''))
|
||||
escape = allow_lazy(escape, unicode)
|
||||
|
||||
@ -42,7 +42,7 @@ def conditional_escape(html):
|
||||
return escape(html)
|
||||
|
||||
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
|
||||
paras = re.split('\n{2,}', value)
|
||||
if autoescape:
|
||||
@ -50,31 +50,31 @@ def linebreaks(value, autoescape=False):
|
||||
else:
|
||||
paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras]
|
||||
return u'\n\n'.join(paras)
|
||||
linebreaks = allow_lazy(linebreaks, unicode)
|
||||
linebreaks = allow_lazy(linebreaks, unicode)
|
||||
|
||||
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))
|
||||
strip_tags = allow_lazy(strip_tags)
|
||||
|
||||
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))
|
||||
strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, unicode)
|
||||
|
||||
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))
|
||||
strip_entities = allow_lazy(strip_entities, unicode)
|
||||
|
||||
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))
|
||||
fix_ampersands = allow_lazy(fix_ampersands, unicode)
|
||||
|
||||
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
|
||||
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:
|
||||
middle = mark_safe(middle)
|
||||
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 = '<a href="http://%s"%s>%s</a>' % (
|
||||
urlquote(middle, safe='/&=:;#?+'), nofollow_attr,
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""
|
||||
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
|
||||
@ -15,17 +15,15 @@ def legacy_maxlength(max_length, maxlength):
|
||||
"""
|
||||
Consolidates max_length and maxlength, providing backwards compatibilty
|
||||
for the legacy "maxlength" argument.
|
||||
|
||||
If one of max_length or maxlength is given, then that value is returned.
|
||||
If both are given, a TypeError is raised.
|
||||
If maxlength is used at all, a deprecation warning is issued.
|
||||
If both are given, a TypeError is raised. If maxlength is used at all, a
|
||||
deprecation warning is issued.
|
||||
"""
|
||||
if maxlength is not None:
|
||||
warn("maxlength is deprecated, use max_length instead.",
|
||||
PendingDeprecationWarning,
|
||||
stacklevel=3)
|
||||
warn("maxlength is deprecated. Use max_length instead.", DeprecationWarning, stacklevel=3)
|
||||
if max_length is not None:
|
||||
raise TypeError("field can not take both the max_length"
|
||||
" argument and the legacy maxlength argument.")
|
||||
raise TypeError("Field cannot take both the max_length argument and the legacy maxlength argument.")
|
||||
max_length = maxlength
|
||||
return max_length
|
||||
|
||||
@ -33,7 +31,8 @@ def remove_maxlength(func):
|
||||
"""
|
||||
A decorator to be used on a class's __init__ that provides backwards
|
||||
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
|
||||
(if it exists) into a "max_length" keyword argument.
|
||||
"""
|
||||
@ -58,7 +57,6 @@ class LegacyMaxlength(type):
|
||||
Metaclass for providing backwards compatibility support for the
|
||||
"maxlength" keyword argument.
|
||||
"""
|
||||
|
||||
def __init__(cls, name, bases, attrs):
|
||||
super(LegacyMaxlength, cls).__init__(name, bases, attrs)
|
||||
# Decorate the class's __init__ to remove any maxlength keyword.
|
||||
|
@ -34,16 +34,13 @@ class SafeString(str, SafeData):
|
||||
Concatenating a safe string with another safe string or safe unicode
|
||||
object is safe. Otherwise, the result is no longer safe.
|
||||
"""
|
||||
t = super(SafeString, self).__add__(rhs)
|
||||
if isinstance(rhs, SafeUnicode):
|
||||
return SafeUnicode(self + rhs)
|
||||
return SafeUnicode(t)
|
||||
elif isinstance(rhs, SafeString):
|
||||
return SafeString(self + rhs)
|
||||
else:
|
||||
return super(SafeString, self).__add__(rhs)
|
||||
|
||||
def __str__(self):
|
||||
return self
|
||||
|
||||
return SafeString(t)
|
||||
return t
|
||||
|
||||
def _proxy_method(self, *args, **kwargs):
|
||||
"""
|
||||
Wrap a call to a normal unicode method up so that we return safe
|
||||
@ -69,11 +66,11 @@ class SafeUnicode(unicode, SafeData):
|
||||
Concatenating a safe unicode object with another safe string or safe
|
||||
unicode object is safe. Otherwise, the result is no longer safe.
|
||||
"""
|
||||
t = super(SafeUnicode, self).__add__(rhs)
|
||||
if isinstance(rhs, SafeData):
|
||||
return SafeUnicode(self + rhs)
|
||||
else:
|
||||
return super(SafeUnicode, self).__add__(rhs)
|
||||
|
||||
return SafeUnicode(t)
|
||||
return t
|
||||
|
||||
def _proxy_method(self, *args, **kwargs):
|
||||
"""
|
||||
Wrap a call to a normal unicode method up so that we return safe
|
||||
|
@ -1,9 +1,12 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import Template, Context, TemplateDoesNotExist
|
||||
from django.utils.html import escape
|
||||
from django.http import HttpResponseServerError, HttpResponseNotFound
|
||||
from django.utils.encoding import smart_unicode
|
||||
import os, re, sys
|
||||
|
||||
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:
|
||||
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')
|
||||
|
||||
from django import get_version
|
||||
t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
|
||||
c = Context({
|
||||
'exception_type': exc_type.__name__,
|
||||
@ -142,8 +145,10 @@ def technical_500_response(request, exc_type, exc_value, tb):
|
||||
'request': request,
|
||||
'request_protocol': request.is_secure() and "https" or "http",
|
||||
'settings': get_safe_settings(),
|
||||
'sys_executable' : sys.executable,
|
||||
'sys_version_info' : '%d.%d.%d' % sys.version_info[0:3],
|
||||
'sys_executable': sys.executable,
|
||||
'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_does_not_exist': template_does_not_exist,
|
||||
'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">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta name="robots" content="NONE,NOARCHIVE" />
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="NONE,NOARCHIVE">
|
||||
<title>{{ exception_type }} at {{ request.path|escape }}</title>
|
||||
<style type="text/css">
|
||||
html * { padding:0; margin:0; }
|
||||
@ -275,6 +280,8 @@ TECHNICAL_500_TEMPLATE = """
|
||||
#requestinfo h3 { margin-bottom:-1em; }
|
||||
.error { background: #ffc; }
|
||||
.specific { color:#cc3300; font-weight:bold; }
|
||||
h2 span.commands { font-size:.7em;}
|
||||
span.commands a:link {color:#5E5694;}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
//<!--
|
||||
@ -365,6 +372,10 @@ TECHNICAL_500_TEMPLATE = """
|
||||
<th>Python Version:</th>
|
||||
<td>{{ sys_version_info }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Python Path:</th>
|
||||
<td>{{ sys_path }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% if unicode_hint %}
|
||||
@ -409,9 +420,7 @@ TECHNICAL_500_TEMPLATE = """
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="traceback">
|
||||
<h2>Traceback <span>(innermost last)</span></h2>
|
||||
<div class="commands"><a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></div>
|
||||
<br/>
|
||||
<h2>Traceback <span class="commands"><a href="#" onclick="return switchPastebinFriendly(this);">Switch to copy-and-paste view</a></span></h2>
|
||||
{% autoescape off %}
|
||||
<div id="browserTraceback">
|
||||
<ul class="traceback">
|
||||
@ -456,27 +465,51 @@ TECHNICAL_500_TEMPLATE = """
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</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 %}
|
||||
<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 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">
|
||||
<html lang="en">
|
||||
<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>
|
||||
<meta name="robots" content="NONE,NOARCHIVE" />
|
||||
<meta name="robots" content="NONE,NOARCHIVE">
|
||||
<style type="text/css">
|
||||
html * { padding:0; margin:0; }
|
||||
body * { padding:10px 20px; }
|
||||
|
@ -9,7 +9,7 @@ from django.http import Http404, HttpResponse
|
||||
|
||||
def archive_index(request, queryset, date_field, num_latest=15,
|
||||
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'):
|
||||
"""
|
||||
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
|
||||
|
||||
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',
|
||||
mimetype=None):
|
||||
"""
|
||||
|
@ -58,6 +58,17 @@ See the `csrf documentation`_.
|
||||
|
||||
.. _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
|
||||
=========
|
||||
|
||||
@ -162,17 +173,6 @@ Examples (when 'today' is 17 Feb 2007):
|
||||
|
||||
.. _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
|
||||
===========
|
||||
|
||||
@ -211,6 +211,15 @@ See the `redirects documentation`_.
|
||||
|
||||
.. _redirects documentation: ../redirects/
|
||||
|
||||
sessions
|
||||
========
|
||||
|
||||
A framework for storing data in anonymous sessions.
|
||||
|
||||
See the `sessions documentation`_.
|
||||
|
||||
.. _sessions documentation: ../sessions/
|
||||
|
||||
sites
|
||||
=====
|
||||
|
||||
@ -240,6 +249,16 @@ See the `syndication documentation`_.
|
||||
|
||||
.. _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
|
||||
=============
|
||||
|
||||
|
@ -154,10 +154,13 @@ custom methods:
|
||||
|
||||
* ``get_profile()`` -- Returns a site-specific profile for this user.
|
||||
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/
|
||||
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
|
||||
.. _storing additional user information: #storing-additional-information-about-users
|
||||
|
||||
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
|
||||
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
|
||||
==============================
|
||||
|
||||
@ -337,6 +379,17 @@ This example shows how you might use both ``authenticate()`` and ``login()``::
|
||||
else:
|
||||
# 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
|
||||
-----------------------------------
|
||||
|
||||
|
@ -168,6 +168,10 @@ development or testing environments. For example::
|
||||
|
||||
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)
|
||||
-------------------------------
|
||||
|
||||
|
@ -242,7 +242,7 @@ We've got two roles here:
|
||||
* Ticket triagers: community members who keep track of tickets, making
|
||||
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
|
||||
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
|
||||
"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
|
||||
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.
|
||||
4. In some cases, a ticket might get moved to the "Someday/Maybe" state.
|
||||
This means the ticket is an enhancement request that we might consider
|
||||
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
|
||||
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
|
||||
|
||||
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``
|
||||
setting (its value is ignored; it just needs to be present) and a ``SITE_ID``
|
||||
setting (any non-zero integer value will do) in order for all the tests to pass.
|
||||
info, with the ``DATABASE_ENGINE`` setting. You'll also need a ``ROOT_URLCONF``
|
||||
setting (its value is ignored; it just needs to be present).
|
||||
|
||||
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.
|
||||
|
@ -1,5 +1,5 @@
|
||||
===================
|
||||
Custom Model Fields
|
||||
Custom model fields
|
||||
===================
|
||||
|
||||
**New in Django development version**
|
||||
@ -8,9 +8,10 @@ Introduction
|
||||
============
|
||||
|
||||
The `model reference`_ documentation explains how to use Django's standard
|
||||
field classes. For many purposes, those classes are all you'll need. Sometimes,
|
||||
though, the Django version won't meet your precise requirements, or you'll want
|
||||
to use a field that is entirely different from those shipped with Django.
|
||||
field classes -- ``CharField``, ``DateField``, etc. For many purposes, those
|
||||
classes are all you'll need. Sometimes, though, the Django version won't meet
|
||||
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 --
|
||||
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
|
||||
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
|
||||
Bridge_. It doesn't matter if you don't know how to play Bridge. You only need
|
||||
to know that 52 cards are dealt out equally to four players, who are
|
||||
traditionally called *north*, *east*, *south* and *west*. Our class looks
|
||||
something like this::
|
||||
Bridge_. (Don't worry, you don't know how to play Bridge to follow this
|
||||
example. You only need to know that 52 cards are dealt out equally to four
|
||||
players, who are traditionally called *north*, *east*, *south* and *west*.)
|
||||
Our class looks something like this::
|
||||
|
||||
class Hand(object):
|
||||
def __init__(self, north, east, south, west):
|
||||
@ -42,10 +43,9 @@ something like this::
|
||||
|
||||
# ... (other possibly useful methods omitted) ...
|
||||
|
||||
This is just an ordinary Python class, nothing Django-specific about it. We
|
||||
would like to be able to things like this in our models (we assume the
|
||||
``hand`` attribute on the model is an instance of ``Hand``)::
|
||||
|
||||
This is just an ordinary Python class, with nothing Django-specific about it.
|
||||
We'd like to be able to things like this in our models (we assume the ``hand``
|
||||
attribute on the model is an instance of ``Hand``)::
|
||||
|
||||
example = MyModel.objects.get(pk=1)
|
||||
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
|
||||
.. _Bridge: http://en.wikipedia.org/wiki/Contract_bridge
|
||||
|
||||
Background Theory
|
||||
Background theory
|
||||
=================
|
||||
|
||||
Database storage
|
||||
@ -87,7 +87,7 @@ that falls out fairly naturally once you have the database side under control).
|
||||
Fields in a model must somehow be converted to fit into an existing database
|
||||
column type. Different databases provide different sets of valid column types,
|
||||
but the rule is still the same: those are the only types you have to work
|
||||
with. Anything you want to store in the database must fit into one of
|
||||
with. Anything you want to store in the database must fit into one of
|
||||
those types.
|
||||
|
||||
Normally, you're either writing a Django field to match a particular database
|
||||
@ -95,10 +95,9 @@ column type, or there's a fairly straightforward way to convert your data to,
|
||||
say, a string.
|
||||
|
||||
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.
|
||||
Say, all the *north* cards first, then the *east*, *south* and *west* cards, in
|
||||
that order. So ``Hand`` objects can be saved to text or character columns in
|
||||
the database.
|
||||
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. So
|
||||
``Hand`` objects can be saved to text or character columns in the database.
|
||||
|
||||
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
|
||||
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
|
||||
to say that everything descends from ``Field`` and then customises key pieces
|
||||
of the class behaviour.
|
||||
to say that everything descends from ``Field`` and then customizes key pieces
|
||||
of the class behavior.
|
||||
|
||||
.. _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
|
||||
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
|
||||
@ -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
|
||||
instances and the database/serializer values in various ways (there are
|
||||
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
|
||||
clearer in the examples below. Just remember that you will often end up
|
||||
creating two classes when you want a custom field. The first class is the
|
||||
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.
|
||||
This is the ``Hand`` class in our 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.
|
||||
example). If this sounds a bit tricky, don't worry -- it will become clearer in
|
||||
the examples below. Just remember that you will often end up creating two
|
||||
classes when you want a custom field:
|
||||
|
||||
* The first class is the 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. This is the ``Hand`` class in our
|
||||
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
|
||||
=============================
|
||||
|
||||
When you are planning your ``Field`` subclass, first give some thought to
|
||||
which existing field 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.
|
||||
When planning your ``Field`` subclass, first give some thought to which
|
||||
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.
|
||||
|
||||
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
|
||||
the ``__init__()`` method of ``Field`` (or your parent class).
|
||||
|
||||
In our example, the Django field we create is going to be called
|
||||
``HandField``. It's not a bad idea to use a similar naming scheme to Django's
|
||||
fields so that our new class is identifiable and yet clearly related to the
|
||||
``Hand`` class it is wrapping. It doesn't behave like any existing field, so
|
||||
we'll subclass directly from ``Field``::
|
||||
In our example, we'll call our field ``HandField``. (It's a good idea to call
|
||||
your ``Field`` subclass ``(Something)Field``, so it's easily identifiable as a
|
||||
``Field`` subclass.) It doesn't behave like any existing field, so we'll
|
||||
subclass directly from ``Field``::
|
||||
|
||||
from django.db import models
|
||||
|
||||
@ -160,7 +163,7 @@ we'll subclass directly from ``Field``::
|
||||
kwargs['max_length'] = 104
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
order:
|
||||
|
||||
- ``verbose_name``
|
||||
- ``name``
|
||||
- ``primary_key``
|
||||
- ``max_length``
|
||||
- ``unique``
|
||||
- ``blank``
|
||||
- ``null``
|
||||
- ``db_index``
|
||||
- ``core``
|
||||
- ``rel``: Used for related fields (like ``ForeignKey``). For advanced use
|
||||
* ``verbose_name``
|
||||
* ``name``
|
||||
* ``primary_key``
|
||||
* ``max_length``
|
||||
* ``unique``
|
||||
* ``blank``
|
||||
* ``null``
|
||||
* ``db_index``
|
||||
* ``core``
|
||||
* ``rel``: Used for related fields (like ``ForeignKey``). For advanced use
|
||||
only.
|
||||
- ``default``
|
||||
- ``editable``
|
||||
- ``serialize``: If ``False``, the field will not be serialized when the
|
||||
* ``default``
|
||||
* ``editable``
|
||||
* ``serialize``: If ``False``, the field will not be serialized when the
|
||||
model is passed to Django's serializers_. Defaults to ``True``.
|
||||
- ``prepopulate_from``
|
||||
- ``unique_for_date``
|
||||
- ``unique_for_month``
|
||||
- ``unique_for_year``
|
||||
- ``validator_list``
|
||||
- ``choices``
|
||||
- ``radio_admin``
|
||||
- ``help_text``
|
||||
- ``db_column``
|
||||
- ``db_tablespace``: Currently only used with the Oracle backend and only
|
||||
* ``prepopulate_from``
|
||||
* ``unique_for_date``
|
||||
* ``unique_for_month``
|
||||
* ``unique_for_year``
|
||||
* ``validator_list``
|
||||
* ``choices``
|
||||
* ``radio_admin``
|
||||
* ``help_text``
|
||||
* ``db_column``
|
||||
* ``db_tablespace``: Currently only used with the Oracle backend and only
|
||||
for index creation. You can usually ignore this option.
|
||||
|
||||
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
|
||||
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
|
||||
possible. If you are only working with custom database column types and your
|
||||
two reasons: either to take advantage of a custom database column type, or to
|
||||
handle complex Python types. Obviously, a combination of the two is also
|
||||
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
|
||||
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
|
||||
to make sure that when Django initialises an instance of our model and assigns
|
||||
a database value to our custom field attribute we convert that value into the
|
||||
If you're handling custom Python types, such as our ``Hand`` class, we need
|
||||
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
|
||||
appropriate Python object. The details of how this happens internally are a
|
||||
little complex. For the field writer, though, things are fairly simple. Make
|
||||
sure your field subclass uses ``django.db.models.SubfieldBase`` as its
|
||||
metaclass. This ensures that the ``to_python()`` method, documented below_,
|
||||
will always be called when the attribute is initialised.
|
||||
|
||||
Our ``HandField`` class now looks like this::
|
||||
little complex, but the code you need to write in your ``Field`` class is
|
||||
simple: make sure your field subclass uses ``django.db.models.SubfieldBase`` as
|
||||
its metaclass::
|
||||
|
||||
class HandField(models.Field):
|
||||
__metaclass__ = models.SubfieldBase
|
||||
@ -241,16 +241,18 @@ Our ``HandField`` class now looks like this::
|
||||
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
|
||||
|
||||
Useful methods
|
||||
--------------
|
||||
|
||||
Once you've created your ``Field`` subclass and setup up the
|
||||
``__metaclass__``, if necessary, there are a few standard methods you need to
|
||||
consider overriding. Which of these you need to implement will depend on you
|
||||
particular field behaviour. The list below is in approximately decreasing
|
||||
order of importance, so start from the top.
|
||||
Once you've created your ``Field`` subclass and set up up the
|
||||
``__metaclass__``, you might consider overriding a few standard methods,
|
||||
depending on your field's behavior. The list of methods below is in
|
||||
approximately decreasing order of importance, so start from the top.
|
||||
|
||||
``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
|
||||
of the way.
|
||||
|
||||
|
||||
``to_python(self, value)``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Converts between all the ways your field can receive its initial value and the
|
||||
Python object you want to end up with. The default version just returns
|
||||
``value``, so is useful is the database backend returns the data already in
|
||||
the correct form (a Python string, for example).
|
||||
Converts a value as returned by your database (or a serializer) to a Python
|
||||
object.
|
||||
|
||||
Normally, you will need to override this method. As a general rule, be
|
||||
prepared to accept an instance of the right type (e.g. ``Hand`` in our ongoing
|
||||
example), a string (from a deserializer, for example), and whatever the
|
||||
database wrapper returns for the column type you are using.
|
||||
The default implementation simply returns ``value``, for the common case in
|
||||
which the database backend already returns data in the correct format (as a
|
||||
Python string, for example).
|
||||
|
||||
In our ``HandField`` class, we are storing the data in a character field in
|
||||
the database, so we need to be able to process strings and ``Hand`` instances
|
||||
in ``to_python()``::
|
||||
If your custom ``Field`` class deals with data structures that are more complex
|
||||
than strings, dates, integers or floats, then you'll need to override this
|
||||
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):
|
||||
# ...
|
||||
@ -362,14 +373,18 @@ in ``to_python()``::
|
||||
if isinstance(value, Hand):
|
||||
return value
|
||||
|
||||
# The string case
|
||||
# The string case.
|
||||
p1 = re.compile('.{26}')
|
||||
p2 = re.compile('..')
|
||||
args = [p2.findall(x) for x in p1.findall(value)]
|
||||
return Hand(*args)
|
||||
|
||||
Notice that we always return a ``Hand`` instance from this method. That is the
|
||||
Python object we want to store in the model's attribute.
|
||||
Notice that we always return a ``Hand`` instance from this method. That's the
|
||||
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)``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -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
|
||||
(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
|
||||
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.
|
||||
|
||||
For example::
|
||||
@ -389,7 +404,6 @@ For example::
|
||||
return ''.join([''.join(l) for l in (self.north,
|
||||
self.east, self.south, self.west)])
|
||||
|
||||
|
||||
``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``
|
||||
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
|
||||
useful. For example, the Django ``DateTimeField`` uses this method to set the
|
||||
attribute to the correct value before returning it in the cases when
|
||||
``auto_now`` or ``auto_now_add`` are set on the field.
|
||||
You only need to override this method if you want to preprocess the value
|
||||
somehow, just before saving. For example, Django's ``DateTimeField`` uses this
|
||||
method to set the attribute correctly in the case of ``auto_now`` or
|
||||
``auto_now_add``.
|
||||
|
||||
If you do override this method, you must return the value of the attribute at
|
||||
the end. You should also update the model's attribute if you make any changes
|
||||
@ -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
|
||||
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
|
||||
form widget). See the `forms documentation`_ for information about this. Also
|
||||
have a look at ``django.contrib.localflavor`` for some examples of custom
|
||||
widgets.
|
||||
form widget). See the `forms documentation`_ for information about this, and
|
||||
take a look at the code in ``django.contrib.localflavor`` for some examples of
|
||||
custom widgets.
|
||||
|
||||
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):
|
||||
# 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.update(kwargs)
|
||||
return super(HandField, self).formfield(**defaults)
|
||||
|
||||
This assumes we have some ``MyFormField`` field class (which has its own
|
||||
default widget) imported. This document doesn't cover the details of writing
|
||||
custom form fields.
|
||||
This assumes we're imported a ``MyFormField`` field class (which has its own
|
||||
default widget). This document doesn't cover the details of writing custom form
|
||||
fields.
|
||||
|
||||
.. _helper functions: ../newforms/#generating-forms-for-models
|
||||
.. _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
|
||||
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
|
||||
database storage is similar in type to some other field, so you can use that
|
||||
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
|
||||
``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
|
||||
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.
|
||||
|
||||
.. _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
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
--------------------
|
||||
|
||||
Writing a custom field can be a tricky process sometimes, particularly if you
|
||||
are doing complex conversions between your Python types and your database and
|
||||
serialization formats. A couple of tips to make things go more smoothly:
|
||||
Writing a custom field can be a tricky process, particularly if you're doing
|
||||
complex conversions between your Python types and your database and
|
||||
serialization formats. Here are a couple of tips to make things go more
|
||||
smoothly:
|
||||
|
||||
1. Look at the existing Django fields (in
|
||||
``django/db/models/fields/__init__.py``) for inspiration. Try to find a field
|
||||
that is already close to what you want and extend it a little bit, in
|
||||
preference to 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.
|
||||
1. Look at the existing Django fields (in
|
||||
``django/db/models/fields/__init__.py``) for inspiration. Try to find a
|
||||
field that's similar to what you want and extend it a little bit,
|
||||
instead of creating an entirely new field from scratch.
|
||||
|
||||
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
|
||||
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
|
||||
documentation`_ for details on creating and managing tablespaces.
|
||||
|
||||
|
@ -253,9 +253,11 @@ For example::
|
||||
|
||||
The class has the following methods:
|
||||
|
||||
* ``send()`` sends the message, using either the connection that is
|
||||
specified in the ``connection`` attribute, or creating a new connection
|
||||
if none already exists.
|
||||
* ``send(fail_silently=False)`` sends the message, using either
|
||||
the connection that is specified in the ``connection``
|
||||
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
|
||||
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
|
||||
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
|
||||
default, this is ``False``.
|
||||
default, this is ``True``.
|
||||
|
||||
* ``context_processors``: A list of template-context processors to apply to
|
||||
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
|
||||
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
|
||||
default, this is ``False``.
|
||||
default, this is ``True``.
|
||||
|
||||
* ``context_processors``: A list of template-context processors to apply to
|
||||
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
|
||||
automatically create database tables for your models, you'll need to
|
||||
ensure that Django has permission to create tables in the database
|
||||
you're using; if you plan to manually create the tables, you can
|
||||
simply grant Django ``SELECT``, ``INSERT``, ``UPDATE`` and ``DELETE``
|
||||
permissions. Django does not issue ``ALTER TABLE`` statements, and so
|
||||
will not require permission to do so. If you will be using Django's
|
||||
`testing framework`_ with data fixtures, Django will need permission
|
||||
to create a temporary test database.
|
||||
ensure that Django has permission to create and alter tables in the
|
||||
database you're using; if you plan to manually create the tables, you
|
||||
can simply grant Django ``SELECT``, ``INSERT``, ``UPDATE`` and
|
||||
``DELETE`` permissions. On some databases, Django will need
|
||||
``ALTER TABLE`` privileges during ``syncdb`` but won't issue
|
||||
``ALTER TABLE`` statements on a table once ``syncdb`` has created it.
|
||||
|
||||
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/
|
||||
.. _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.
|
||||
|
||||
* Performs URL rewriting based on the ``APPEND_SLASH`` and ``PREPEND_WWW``
|
||||
settings. If ``APPEND_SLASH`` is ``True``, URLs that lack a trailing
|
||||
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
|
||||
redirected to ``foo.com/bar/``, but ``foo.com/bar/file.txt`` is passed
|
||||
through unchanged.
|
||||
settings.
|
||||
|
||||
If ``APPEND_SLASH`` is ``True`` 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.
|
||||
|
||||
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
|
||||
redirected to the same URL with a leading "www."
|
||||
|
@ -618,8 +618,9 @@ statement for this field.
|
||||
**New in Django development version**
|
||||
|
||||
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
|
||||
the model, if any. If the backend doesn't support tablespaces, this
|
||||
this field is indexed. The default is the project's
|
||||
``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.
|
||||
|
||||
``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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You may wish to show a visitor which fields are required. Here is the above
|
||||
example modified to insert an asterix after the label of each required field::
|
||||
It's common to show a user which fields are required. Here's an example of how
|
||||
to do that, using the above example modified to insert an asterisk after the
|
||||
label of each required field::
|
||||
|
||||
<form method="post" action="">
|
||||
<dl>
|
||||
{% 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>
|
||||
{% if field.help_text %}<dd>{{ field.help_text }}</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>
|
||||
|
||||
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
|
||||
check ``field.field.required`` and not ``field.required``. In the template,
|
||||
``field`` is a ``newforms.forms.BoundField`` instance, which holds the actual
|
||||
``Field`` instance in its ``field`` attribute.
|
||||
addition here. It adds the asterisk only if the field is required.
|
||||
|
||||
Note that we check ``field.field.required`` and not ``field.required``. In the
|
||||
template, ``field`` is a ``newforms.forms.BoundField`` instance, which holds
|
||||
the actual ``Field`` instance in its ``field`` attribute.
|
||||
|
||||
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**
|
||||
|
||||
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
|
||||
want to override. For example::
|
||||
want to override. For example, here is the default error message::
|
||||
|
||||
>>> generic = forms.CharField()
|
||||
>>> generic.clean('')
|
||||
@ -1115,14 +1117,16 @@ want to override. For example::
|
||||
...
|
||||
ValidationError: [u'This field is required.']
|
||||
|
||||
And here is a custom error message::
|
||||
|
||||
>>> name = forms.CharField(error_messages={'required': 'Please enter your name'})
|
||||
>>> name.clean('')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Please enter your name']
|
||||
|
||||
In the `built-in Field classes`_ section below, each Field defines the error
|
||||
message keys it uses.
|
||||
In the `built-in Field classes`_ section below, each ``Field`` defines the
|
||||
error message keys it uses.
|
||||
|
||||
Dynamic initial values
|
||||
----------------------
|
||||
@ -1432,7 +1436,7 @@ Also takes the following optional arguments:
|
||||
The optional argument ``error_message`` is also accepted for backwards
|
||||
compatibility. The preferred way to provide an error message is to use the
|
||||
``error_messages`` argument, passing a dictionary with ``'invalid'`` as a key
|
||||
and the error message as the value.
|
||||
and the error message as the value.
|
||||
|
||||
``TimeField``
|
||||
~~~~~~~~~~~~~
|
||||
@ -1766,421 +1770,14 @@ You can then use this field whenever you have a form that requires a comment::
|
||||
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.
|
||||
The prefered way of generating forms that work with models is explained in the
|
||||
`ModelForms documentation`_.
|
||||
|
||||
For this reason, Django provides a few helper functions that let you create a
|
||||
``Form`` class from a Django model.
|
||||
Looking for the ``form_for_model`` and ``form_for_instance`` documentation?
|
||||
They've been deprecated, but you can still `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.
|
||||
.. _ModelForms documentation: ../modelforms/
|
||||
.. _view the documentation: ../form_for_model/
|
||||
|
||||
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
|
||||
every regular expression in the URLconf.
|
||||
|
||||
* If you don't define your own 404 view -- and simply use the default,
|
||||
which is recommended -- you still have one obligation: To create a
|
||||
``404.html`` template in the root of your template directory. The default
|
||||
404 view will use that template for all 404 errors.
|
||||
* If you don't define your own 404 view -- and simply use the
|
||||
default, which is recommended -- you still have one obligation:
|
||||
you must create a ``404.html`` template in the root of your
|
||||
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.
|
||||
|
||||
The 500 (server error) view
|
||||
@ -572,7 +575,8 @@ the view ``django.views.defaults.server_error``, which loads and renders the
|
||||
template ``500.html``.
|
||||
|
||||
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
|
||||
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.serialize(queryset, ensure_ascii=False, stream=response)
|
||||
|
||||
Django ships with a copy of simplejson_ in the source. Be aware, that if
|
||||
you're using that for serializing directly that not all Django output can be
|
||||
The Django source code includes the simplejson_ module. Be aware that if
|
||||
you're serializing using that module directly, not all Django output can be
|
||||
passed unmodified to simplejson. In particular, `lazy translation objects`_
|
||||
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
|
||||
.. _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()``
|
||||
|
||||
* ``setdefault()``
|
||||
|
||||
It also has these three methods:
|
||||
|
||||
* ``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
|
||||
``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
|
||||
-------------
|
||||
|
||||
@ -393,6 +414,26 @@ Default: ``'webmaster@localhost'``
|
||||
Default e-mail address to use for various automated correspondence from the
|
||||
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
|
||||
----------------------
|
||||
|
||||
@ -1129,12 +1170,12 @@ If you're not setting the ``DJANGO_SETTINGS_MODULE`` environment variable, you
|
||||
settings.
|
||||
|
||||
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.
|
||||
|
||||
If you set ``DJANGO_SETTINGS_MODULE``, access settings values somehow, *then*
|
||||
call ``configure()``, Django will raise an ``EnvironmentError`` saying settings
|
||||
have already been configured.
|
||||
call ``configure()``, Django will raise a ``RuntimeError`` indicating
|
||||
that settings have already been configured.
|
||||
|
||||
Also, it's an error to call ``configure()`` more than once, or to call
|
||||
``configure()`` after any setting has been accessed.
|
||||
|
@ -98,8 +98,8 @@ This example is equivalent to::
|
||||
except MyModel.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
Note: As with ``get()``, an ``AssertionError`` will be raised if more than
|
||||
one object is found.
|
||||
Note: As with ``get()``, an ``MultipleObjectsReturned`` exception will be
|
||||
raised if more than one object is found.
|
||||
|
||||
.. _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
|
||||
``['0613']``. For a request to ``/rss/beats/0613/foo/bar/``, bits would
|
||||
be ``['0613', 'foo', 'bar']``.
|
||||
|
||||
* ``get_object()`` is responsible for retrieving the given beat, from the
|
||||
given ``bits``. In this case, it uses the Django database API to retrieve
|
||||
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
|
||||
subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
|
||||
``get_object()`` tells Django to produce a 404 error for that request.
|
||||
|
||||
* To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
|
||||
Django uses the ``title()``, ``link()`` and ``description()`` methods. In
|
||||
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``
|
||||
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
|
||||
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
|
||||
process, so is a good place to bail out early).
|
||||
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
|
||||
process, so it's a good place to bail out early.)
|
||||
|
||||
* Finally, note that ``items()`` in this example also takes the ``obj``
|
||||
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
|
||||
# them in this order.
|
||||
|
||||
# First, the framework tries the get_absolute_url() method on each item
|
||||
# returned by items(). Failing that, it tries these two methods:
|
||||
# First, the framework tries the two methods below, in
|
||||
# order. Failing that, it falls back to the get_absolute_url()
|
||||
# method on each item returned by items().
|
||||
|
||||
def item_link(self, item):
|
||||
"""
|
||||
|
@ -310,58 +310,106 @@ Automatic HTML escaping
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
A very real problem when creating HTML (and other) output using templates and
|
||||
variable substitution is the possibility of accidently inserting some variable
|
||||
value that affects the resulting HTML. For example, a template fragment such as
|
||||
::
|
||||
When generating HTML from templates, there's always a risk that a variable will
|
||||
include characters that affect the resulting HTML. For example, consider this
|
||||
template fragment::
|
||||
|
||||
Hello, {{ name }}.
|
||||
|
||||
seems like a harmless way to display the user's name. However, if you are
|
||||
displaying data that the user entered directly and they had entered their name as ::
|
||||
At first, this seems like a harmless way to display a user's name, but consider
|
||||
what would happen if the user entered his name as this::
|
||||
|
||||
<script>alert('hello')</script>
|
||||
|
||||
this would always display a Javascript alert box when the page was loaded.
|
||||
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,
|
||||
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
|
||||
Site Scripting`_ (XSS) attacks.
|
||||
With this name value, the template would be rendered as::
|
||||
|
||||
Hello, <script>alert('hello')</script>
|
||||
|
||||
...which means the browser would pop-up a JavaScript alert box!
|
||||
|
||||
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
|
||||
|
||||
In order to provide some protection against these problems, Django
|
||||
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.
|
||||
How to turn it off
|
||||
------------------
|
||||
|
||||
Since some variables will contain data that is *intended* to be rendered
|
||||
as HTML, template tag and filter writers can mark their output strings as
|
||||
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.
|
||||
If you don't want data to be auto-escaped, on a per-site, per-template level or
|
||||
per-variable level, you can turn it off in several ways.
|
||||
|
||||
By default, automatic HTML escaping is always applied. However, sometimes you
|
||||
will not want this to occur (for example, if you're using the templating
|
||||
system to create an email). To control automatic escaping inside your template,
|
||||
wrap the affected content in the ``autoescape`` tag, like so::
|
||||
Why would you want to turn it off? Because sometimes, template variables
|
||||
contain data that you *intend* to be rendered as raw HTML, in which case you
|
||||
don't want their contents to be escaped. For example, you might store a blob of
|
||||
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 %}
|
||||
Hello {{ name }}
|
||||
{% endautoescape %}
|
||||
|
||||
The auto-escaping tag passes its effect onto templates that extend the
|
||||
current one as well as templates included via the ``include`` tag, just like
|
||||
all block tags.
|
||||
|
||||
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::
|
||||
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. Here is an example template::
|
||||
|
||||
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 }}
|
||||
{% autoescape on %}
|
||||
Auto-escaping applies again, {{ name }}
|
||||
Auto-escaping applies again: {{ name }}
|
||||
{% endautoescape %}
|
||||
{% endautoescape %}
|
||||
|
||||
For individual variables, the ``safe`` filter can also be used to indicate
|
||||
that the contents should not be automatically escaped::
|
||||
The auto-escaping tag passes its effect onto templates that extend the
|
||||
current one as well as templates included via the ``include`` tag, just like
|
||||
all block tags. For example::
|
||||
|
||||
This will be escaped: {{ data }}
|
||||
This will not be escaped: {{ data|safe }}
|
||||
# base.html
|
||||
|
||||
Think of *safe* as shorthand for *safe from further escaping* or *can be
|
||||
safely interpreted as HTML*. In this example, if ``data`` contains ``'<a>'``,
|
||||
the output will be::
|
||||
{% autoescape off %}
|
||||
<h1>{% block title %}</h1>
|
||||
{% 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
|
||||
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.
|
||||
# child.html
|
||||
|
||||
You should also note that if you are trying to write a template that might be
|
||||
used in situations where automatic escaping is enabled or disabled and you
|
||||
don't know which (such as when your template is included in other templates),
|
||||
you can safely write as if you were in an ``{% autoescape off %}`` situation.
|
||||
Scatter ``escape`` filters around for any variables that need escaping. When
|
||||
auto-escaping is on, these extra filters won't change the output -- any
|
||||
variables that use the ``escape`` filter do not have further automatic
|
||||
escaping applied to them.
|
||||
{% extends "base.html" %}
|
||||
{% block title %}This & that{% endblock %}
|
||||
{% block content %}<b>Hello!</b>{% endblock %}
|
||||
|
||||
Because auto-escaping is turned off in the base template, it will also be
|
||||
turned off in the child template, resulting in the following rendered HTML::
|
||||
|
||||
<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
|
||||
--------------------------------------
|
||||
|
||||
Sometimes you will pass a string literal as an argument to a filter. For
|
||||
example::
|
||||
As we mentioned earlier, filter arguments can be strings::
|
||||
|
||||
{{ data|default:"This is a string literal." }}
|
||||
|
||||
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
|
||||
``safe`` filter). The reasoning behind this is that the template author is in
|
||||
control of what goes into the string literal, so they can make sure the text
|
||||
is correctly escaped when the template is written.
|
||||
template -- they act as if they were all passed through the ``safe`` filter.
|
||||
The reasoning behind this is that the template author is in control of what
|
||||
goes into the string literal, so they can make sure the text is correctly
|
||||
escaped when the template is written.
|
||||
|
||||
This means you would write ::
|
||||
|
||||
@ -426,7 +482,7 @@ This means you would write ::
|
||||
{{ data|default:"3 > 2" }} <-- Bad! Don't do this.
|
||||
|
||||
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.
|
||||
|
||||
Using the built-in reference
|
||||
@ -509,17 +565,17 @@ autoescape
|
||||
|
||||
**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
|
||||
inside the block.
|
||||
|
||||
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
|
||||
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
|
||||
escaping, either by the code that populated the variable, or because it has
|
||||
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 had
|
||||
the ``safe`` or ``escape`` filters applied.
|
||||
|
||||
block
|
||||
@ -1171,8 +1227,10 @@ Adds the arg to the value.
|
||||
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
|
||||
~~~~~~~~
|
||||
@ -1230,11 +1288,11 @@ once, after all other filters).
|
||||
|
||||
Escapes a string's HTML. Specifically, it makes these replacements:
|
||||
|
||||
* ``"&"`` to ``"&"``
|
||||
* ``<`` to ``"<"``
|
||||
* ``>`` to ``">"``
|
||||
* ``'"'`` (double quote) to ``'"'``
|
||||
* ``"'"`` (single quote) to ``'''``
|
||||
* ``<`` is converted to ``<``
|
||||
* ``>`` is converted to ``>``
|
||||
* ``'`` (single quote) is converted to ``'``
|
||||
* ``"`` (double quote) is converted to ``"``
|
||||
* ``&`` is converted to ``&``
|
||||
|
||||
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
|
||||
@ -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
|
||||
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
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
@ -1268,36 +1335,36 @@ floatformat
|
||||
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:
|
||||
|
||||
======== ======================= ======
|
||||
value Template Output
|
||||
======== ======================= ======
|
||||
34.23234 {{ value|floatformat }} 34.2
|
||||
34.00000 {{ value|floatformat }} 34
|
||||
34.26000 {{ value|floatformat }} 34.3
|
||||
======== ======================= ======
|
||||
============ =========================== ========
|
||||
``value`` Template Output
|
||||
============ =========================== ========
|
||||
``34.23234`` ``{{ value|floatformat }}`` ``34.2``
|
||||
``34.00000`` ``{{ value|floatformat }}`` ``34``
|
||||
``34.26000`` ``{{ value|floatformat }}`` ``34.3``
|
||||
============ =========================== ========
|
||||
|
||||
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
|
||||
======== ========================= ======
|
||||
34.23234 {{ value|floatformat:3 }} 34.232
|
||||
34.00000 {{ value|floatformat:3 }} 34.000
|
||||
34.26000 {{ value|floatformat:3 }} 34.260
|
||||
======== ========================= ======
|
||||
============ ============================= ==========
|
||||
``value`` Template Output
|
||||
============ ============================= ==========
|
||||
``34.23234`` ``{{ value|floatformat:3 }}`` ``34.232``
|
||||
``34.00000`` ``{{ value|floatformat:3 }}`` ``34.000``
|
||||
``34.26000`` ``{{ value|floatformat:3 }}`` ``34.260``
|
||||
============ ============================= ==========
|
||||
|
||||
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
|
||||
displayed. For example:
|
||||
|
||||
======== ============================ ======
|
||||
value Template Output
|
||||
======== ============================ ======
|
||||
34.23234 {{ value|floatformat:"-3" }} 34.232
|
||||
34.00000 {{ value|floatformat:"-3" }} 34
|
||||
34.26000 {{ value|floatformat:"-3" }} 34.260
|
||||
======== ============================ ======
|
||||
============ ================================ ==========
|
||||
``value`` Template Output
|
||||
============ ================================ ==========
|
||||
``34.23234`` ``{{ value|floatformat:"-3" }}`` ``34.232``
|
||||
``34.00000`` ``{{ value|floatformat:"-3" }}`` ``34``
|
||||
``34.26000`` ``{{ value|floatformat:"-3" }}`` ``34.260``
|
||||
============ ================================ ==========
|
||||
|
||||
Using ``floatformat`` with no argument is equivalent to using ``floatformat``
|
||||
with an argument of ``-1``.
|
||||
|
@ -727,134 +727,144 @@ Filters and auto-escaping
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
When you are writing a custom filter, you need to give some thought to how
|
||||
this filter will interact with Django's auto-escaping behaviour. Firstly, you
|
||||
should realise that there are three types of strings that can be passed around
|
||||
inside the template code:
|
||||
When writing a custom filter, give some thought to how the filter will interact
|
||||
with Django's auto-escaping behavior. Note that three types of strings can be
|
||||
passed around inside the template code:
|
||||
|
||||
* raw strings are the native Python ``str`` or ``unicode`` types. On
|
||||
output, they are escaped if auto-escaping is in effect and presented
|
||||
unchanged, otherwise.
|
||||
* **Raw strings** are the native Python ``str`` or ``unicode`` types. On
|
||||
output, they're escaped if auto-escaping is in effect and presented
|
||||
unchanged, otherwise.
|
||||
|
||||
* "safe" strings are strings that are safe from further escaping at output
|
||||
time. Any necessary escaping has already been done. They are commonly used
|
||||
for output that contains raw HTML that is intended to be intrepreted on the
|
||||
client side.
|
||||
* **Safe strings** are strings that have been marked safe from further
|
||||
escaping at output time. Any necessary escaping has already been done.
|
||||
They're commonly used for output that contains raw HTML that is intended
|
||||
to be interpreted as-is on the client side.
|
||||
|
||||
Internally, these strings are of type ``SafeString`` or ``SafeUnicode``,
|
||||
although they share a common base class in ``SafeData``, so you can test
|
||||
for them using code like::
|
||||
Internally, these strings are of type ``SafeString`` or ``SafeUnicode``.
|
||||
They share a common base class of ``SafeData``, so you can test
|
||||
for them using code like::
|
||||
|
||||
if isinstance(value, SafeData):
|
||||
# Do something with the "safe" string.
|
||||
if isinstance(value, SafeData):
|
||||
# Do something with the "safe" string.
|
||||
|
||||
* strings which are marked as "needing escaping" are *always* escaped on
|
||||
output, regardless of whether they are in an ``autoescape`` block or not.
|
||||
These strings are only escaped once, however, even if auto-escaping
|
||||
applies. This type of string is internally represented by the types
|
||||
``EscapeString`` and ``EscapeUnicode``. You will not normally need to worry
|
||||
about these; they exist for the implementation of the ``escape`` filter.
|
||||
* **Strings marked as "needing escaping"** are *always* escaped on
|
||||
output, regardless of whether they are in an ``autoescape`` block or not.
|
||||
These strings are only escaped once, however, even if auto-escaping
|
||||
applies.
|
||||
|
||||
When you are writing a filter, your code will typically fall into one of two
|
||||
situations:
|
||||
Internally, these strings are of type ``EscapeString`` or
|
||||
``EscapeUnicode``. Generally you don't have to worry about these; they
|
||||
exist for the implementation of the ``escape`` filter.
|
||||
|
||||
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
||||
``'``, ``"`` or ``&``) into the result that were not already present. In
|
||||
this case, you can let Django take care of all the auto-escaping handling
|
||||
for you. All you need to do is put the ``is_safe`` attribute on your
|
||||
filter function and set it to ``True``. This attribute tells Django that
|
||||
is a "safe" string is passed into your filter, the result will still be
|
||||
"safe" and if a non-safe string is passed in, Django will automatically
|
||||
escape it, if necessary. The reason ``is_safe`` is necessary is because
|
||||
there are plenty of normal string operations that will turn a ``SafeData``
|
||||
object back into a normal ``str`` or ``unicode`` object and, rather than
|
||||
try to catch them all, which would be very difficult, Django repairs the
|
||||
damage after the filter has completed.
|
||||
Template filter code falls into one of two situations:
|
||||
|
||||
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
|
||||
the result (aside from any that were already present), you should mark
|
||||
your filter with ``is_safe``::
|
||||
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
||||
``'``, ``"`` or ``&``) into the result that were not already present. In
|
||||
this case, you can let Django take care of all the auto-escaping
|
||||
handling for you. All you need to do is put the ``is_safe`` attribute on
|
||||
your filter function and set it to ``True``, like so::
|
||||
|
||||
@register.filter
|
||||
def myfilter(value):
|
||||
return value
|
||||
myfilter.is_safe = True
|
||||
|
||||
This attribute tells Django that if a "safe" string is passed into your
|
||||
filter, the result will still be "safe" and if a non-safe string is
|
||||
passed in, Django will automatically escape it, if necessary.
|
||||
|
||||
You can think of this as meaning "this filter is safe -- it doesn't
|
||||
introduce any possibility of unsafe HTML."
|
||||
|
||||
The reason ``is_safe`` is necessary is because there are plenty of
|
||||
normal string operations that will turn a ``SafeData`` object back into
|
||||
a normal ``str`` or ``unicode`` object and, rather than try to catch
|
||||
them all, which would be very difficult, Django repairs the damage after
|
||||
the filter has completed.
|
||||
|
||||
For example, suppose you have a filter that adds the string ``xx`` to the
|
||||
end of any input. Since this introduces no dangerous HTML characters to
|
||||
the result (aside from any that were already present), you should mark
|
||||
your filter with ``is_safe``::
|
||||
|
||||
@register.filter
|
||||
def add_xx(value):
|
||||
return '%sxx' % value
|
||||
add_xx.is_safe = True
|
||||
|
||||
When this filter is used in a template where auto-escaping is enabled,
|
||||
Django will escape the output whenever the input is not already marked as
|
||||
"safe".
|
||||
When this filter is used in a template where auto-escaping is enabled,
|
||||
Django will escape the output whenever the input is not already marked as
|
||||
"safe".
|
||||
|
||||
By default, ``is_safe`` defaults to ``False`` and you can omit it from
|
||||
any filters where it isn't required.
|
||||
By default, ``is_safe`` defaults to ``False``, and you can omit it from
|
||||
any filters where it isn't required.
|
||||
|
||||
Be careful when deciding if your filter really does leave safe strings
|
||||
as safe. Sometimes if you are *removing* characters, you can
|
||||
inadvertently leave unbalanced HTML tags or entities in the result.
|
||||
For example, removing a ``>`` from the input might turn ``<a>`` into
|
||||
``<a``, which would need to be escaped on output to avoid causing
|
||||
problems. Similarly, removing a semicolon (``;``) can turn ``&``
|
||||
into ``&``, which is no longer a valid entity and thus needs
|
||||
further escaping. Most cases won't be nearly this tricky, but keep an
|
||||
eye out for any problems like that when reviewing your code.
|
||||
Be careful when deciding if your filter really does leave safe strings
|
||||
as safe. If you're *removing* characters, you might inadvertently leave
|
||||
unbalanced HTML tags or entities in the result. For example, removing a
|
||||
``>`` from the input might turn ``<a>`` into ``<a``, which would need to
|
||||
be escaped on output to avoid causing problems. Similarly, removing a
|
||||
semicolon (``;``) can turn ``&`` into ``&``, which is no longer a
|
||||
valid entity and thus needs further escaping. Most cases won't be nearly
|
||||
this tricky, but keep an eye out for any problems like that when
|
||||
reviewing your code.
|
||||
|
||||
2. Alternatively, your filter code can manually take care of any necessary
|
||||
escaping. This is usually necessary when you are introducing new HTML
|
||||
markup into 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
|
||||
handle the input yourself.
|
||||
2. Alternatively, your filter code can manually take care of any necessary
|
||||
escaping. This is necessary when you're introducing new HTML markup into
|
||||
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 handle the input yourself.
|
||||
|
||||
To mark the output as a safe string, use
|
||||
``django.utils.safestring.mark_safe()``.
|
||||
To mark the output as a safe string, use ``django.utils.safestring.mark_safe()``.
|
||||
|
||||
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
|
||||
depend upon whether or not auto-escaping is in effect. The idea is to
|
||||
write filters than can operate in templates where auto-escaping is either
|
||||
on or off in order to make things easier for your template authors.
|
||||
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 depends on
|
||||
whether auto-escaping is in effect. The idea is to write filters than
|
||||
can operate in templates where auto-escaping is either on or off in
|
||||
order to make things easier for your template authors.
|
||||
|
||||
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
|
||||
specify this attribute, it defaults to ``False``). This attribute tells
|
||||
Django that your filter function wants to be passed an extra keyword
|
||||
argument, called ``autoescape`` that is ``True`` is auto-escaping is in
|
||||
effect and ``False`` otherwise.
|
||||
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 specify this attribute, it defaults to ``False``). This attribute
|
||||
tells Django that your filter function wants to be passed an extra
|
||||
keyword argument, called ``autoescape``, that is ``True`` is
|
||||
auto-escaping is in effect and ``False`` otherwise.
|
||||
|
||||
An example might make this clearer. Let's write a filter that emphasizes
|
||||
the first character of a string::
|
||||
For example, let's write a filter that emphasizes the first character of
|
||||
a string::
|
||||
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
def initial_letter_filter(text, autoescape=None):
|
||||
first, other = text[0] ,text[1:]
|
||||
if autoescape:
|
||||
esc = conditional_escape
|
||||
else:
|
||||
esc = lambda x: x
|
||||
result = '<strong>%s</strong>%s' % (esc(first), esc(other))
|
||||
return mark_safe(result)
|
||||
initial_letter_filter.needs_autoescape = True
|
||||
def initial_letter_filter(text, autoescape=None):
|
||||
first, other = text[0], text[1:]
|
||||
if autoescape:
|
||||
esc = conditional_escape
|
||||
else:
|
||||
esc = lambda x: x
|
||||
result = '<strong>%s</strong>%s' % (esc(first), esc(other))
|
||||
return mark_safe(result)
|
||||
initial_letter_filter.needs_autoescape = True
|
||||
|
||||
The ``needs_autoescape`` attribute on the filter function and the
|
||||
``autoescape`` keyword argument mean that our function will know whether
|
||||
or not automatic escaping is in effect when the filter is called. We use
|
||||
``autoescape`` to decide whether the input data needs to be passed through
|
||||
``django.utils.html.conditional_escape`` or not (in the latter case, we
|
||||
just use the identity function as the "escape" function). The
|
||||
``conditional_escape()`` function is like ``escape()`` except it only
|
||||
escapes input that is **not** a ``SafeData`` instance. If a ``SafeData``
|
||||
instance is passed to ``conditional_escape()``, the data is returned
|
||||
unchanged.
|
||||
The ``needs_autoescape`` attribute on the filter function and the
|
||||
``autoescape`` keyword argument mean that our function will know whether
|
||||
automatic escaping is in effect when the filter is called. We use
|
||||
``autoescape`` to decide whether the input data needs to be passed through
|
||||
``django.utils.html.conditional_escape`` or not. (In the latter case, we
|
||||
just use the identity function as the "escape" function.) The
|
||||
``conditional_escape()`` function is like ``escape()`` except it only
|
||||
escapes input that is **not** a ``SafeData`` instance. If a ``SafeData``
|
||||
instance is passed to ``conditional_escape()``, the data is returned
|
||||
unchanged.
|
||||
|
||||
Finally, in the above example, we remember to mark the result as safe
|
||||
so that our HTML is inserted directly into the template without further
|
||||
escaping.
|
||||
Finally, in the above example, we remember to mark the result as safe
|
||||
so that our HTML is inserted directly into the template without further
|
||||
escaping.
|
||||
|
||||
There is no need to worry about the ``is_safe`` attribute in this case
|
||||
(although including it wouldn't hurt anything). Whenever you are manually
|
||||
handling the auto-escaping issues and returning a safe string, the
|
||||
``is_safe`` attribute won't change anything either way.
|
||||
There's no need to worry about the ``is_safe`` attribute in this case
|
||||
(although including it wouldn't hurt anything). Whenever you manually
|
||||
handle the auto-escaping issues and return a safe string, the
|
||||
``is_safe`` attribute won't change anything either way.
|
||||
|
||||
Writing custom template tags
|
||||
----------------------------
|
||||
@ -981,7 +991,7 @@ Auto-escaping considerations
|
||||
|
||||
The output from template tags is **not** automatically run through the
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
current context's value. The ``__init__`` method for the ``Context`` class
|
||||
takes a parameter called ``autoescape`` that you can use for this purpose. For
|
||||
example::
|
||||
sub-rendering, set the auto-escape attribute to the current context's value.
|
||||
The ``__init__`` method for the ``Context`` class takes a parameter called
|
||||
``autoescape`` that you can use for this purpose. For example::
|
||||
|
||||
def render(self, context):
|
||||
# ...
|
||||
new_context = Context({'var': obj}, autoescape=context.autoescape)
|
||||
# ... Do something with new_context ...
|
||||
|
||||
This is not a very common situation, but it is sometimes useful, particularly
|
||||
if you are rendering a template yourself. For example::
|
||||
This is not a very common situation, but it's useful if you're rendering a
|
||||
template yourself. For example::
|
||||
|
||||
def render(self, context):
|
||||
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
|
||||
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.
|
||||
|
||||
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.,
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
: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
|
||||
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
|
||||
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
|
||||
: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
|
||||
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:
|
||||
|
||||
.. 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
|
||||
: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:
|
||||
|
||||
.. 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
|
||||
: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:
|
||||
|
||||
@ -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,
|
||||
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
|
||||
: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
|
||||
========================
|
||||
@ -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:
|
||||
|
||||
.. 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
|
||||
|
||||
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.
|
||||
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
|
||||
: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
|
||||
``"collapse"`` class that displays a particular fieldset initially collapsed.
|
||||
@ -190,7 +190,7 @@ aren't commonly used::
|
||||
('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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
.. 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
|
||||
: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
|
||||
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
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
.. 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
|
||||
: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
|
||||
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:
|
||||
|
||||
.. 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
|
||||
: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
|
||||
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
|
||||
``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
|
||||
: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.
|
||||
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
|
||||
no fields.
|
||||
|
@ -48,4 +48,9 @@ u'Default headline'
|
||||
>>> d = now - a.pub_date
|
||||
>>> d.seconds < 5
|
||||
True
|
||||
|
||||
# make sure that SafeUnicode fields work
|
||||
>>> from django.utils.safestring import SafeUnicode
|
||||
>>> a.headline = SafeUnicode(u'SafeUnicode Headline')
|
||||
>>> a.save()
|
||||
"""}
|
||||
|
@ -18,42 +18,42 @@ class TaggedItem(models.Model):
|
||||
tag = models.SlugField()
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
object_id = models.PositiveIntegerField()
|
||||
|
||||
|
||||
content_object = generic.GenericForeignKey()
|
||||
|
||||
|
||||
class Meta:
|
||||
ordering = ["tag"]
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return self.tag
|
||||
|
||||
class Animal(models.Model):
|
||||
common_name = models.CharField(max_length=150)
|
||||
latin_name = models.CharField(max_length=150)
|
||||
|
||||
|
||||
tags = generic.GenericRelation(TaggedItem)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.common_name
|
||||
|
||||
|
||||
class Vegetable(models.Model):
|
||||
name = models.CharField(max_length=150)
|
||||
is_yucky = models.BooleanField(default=True)
|
||||
|
||||
|
||||
tags = generic.GenericRelation(TaggedItem)
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Mineral(models.Model):
|
||||
name = models.CharField(max_length=150)
|
||||
hardness = models.PositiveSmallIntegerField()
|
||||
|
||||
|
||||
# note the lack of an explicit GenericRelation here...
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create the world in 7 lines of code...
|
||||
>>> lion = Animal(common_name="Lion", latin_name="Panthera leo")
|
||||
@ -117,13 +117,13 @@ __test__ = {'API_TESTS':"""
|
||||
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
|
||||
[(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
|
||||
|
||||
# If Generic Relation is not explicitly defined, any related objects
|
||||
# If Generic Relation is not explicitly defined, any related objects
|
||||
# remain after deletion of the source object.
|
||||
>>> quartz.delete()
|
||||
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
|
||||
[(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
|
||||
|
||||
# If you delete a tag, the objects using the tag are unaffected
|
||||
# If you delete a tag, the objects using the tag are unaffected
|
||||
# (other than losing a tag)
|
||||
>>> tag = TaggedItem.objects.get(id=1)
|
||||
>>> tag.delete()
|
||||
@ -132,4 +132,8 @@ __test__ = {'API_TESTS':"""
|
||||
>>> [(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)]
|
||||
|
||||
>>> 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())
|
||||
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.
|
||||
>>> get_object_or_404(Article.objects.none(), title__contains="Run")
|
||||
|
@ -108,6 +108,10 @@ class Car(models.Model):
|
||||
colour = models.CharField(max_length=5)
|
||||
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.
|
||||
invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" 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: 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.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
|
||||
model instance.
|
||||
|
||||
The function django.newforms.form_for_model() takes a model class and returns
|
||||
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.
|
||||
This is mostly just a reworking of the form_for_model/form_for_instance tests
|
||||
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,
|
||||
most of these tests should be rewritten.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
@ -30,23 +15,6 @@ ARTICLE_STATUS = (
|
||||
(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):
|
||||
name = models.CharField(max_length=20)
|
||||
slug = models.SlugField(max_length=20)
|
||||
@ -87,20 +55,124 @@ class PhoneNumber(models.Model):
|
||||
def __unicode__(self):
|
||||
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': """
|
||||
>>> 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
|
||||
|
||||
>>> Category.objects.all()
|
||||
[]
|
||||
|
||||
>>> CategoryForm = form_for_model(Category)
|
||||
>>> class CategoryForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Category
|
||||
>>> f = CategoryForm()
|
||||
>>> print f
|
||||
<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
|
||||
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)
|
||||
>>> print f
|
||||
<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
|
||||
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!
|
||||
>>> 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)
|
||||
>>> print f
|
||||
<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>
|
||||
|
||||
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
|
||||
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.
|
||||
>>> w = Writer.objects.get(name='Mike Royko')
|
||||
>>> RoykoForm = form_for_instance(w)
|
||||
>>> f = RoykoForm(auto_id=False)
|
||||
>>> class RoykoForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Writer
|
||||
>>> f = RoykoForm(auto_id=False, instance=w)
|
||||
>>> 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>
|
||||
|
||||
@ -243,8 +312,10 @@ current values are inserted as 'initial' data in each Field.
|
||||
>>> art.save()
|
||||
>>> art.id
|
||||
1
|
||||
>>> TestArticleForm = form_for_instance(art)
|
||||
>>> f = TestArticleForm(auto_id=False)
|
||||
>>> class TestArticleForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Article
|
||||
>>> f = TestArticleForm(auto_id=False, instance=art)
|
||||
>>> print f.as_ul()
|
||||
<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>
|
||||
@ -266,7 +337,7 @@ current values are inserted as 'initial' data in each Field.
|
||||
<option value="2">It's a test</option>
|
||||
<option value="3">Third test</option>
|
||||
</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()
|
||||
True
|
||||
>>> test_art = f.save()
|
||||
@ -278,8 +349,11 @@ u'Test headline'
|
||||
|
||||
You can create a form over a subset of the available fields
|
||||
by specifying a 'fields' argument to form_for_instance.
|
||||
>>> PartialArticleForm = form_for_instance(art, fields=('headline', 'slug', 'pub_date'))
|
||||
>>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False)
|
||||
>>> class PartialArticleForm(ModelForm):
|
||||
... 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()
|
||||
<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>
|
||||
@ -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.all()
|
||||
[<Category: Entertainment>]
|
||||
>>> TestArticleForm = form_for_instance(new_art)
|
||||
>>> f = TestArticleForm(auto_id=False)
|
||||
>>> class TestArticleForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = Article
|
||||
>>> f = TestArticleForm(auto_id=False, instance=new_art)
|
||||
>>> print f.as_ul()
|
||||
<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>
|
||||
@ -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>
|
||||
|
||||
>>> 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.id
|
||||
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.
|
||||
>>> 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.id
|
||||
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.
|
||||
>>> 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',
|
||||
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
||||
>>> new_art = f.save()
|
||||
@ -354,7 +432,9 @@ Create a new article, with categories, via the form.
|
||||
[<Category: Entertainment>, <Category: It's a test>]
|
||||
|
||||
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',
|
||||
... 'writer': u'1', 'article': u'Test.'})
|
||||
>>> 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.
|
||||
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',
|
||||
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
|
||||
>>> 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')
|
||||
[<Category: Entertainment>, <Category: It's a test>]
|
||||
|
||||
Here, we define a custom Form. Because it happens to have the same fields as
|
||||
the Category model, we can use save_instance() to apply its changes to an
|
||||
Here, we define a custom ModelForm. Because it happens to have the same fields as
|
||||
the Category model, we can just call the form's save() to apply its changes to an
|
||||
existing Category instance.
|
||||
>>> class ShortCategory(Form):
|
||||
>>> class ShortCategory(ModelForm):
|
||||
... name = CharField(max_length=5)
|
||||
... slug = CharField(max_length=5)
|
||||
... url = CharField(max_length=3)
|
||||
@ -398,8 +480,8 @@ existing Category instance.
|
||||
<Category: Third test>
|
||||
>>> cat.id
|
||||
3
|
||||
>>> sc = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'})
|
||||
>>> save_instance(sc, cat)
|
||||
>>> form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat)
|
||||
>>> form.save()
|
||||
<Category: Third>
|
||||
>>> Category.objects.get(id=3)
|
||||
<Category: Third>
|
||||
@ -407,7 +489,9 @@ existing Category instance.
|
||||
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
|
||||
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)
|
||||
>>> print f.as_ul()
|
||||
<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 ############################################################
|
||||
|
||||
>>> PhoneNumberForm = form_for_model(PhoneNumber)
|
||||
>>> class PhoneNumberForm(ModelForm):
|
||||
... class Meta:
|
||||
... model = PhoneNumber
|
||||
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
||||
>>> f.is_valid()
|
||||
True
|
||||
>>> f.cleaned_data
|
||||
{'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
|
||||
values so that complex trees can be fetched in a single query. However, this
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
41. Serialization
|
||||
42. Serialization
|
||||
|
||||
``django.core.serializers`` provides interfaces to converting Django querysets
|
||||
to and from "flat" data (i.e. strings).
|
||||
@ -218,3 +218,41 @@ None
|
||||
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
|
||||
"""
|
||||
38. Testing using the Test Client
|
||||
39. Testing using the Test Client
|
||||
|
||||
The test client is a class that can act like a simple
|
||||
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
|
||||
Django project. If you want to add a utility command of your own, you can.
|
||||
@ -27,4 +27,4 @@ Traceback (most recent call last):
|
||||
CommandError: Unknown command: 'explode'
|
||||
|
||||
|
||||
"""}
|
||||
"""}
|
||||
|
60
tests/regressiontests/cache/tests.py
vendored
60
tests/regressiontests/cache/tests.py
vendored
@ -3,8 +3,8 @@
|
||||
# Unit tests for cache framework
|
||||
# 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.utils.cache import patch_vary_headers
|
||||
from django.http import HttpResponse
|
||||
@ -27,7 +27,7 @@ class Cache(unittest.TestCase):
|
||||
cache.add("addkey1", "value")
|
||||
cache.add("addkey1", "newvalue")
|
||||
self.assertEqual(cache.get("addkey1"), "value")
|
||||
|
||||
|
||||
def test_non_existent(self):
|
||||
# get with non-existent keys
|
||||
self.assertEqual(cache.get("does_not_exist"), None)
|
||||
@ -72,12 +72,20 @@ class Cache(unittest.TestCase):
|
||||
'function' : f,
|
||||
'class' : C,
|
||||
}
|
||||
cache.set("stuff", stuff)
|
||||
self.assertEqual(cache.get("stuff"), stuff)
|
||||
|
||||
def test_expiration(self):
|
||||
# expiration
|
||||
cache.set('expire', 'very quickly', 1)
|
||||
time.sleep(2)
|
||||
self.assertEqual(cache.get("expire"), None)
|
||||
cache.set('expire1', 'very quickly', 1)
|
||||
cache.set('expire2', 'very quickly', 1)
|
||||
cache.set('expire3', 'very quickly', 1)
|
||||
|
||||
time.sleep(2)
|
||||
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):
|
||||
stuff = {
|
||||
@ -90,6 +98,44 @@ class Cache(unittest.TestCase):
|
||||
cache.set(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):
|
||||
"""TestCase for django.utils.cache functions."""
|
||||
|
@ -49,6 +49,18 @@ u'\\\\ : backslashes, too'
|
||||
>>> capfirst(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')
|
||||
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.uk import tests as localflavor_uk_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 util import tests as util_tests
|
||||
from widgets import tests as widgets_tests
|
||||
@ -50,6 +51,7 @@ __test__ = {
|
||||
'localflavor_sk_tests': localflavor_sk_tests,
|
||||
'localflavor_uk_tests': localflavor_uk_tests,
|
||||
'localflavor_us_tests': localflavor_us_tests,
|
||||
'localflavor_za_tests': localflavor_za_tests,
|
||||
'regression_tests': regression_tests,
|
||||
'util_tests': util_tests,
|
||||
'widgets_tests': widgets_tests,
|
||||
|
@ -43,7 +43,7 @@ u'django'
|
||||
Translating a string requiring no auto-escaping shouldn't change the "safe"
|
||||
status.
|
||||
|
||||
>>> from django.utils.safestring import mark_safe
|
||||
>>> from django.utils.safestring import mark_safe, SafeString
|
||||
>>> s = mark_safe('Password')
|
||||
>>> type(s)
|
||||
<class 'django.utils.safestring.SafeString'>
|
||||
@ -51,6 +51,19 @@ status.
|
||||
>>> type(ugettext(s))
|
||||
<class 'django.utils.safestring.SafeUnicode'>
|
||||
>>> 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__ = {
|
||||
|
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