Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

gis: Merged revisions 6672,6686-6688,6690,6693,6707-6708,6726,6730,67…

…53,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
  • Loading branch information...
commit 34560a01daee3c42a7c5ec462f38a485cccf4df7 1 parent 5799c2e
Justin Bronn jbronn authored
Showing with 3,823 additions and 1,644 deletions.
  1. +8 −0 AUTHORS
  2. +3 −3 django/conf/__init__.py
  3. +5 −1 django/conf/global_settings.py
  4. BIN  django/conf/locale/ca/LC_MESSAGES/django.mo
  5. +198 −194 django/conf/locale/ca/LC_MESSAGES/django.po
  6. BIN  django/conf/locale/ca/LC_MESSAGES/djangojs.mo
  7. +8 −6 django/conf/locale/ca/LC_MESSAGES/djangojs.po
  8. +1 −1  django/contrib/admin/templatetags/admin_list.py
  9. +1 −1  django/contrib/auth/models.py
  10. +12 −0 django/contrib/auth/tests.py
  11. +21 −21 django/contrib/contenttypes/generic.py
  12. 0  django/contrib/localflavor/mx/__init__.py
  13. +14 −0 django/contrib/localflavor/mx/forms.py
  14. +45 −0 django/contrib/localflavor/mx/mx_states.py
  15. +17 −1 django/contrib/localflavor/uk/forms.py
  16. +97 −0 django/contrib/localflavor/uk/uk_regions.py
  17. 0  django/contrib/localflavor/za/__init__.py
  18. +57 −0 django/contrib/localflavor/za/forms.py
  19. +13 −0 django/contrib/localflavor/za/za_provinces.py
  20. +29 −2 django/contrib/markup/templatetags/markup.py
  21. +8 −1 django/contrib/markup/tests.py
  22. +8 −0 django/contrib/sessions/backends/base.py
  23. +11 −2 django/contrib/sessions/backends/file.py
  24. +0 −34 django/contrib/sessions/models.py
  25. +13 −0 django/contrib/sessions/tests.py
  26. +10 −1 django/core/cache/__init__.py
  27. +90 −41 django/core/cache/backends/filebased.py
  28. +81 −24 django/core/cache/backends/locmem.py
  29. +0 −73 django/core/cache/backends/simple.py
  30. +4 −0 django/core/exceptions.py
  31. +3 −2 django/core/handlers/base.py
  32. +5 −4 django/core/handlers/modpython.py
  33. +2 −2 django/core/handlers/wsgi.py
  34. +36 −46 django/core/management/__init__.py
  35. +3 −3 django/core/management/commands/loaddata.py
  36. +3 −2 django/core/management/commands/syncdb.py
  37. +12 −4 django/core/management/validation.py
  38. +15 −1 django/core/serializers/pyyaml.py
  39. +2 −2 django/db/__init__.py
  40. +23 −13 django/db/backends/oracle/base.py
  41. +2 −0  django/db/backends/postgresql_psycopg2/base.py
  42. +3 −1 django/db/models/base.py
  43. +1 −1  django/db/models/fields/__init__.py
  44. +3 −3 django/db/models/options.py
  45. +2 −1  django/db/models/query.py
  46. +1 −1  django/http/__init__.py
  47. +2 −3 django/http/utils.py
  48. +43 −15 django/middleware/common.py
  49. +10 −16 django/newforms/fields.py
  50. +166 −2 django/newforms/models.py
  51. +1 −1  django/shortcuts/__init__.py
  52. +5 −2 django/template/context.py
  53. +26 −2 django/template/defaultfilters.py
  54. +11 −0 django/test/_doctest.py
  55. +3 −3 django/utils/datastructures.py
  56. +12 −12 django/utils/html.py
  57. +8 −10 django/utils/maxlength.py
  58. +9 −12 django/utils/safestring.py
  59. +63 −30 django/views/debug.py
  60. +1 −1  django/views/generic/date_based.py
  61. +1 −1  django/views/generic/list_detail.py
  62. +30 −11 docs/add_ons.txt
  63. +54 −1 docs/authentication.txt
  64. +4 −0 docs/cache.txt
  65. +12 −7 docs/contributing.txt
  66. +144 −129 docs/custom_model_fields.txt
  67. +5 −0 docs/databases.txt
  68. +5 −3 docs/email.txt
  69. +418 −0 docs/form_for_model.txt
  70. +2 −2 docs/generic_views.txt
  71. +9 −7 docs/install.txt
  72. +654 −0 docs/localflavor.txt
  73. +14 −5 docs/middleware.txt
  74. +3 −2 docs/model-api.txt
  75. +313 −0 docs/modelforms.txt
  76. +22 −425 docs/newforms.txt
  77. +11 −7 docs/request_response.txt
  78. +2 −7 docs/serialization.txt
  79. +2 −0  docs/sessions.txt
  80. +44 −3 docs/settings.txt
  81. +2 −2 docs/shortcuts.txt
  82. +8 −5 docs/syndication_feeds.txt
  83. +170 −103 docs/templates.txt
  84. +139 −130 docs/templates_python.txt
  85. +25 −25 docs/tutorial02.txt
  86. +1 −1  tests/modeltests/empty/models.py
  87. +5 −0 tests/modeltests/field_defaults/models.py
  88. +17 −13 tests/modeltests/generic_relations/models.py
  89. +1 −1  tests/modeltests/get_object_or_404/models.py
  90. +6 −0 tests/modeltests/invalid_models/models.py
  91. +165 −129 tests/modeltests/model_forms/models.py
  92. +1 −1  tests/modeltests/select_related/models.py
  93. +39 −1 tests/modeltests/serializers/models.py
  94. +1 −1  tests/modeltests/test_client/models.py
  95. +2 −2 tests/modeltests/user_commands/models.py
  96. +53 −7 tests/regressiontests/cache/tests.py
  97. +12 −0 tests/regressiontests/defaultfilters/tests.py
  98. +40 −0 tests/regressiontests/forms/localflavor/za.py
  99. +2 −0  tests/regressiontests/forms/tests.py
  100. +14 −1 tests/regressiontests/i18n/tests.py
  101. +3 −3 tests/regressiontests/maxlength/tests.py
  102. 0  tests/regressiontests/middleware/__init__.py
  103. +93 −0 tests/regressiontests/middleware/tests.py
  104. +7 −0 tests/regressiontests/middleware/urls.py
  105. +10 −0 tests/regressiontests/model_regress/models.py
  106. +4 −4 tests/regressiontests/string_lookup/models.py
  107. +18 −0 tests/regressiontests/templates/context.py
  108. +2 −0  tests/regressiontests/templates/tests.py
  109. +0 −2  tests/regressiontests/views/views.py
  110. +1 −2  tests/runtests.py
  111. +3 −0  tests/urls.py
8 AUTHORS
View
@@ -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
6 django/conf/__init__.py
View
@@ -52,7 +52,7 @@ def _import_settings(self):
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 @@ def configure(self, default_settings=global_settings, **options):
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 @@ def __init__(self, settings_module):
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.
6 django/conf/global_settings.py
View
@@ -253,6 +253,10 @@
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 @@
# 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
BIN  django/conf/locale/ca/LC_MESSAGES/django.mo
View
Binary file not shown
392 django/conf/locale/ca/LC_MESSAGES/django.po
View
@@ -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"
BIN  django/conf/locale/ca/LC_MESSAGES/djangojs.mo
View
Binary file not shown
14 django/conf/locale/ca/LC_MESSAGES/djangojs.po
View
@@ -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"
+
2  django/contrib/admin/templatetags/admin_list.py
View
@@ -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'}
2  django/contrib/auth/models.py
View
@@ -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()
12 django/contrib/auth/tests.py
View
@@ -16,9 +16,21 @@
>>> 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()
42 django/contrib/contenttypes/generic.py
View
@@ -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 @@ def contribute_to_class(self, cls, name):
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 @@ def __set__(self, instance, value):
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 @@ def m2m_db_table(self):
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 @@ def contribute_to_class(self, cls, name):
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 __init__(self, model=None, core_filters=None, instance=None, symmetrical=Non
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
View
No changes.
14 django/contrib/localflavor/mx/forms.py
View
@@ -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
View
@@ -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')),
+)
+
18 django/contrib/localflavor/uk/forms.py
View
@@ -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 @@ def __init__(self, *args, **kwargs):
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
View
@@ -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
View
No changes.
57 django/contrib/localflavor/za/forms.py
View
@@ -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
View
@@ -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')),
+)
31 django/contrib/markup/templatetags/markup.py
View
@@ -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):
9 django/contrib/markup/tests.py
View
@@ -61,8 +61,15 @@ def test_docutils(self):
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)
8 django/contrib/sessions/backends/base.py
View
@@ -51,6 +51,14 @@ def pop(self, key, *args):
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
13 django/contrib/sessions/backends/file.py
View
@@ -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)
34 django/contrib/sessions/models.py
View
@@ -18,40 +18,6 @@ def encode(self, session_dict):
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:
13 django/contrib/sessions/tests.py
View
@@ -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 @@
>>> 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 @@
>>> 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
View
@@ -22,19 +22,28 @@
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
View
@@ -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 @@ def get(self, key, default=None):
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 @@ def get(self, key, default=None):
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 @@ def _createdir(self):
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
View
@@ -6,65 +6,122 @@
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: