1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

newforms-admin: Merged from trunk up to [7729].

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7730 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Brian Rosner 2008-06-23 21:05:02 +00:00
parent c929440fcd
commit 420f19aa35
26 changed files with 1056 additions and 954 deletions

View File

@ -5,8 +5,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-05-26 10:35+0200\n"
"PO-Revision-Date: 2008-05-14 11:15+0200\n"
"POT-Creation-Date: 2008-06-20 12:36+0200\n"
"PO-Revision-Date: 2008-06-20 12:48+0200\n"
"Last-Translator: Django Catalan Group <django-cat@googlegroups.com>\n"
"Language-Team: Catalan <ca@li.org>\n"
"MIME-Version: 1.0\n"
@ -14,193 +14,201 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: conf/global_settings.py:43
msgid "Arabic"
msgstr "Arabic"
#: conf/global_settings.py:44
msgid "Bengali"
msgstr "Bengalí"
msgid "Arabic"
msgstr "aràbic"
#: conf/global_settings.py:45
msgid "Bulgarian"
msgstr "Búlgar"
msgid "Bengali"
msgstr "bengalí"
#: conf/global_settings.py:46
msgid "Catalan"
msgstr "Català"
msgid "Bulgarian"
msgstr "búlgar"
#: conf/global_settings.py:47
msgid "Czech"
msgstr "Txec"
msgid "Catalan"
msgstr "català"
#: conf/global_settings.py:48
msgid "Welsh"
msgstr "Galès"
msgid "Czech"
msgstr "txec"
#: conf/global_settings.py:49
msgid "Danish"
msgstr "Danès"
msgid "Welsh"
msgstr "galès"
#: conf/global_settings.py:50
msgid "German"
msgstr "Alemany"
msgid "Danish"
msgstr "danès"
#: conf/global_settings.py:51
msgid "Greek"
msgstr "Grec"
msgid "German"
msgstr "alemany"
#: conf/global_settings.py:52
msgid "English"
msgstr "Anglès"
msgid "Greek"
msgstr "grec"
#: conf/global_settings.py:53
msgid "Spanish"
msgstr "Espanyol"
msgid "English"
msgstr "anglès"
#: conf/global_settings.py:54
msgid "Argentinean Spanish"
msgstr "Castellà Argentí"
msgid "Spanish"
msgstr "espanyol"
#: conf/global_settings.py:55
msgid "Basque"
msgstr "Euskera"
msgid "Estonian"
msgstr "estonià"
#: conf/global_settings.py:56
msgid "Persian"
msgstr "Persa"
msgid "Argentinean Spanish"
msgstr "castellà argentí"
#: conf/global_settings.py:57
msgid "Finnish"
msgstr "Finlandès"
msgid "Basque"
msgstr "euskera"
#: conf/global_settings.py:58
msgid "French"
msgstr "Francès"
msgid "Persian"
msgstr "persa"
#: conf/global_settings.py:59
msgid "Irish"
msgstr "Irlandès"
msgid "Finnish"
msgstr "finlandès"
#: conf/global_settings.py:60
msgid "Galician"
msgstr "Gallec"
msgid "French"
msgstr "francès"
#: conf/global_settings.py:61
msgid "Hungarian"
msgstr "Húngar"
msgid "Irish"
msgstr "irlandès"
#: conf/global_settings.py:62
msgid "Hebrew"
msgstr "Hebreu"
msgid "Galician"
msgstr "gallec"
#: conf/global_settings.py:63
msgid "Croatian"
msgstr "Croat"
msgid "Hungarian"
msgstr "húngar"
#: conf/global_settings.py:64
msgid "Icelandic"
msgstr "Islandès"
msgid "Hebrew"
msgstr "hebreu"
#: conf/global_settings.py:65
msgid "Italian"
msgstr "Italià"
msgid "Croatian"
msgstr "croat"
#: conf/global_settings.py:66
msgid "Japanese"
msgstr "Japonès"
msgid "Icelandic"
msgstr "islandès"
#: conf/global_settings.py:67
msgid "Georgian"
msgstr "Georgià"
msgid "Italian"
msgstr "italià"
#: conf/global_settings.py:68
msgid "Korean"
msgstr "Coreà"
msgid "Japanese"
msgstr "japonès"
#: conf/global_settings.py:69
msgid "Khmer"
msgstr "Khmer"
msgid "Georgian"
msgstr "georgià"
#: conf/global_settings.py:70
msgid "Kannada"
msgstr "Canès"
msgid "Korean"
msgstr "coreà"
#: conf/global_settings.py:71
msgid "Latvian"
msgstr "Letó"
msgid "khmer"
msgstr "khmer"
#: conf/global_settings.py:72
msgid "Macedonian"
msgstr "Macedoni"
msgid "Kannada"
msgstr "canès"
#: conf/global_settings.py:73
msgid "Dutch"
msgstr "Holandès"
msgid "Latvian"
msgstr "letó"
#: conf/global_settings.py:74
msgid "Norwegian"
msgstr "Norueg"
msgid "Lithuanian"
msgstr "lituà"
#: conf/global_settings.py:75
msgid "Polish"
msgstr "Polac"
msgid "Macedonian"
msgstr "macedoni"
#: conf/global_settings.py:76
msgid "Portugese"
msgstr "Portuguès"
msgid "Dutch"
msgstr "holandès"
#: conf/global_settings.py:77
msgid "Brazilian Portuguese"
msgstr "Portuguès de Brasil"
msgid "Norwegian"
msgstr "norueg"
#: conf/global_settings.py:78
msgid "Romanian"
msgstr "Rumanès"
msgid "Polish"
msgstr "polac"
#: conf/global_settings.py:79
msgid "Russian"
msgstr "s"
msgid "Portugese"
msgstr "portuguès"
#: conf/global_settings.py:80
msgid "Slovak"
msgstr "Eslovac"
msgid "Brazilian Portuguese"
msgstr "portuguès de brasil"
#: conf/global_settings.py:81
msgid "Slovenian"
msgstr "Esloveni"
msgid "Romanian"
msgstr "rumanès"
#: conf/global_settings.py:82
msgid "Serbian"
msgstr "Serbi"
msgid "Russian"
msgstr "rús"
#: conf/global_settings.py:83
msgid "Swedish"
msgstr "Suec"
msgid "Slovak"
msgstr "eslovac"
#: conf/global_settings.py:84
msgid "Tamil"
msgstr "Tàmil"
msgid "Slovenian"
msgstr "esloveni"
#: conf/global_settings.py:85
msgid "Telugu"
msgstr "Telugu"
msgid "Serbian"
msgstr "serbi"
#: conf/global_settings.py:86
msgid "Turkish"
msgstr "Turc"
msgid "Swedish"
msgstr "suec"
#: conf/global_settings.py:87
msgid "Ukrainian"
msgstr "Ucranià"
msgid "Tamil"
msgstr "tàmil"
#: conf/global_settings.py:88
msgid "Simplified Chinese"
msgstr "Xinès simplificat"
msgid "Telugu"
msgstr "telugu"
#: conf/global_settings.py:89
msgid "Turkish"
msgstr "turc"
#: conf/global_settings.py:90
msgid "Ukrainian"
msgstr "ucranià"
#: conf/global_settings.py:91
msgid "Simplified Chinese"
msgstr "xinès simplificat"
#: conf/global_settings.py:92
msgid "Traditional Chinese"
msgstr "Xinès tradicional"
msgstr "xinès tradicional"
#: contrib/admin/filterspecs.py:44
#, python-format
@ -1137,15 +1145,15 @@ msgstr "permisos"
msgid "group"
msgstr "grup"
#: contrib/auth/models.py:98 contrib/auth/models.py:141
#: contrib/auth/models.py:98 contrib/auth/models.py:148
msgid "groups"
msgstr "grups"
#: contrib/auth/models.py:131
#: contrib/auth/models.py:138
msgid "username"
msgstr "nom d'usuari"
#: contrib/auth/models.py:131
#: contrib/auth/models.py:138
msgid ""
"Required. 30 characters or fewer. Alphanumeric characters only (letters, "
"digits and underscores)."
@ -1153,23 +1161,23 @@ msgstr ""
"Obligatori. 30 o menys caràcters. Només caràcters alfanumèrics (lletres, "
"dígits i el guió baix)."
#: contrib/auth/models.py:132
#: contrib/auth/models.py:139
msgid "first name"
msgstr "nom propi"
#: contrib/auth/models.py:133
#: contrib/auth/models.py:140
msgid "last name"
msgstr "cognoms"
#: contrib/auth/models.py:134
#: contrib/auth/models.py:141
msgid "e-mail address"
msgstr "adreça de correu electrònic"
#: contrib/auth/models.py:135
#: contrib/auth/models.py:142
msgid "password"
msgstr "contrasenya"
#: contrib/auth/models.py:135
#: contrib/auth/models.py:142
msgid ""
"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
"password form</a>."
@ -1177,19 +1185,19 @@ msgstr ""
"Utilitzeu '[algo]$[salt]$[hexdigest]' o el <a href=\"password/\">formulari "
"de canvi de contrasenya</a>."
#: contrib/auth/models.py:136
#: contrib/auth/models.py:143
msgid "staff status"
msgstr "membre del personal"
#: contrib/auth/models.py:136
#: contrib/auth/models.py:143
msgid "Designates whether the user can log into this admin site."
msgstr "Indica si l'usuari pot entrar en el lloc administratiu."
#: contrib/auth/models.py:137
#: contrib/auth/models.py:144
msgid "active"
msgstr "actiu"
#: contrib/auth/models.py:137
#: contrib/auth/models.py:144
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
@ -1197,11 +1205,11 @@ msgstr ""
"Designa si aquest usuari ha de ser tractat com a actiu. Deseleccioneu-ho "
"enlloc d'esborrar comptes d'usuari."
#: contrib/auth/models.py:138
#: contrib/auth/models.py:145
msgid "superuser status"
msgstr "estat de superusuari"
#: contrib/auth/models.py:138
#: contrib/auth/models.py:145
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
@ -1209,15 +1217,15 @@ msgstr ""
"Designa que aquest usuari té tots els permissos sense assignar-los "
"explícitament."
#: contrib/auth/models.py:139
#: contrib/auth/models.py:146
msgid "last login"
msgstr "últim inici de sessió"
#: contrib/auth/models.py:140
#: contrib/auth/models.py:147
msgid "date joined"
msgstr "data de creació"
#: contrib/auth/models.py:142
#: contrib/auth/models.py:149
msgid ""
"In addition to the permissions manually assigned, this user will also get "
"all permissions granted to each group he/she is in."
@ -1225,39 +1233,39 @@ msgstr ""
"Junt amb els permissos asignats manualment, aquest usuari tindrà, també, els "
"permissos dels grups dels que sigui membre."
#: contrib/auth/models.py:143
#: contrib/auth/models.py:150
msgid "user permissions"
msgstr "permissos de l'usuari"
#: contrib/auth/models.py:147
#: contrib/auth/models.py:154
msgid "user"
msgstr "usuari"
#: contrib/auth/models.py:148
#: contrib/auth/models.py:155
msgid "users"
msgstr "usuaris"
#: contrib/auth/models.py:154
#: contrib/auth/models.py:161
msgid "Personal info"
msgstr "Informació personal"
#: contrib/auth/models.py:155
#: contrib/auth/models.py:162
msgid "Permissions"
msgstr "permisos"
#: contrib/auth/models.py:156
#: contrib/auth/models.py:163
msgid "Important dates"
msgstr "Dates importants"
#: contrib/auth/models.py:157
#: contrib/auth/models.py:164
msgid "Groups"
msgstr "Grups"
#: contrib/auth/models.py:316
#: contrib/auth/models.py:323
msgid "message"
msgstr "missatge"
#: contrib/auth/views.py:47
#: contrib/auth/views.py:48
msgid "Logged out"
msgstr "Sessió finalitzada"
@ -3897,12 +3905,12 @@ msgstr ""
msgid "%(object)s with this %(type)s already exists for the given %(field)s."
msgstr "Ja existeix un %(object)s del tipus %(type)s amb aquest %(field)s."
#: db/models/fields/__init__.py:54
#: db/models/fields/__init__.py:51
#, python-format
msgid "%(optname)s with this %(fieldname)s already exists."
msgstr "Ja existeix %(optname)s amb aquest %(fieldname)s."
#: db/models/fields/__init__.py:179 db/models/fields/__init__.py:348
#: db/models/fields/__init__.py:176 db/models/fields/__init__.py:348
#: db/models/fields/__init__.py:780 db/models/fields/__init__.py:791
#: newforms/fields.py:46 oldforms/__init__.py:374
msgid "This field is required."
@ -3937,18 +3945,18 @@ msgstr "Aquest valor ha de ser None (Cap), True (Veritat) o False (Fals)"
msgid "Please enter a valid %s."
msgstr "Si us plau, introduïu un %s vàlid."
#: db/models/fields/related.py:721
#: db/models/fields/related.py:746
msgid "Separate multiple IDs with commas."
msgstr "Separi múltiples IDs amb comes."
#: db/models/fields/related.py:723
#: db/models/fields/related.py:748
msgid ""
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
msgstr ""
"Premeu la tecla \"Control\" -o \"Command\" en un Mac- per seleccionar més "
"d'un valor."
#: db/models/fields/related.py:770
#: db/models/fields/related.py:795
#, python-format
msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
msgid_plural ""
@ -4007,11 +4015,11 @@ msgstr "Assegureu-vos de que no hi ha més de %s decimals."
msgid "Ensure that there are no more than %s digits before the decimal point."
msgstr "Assegureu-vos de que no hi ha més de %s dígits decimals."
#: newforms/fields.py:263 newforms/fields.py:751
#: newforms/fields.py:263 newforms/fields.py:750
msgid "Enter a valid date."
msgstr "Introduïu una data vàlida."
#: newforms/fields.py:296 newforms/fields.py:752
#: newforms/fields.py:296 newforms/fields.py:751
msgid "Enter a valid time."
msgstr "Introduïu una hora vàlida."
@ -4035,25 +4043,25 @@ msgstr "Introduïu una URL vàlida."
msgid "This URL appears to be a broken link."
msgstr "Aquesta URL sembla ser un enllaç trencat."
#: newforms/fields.py:560 newforms/models.py:299
#: newforms/fields.py:559 newforms/models.py:305
msgid "Select a valid choice. That choice is not one of the available choices."
msgstr ""
"Escolli una opció vàlida; Aquesta opció no és una de les opcions disponibles."
#: newforms/fields.py:599
#: newforms/fields.py:598
#, python-format
msgid "Select a valid choice. %(value)s is not one of the available choices."
msgstr "Esculliu 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:371
#: newforms/fields.py:599 newforms/fields.py:661 newforms/models.py:372
msgid "Enter a list of values."
msgstr "Introduïu una llista de valors."
#: newforms/fields.py:780
#: newforms/fields.py:779
msgid "Enter a valid IPv4 address."
msgstr "Introduïu una adreça IPv4 vàlida."
#: newforms/models.py:372
#: newforms/models.py:373
#, python-format
msgid "Select a valid choice. %s is not one of the available choices."
msgstr "Escolliu una opció vàlida; %s' no és una de les opcions vàlides."
@ -4194,51 +4202,51 @@ msgstr "Dg"
#: utils/dates.py:18
msgid "January"
msgstr "Gener"
msgstr "gener"
#: utils/dates.py:18
msgid "February"
msgstr "Febrer"
msgstr "febrer"
#: utils/dates.py:18 utils/dates.py:31
msgid "March"
msgstr "Març"
msgstr "març"
#: utils/dates.py:18 utils/dates.py:31
msgid "April"
msgstr "Abril"
msgstr "abril"
#: utils/dates.py:18 utils/dates.py:31
msgid "May"
msgstr "Maig"
msgstr "maig"
#: utils/dates.py:18 utils/dates.py:31
msgid "June"
msgstr "Juny"
msgstr "juny"
#: utils/dates.py:19 utils/dates.py:31
msgid "July"
msgstr "Juliol"
msgstr "juliol"
#: utils/dates.py:19
msgid "August"
msgstr "Agost"
msgstr "agost"
#: utils/dates.py:19
msgid "September"
msgstr "Setembre"
msgstr "setembre"
#: utils/dates.py:19
msgid "October"
msgstr "Octubre"
msgstr "octubre"
#: utils/dates.py:19
msgid "November"
msgstr "Novembre"
msgstr "novembre"
#: utils/dates.py:20
msgid "December"
msgstr "Desembre"
msgstr "desembre"
#: utils/dates.py:23
msgid "jan"
@ -4290,31 +4298,31 @@ msgstr "des"
#: utils/dates.py:31
msgid "Jan."
msgstr "Gen."
msgstr "gen."
#: utils/dates.py:31
msgid "Feb."
msgstr "Feb."
msgstr "feb."
#: utils/dates.py:32
msgid "Aug."
msgstr "Ago."
msgstr "ago."
#: utils/dates.py:32
msgid "Sept."
msgstr "Set."
msgstr "set."
#: utils/dates.py:32
msgid "Oct."
msgstr "Oct."
msgstr "oct."
#: utils/dates.py:32
msgid "Nov."
msgstr "Nov."
msgstr "nov."
#: utils/dates.py:32
msgid "Dec."
msgstr "Des."
msgstr "des."
#: utils/text.py:127
msgid "or"

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,13 @@ msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-12-09 11:51+0100\n"
"PO-Revision-Date: 2005-12-04 13:21+0100\n"
"Last-Translator: Dirk Eschler <dirk.eschler@gmx.net>\n"
"POT-Creation-Date: 2008-06-23 20:52+0200\n"
"PO-Revision-Date: 2008-06-23 21:02+0100\n"
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language-Team: \n"
#: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format
@ -38,82 +39,78 @@ msgstr "Ausgewählte %s"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
msgstr "Gewünschte Auswahl treffen und "
msgstr "Auswahl treffen und Klick auf"
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "Alles abwählen"
#: contrib/admin/media/js/dateparse.js:26
#: contrib/admin/media/js/calendar.js:24
msgid ""
"January February March April May June July August September October November "
"December"
msgstr ""
"Januar Februar März April Mai Juni Juli August September Oktober November "
"Dezember"
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag"
#: contrib/admin/media/js/dateparse.js:32
msgid "January February March April May June July August September October November December"
msgstr "Januar Februar März April Mai Juni Juli August September Oktober November Dezember"
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
msgstr "S M D M D F S"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
msgid "Now"
msgstr "Jetzt"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
msgid "Clock"
msgstr "Uhr"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
msgid "Choose a time"
msgstr "Uhrzeit"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Midnight"
msgstr "Mitternacht"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "6 a.m."
msgstr "6 Uhr"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "Noon"
msgstr "Mittag"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
msgid "Cancel"
msgstr "Abbrechen"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
msgid "Today"
msgstr "Heute"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
msgid "Calendar"
msgstr "Kalender"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
msgid "Yesterday"
msgstr "Gestern"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
msgid "Tomorrow"
msgstr "Morgen"
#: contrib/admin/media/js/dateparse.js:33
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag"
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:34
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:72
msgid "Show"
msgstr "Anzeigen"
msgstr "Einblenden"
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
msgid "Hide"
msgstr "Verbergen"
msgstr "Ausblenden"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:47
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Now"
msgstr "Jetzt"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:51
msgid "Clock"
msgstr "Uhr"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:78
msgid "Choose a time"
msgstr "Uhrzeit"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "Midnight"
msgstr "Mitternacht"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "6 a.m."
msgstr "6 Uhr"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
msgid "Noon"
msgstr "Mittag"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
msgid "Cancel"
msgstr "Abbrechen"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:128
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:177
msgid "Today"
msgstr "Heute"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:132
msgid "Calendar"
msgstr "Kalender"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:175
msgid "Yesterday"
msgstr "Gestern"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
msgid "Tomorrow"
msgstr "Morgen"

View File

@ -5,14 +5,15 @@ import random
import sys
import time
from datetime import datetime, timedelta
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
try:
import cPickle as pickle
except ImportError:
import pickle
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
class SessionBase(object):
"""
Base class for all Session classes.
@ -169,8 +170,8 @@ class SessionBase(object):
def set_expiry(self, value):
"""
Sets a custom expiration for the session. ``value`` can be an integer, a
Python ``datetime`` or ``timedelta`` object or ``None``.
Sets a custom expiration for the session. ``value`` can be an integer,
a Python ``datetime`` or ``timedelta`` object or ``None``.
If ``value`` is an integer, the session will expire after that many
seconds of inactivity. If set to ``0`` then the session will expire on

View File

@ -2,6 +2,7 @@ from django.conf import settings
from django.contrib.sessions.backends.base import SessionBase
from django.core.cache import cache
class SessionStore(SessionBase):
"""
A cache-based session store.

View File

@ -1,12 +1,14 @@
import datetime
from django.conf import settings
from django.contrib.sessions.models import Session
from django.contrib.sessions.backends.base import SessionBase
from django.core.exceptions import SuspiciousOperation
import datetime
class SessionStore(SessionBase):
"""
Implements database session store
Implements database session store.
"""
def __init__(self, session_key=None):
super(SessionStore, self).__init__(session_key)

View File

@ -1,9 +1,11 @@
import os
import tempfile
from django.conf import settings
from django.contrib.sessions.backends.base import SessionBase
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
class SessionStore(SessionBase):
"""
Implements a file based session store.
@ -15,10 +17,10 @@ class SessionStore(SessionBase):
# 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)
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)
@ -31,9 +33,11 @@ class SessionStore(SessionBase):
session_key = self.session_key
# Make sure we're not vulnerable to directory traversal. Session keys
# should always be md5s, so they should never contain directory components.
# should always be md5s, so they should never contain directory
# components.
if os.path.sep in session_key:
raise SuspiciousOperation("Invalid characters (directory components) in session key")
raise SuspiciousOperation(
"Invalid characters (directory components) in session key")
return os.path.join(self.storage_path, self.file_prefix + session_key)

View File

@ -7,6 +7,7 @@ from django.utils.http import cookie_date
TEST_COOKIE_NAME = 'testcookie'
TEST_COOKIE_VALUE = 'worked'
class SessionMiddleware(object):
def process_request(self, request):
@ -40,5 +41,4 @@ class SessionMiddleware(object):
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None)
return response

View File

@ -6,9 +6,12 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
class SessionManager(models.Manager):
def encode(self, session_dict):
"Returns the given session dictionary pickled and encoded as a string."
"""
Returns the given session dictionary pickled and encoded as a string.
"""
pickled = pickle.dumps(session_dict)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5)
@ -21,6 +24,7 @@ class SessionManager(models.Manager):
s.delete() # Clear sessions with no data.
return s
class Session(models.Model):
"""
Django provides full support for anonymous sessions. The session
@ -38,7 +42,8 @@ class Session(models.Model):
the sessions documentation that is shipped with Django (also available
on the Django website).
"""
session_key = models.CharField(_('session key'), max_length=40, primary_key=True)
session_key = models.CharField(_('session key'), max_length=40,
primary_key=True)
session_data = models.TextField(_('session data'))
expire_date = models.DateTimeField(_('expire date'))
objects = SessionManager()

View File

@ -13,5 +13,6 @@ def create_default_site(app, created_models, verbosity):
print "Creating example.com Site object"
s = Site(domain="example.com", name="example.com")
s.save()
Site.objects.clear_cache()
dispatcher.connect(create_default_site, sender=site_app, signal=signals.post_syncdb)

View File

@ -42,6 +42,14 @@ class Site(models.Model):
def __unicode__(self):
return self.domain
def delete(self):
pk = self.pk
super(Site, self).delete()
try:
del(SITE_CACHE[pk])
except KeyError:
pass
# Register the admin options for these models.
# TODO: Maybe this should live in a separate module admin.py, but how would we
# ensure that module was loaded?

View File

@ -0,0 +1,13 @@
"""
>>> # Make sure that get_current() does not return a deleted Site object.
>>> from django.contrib.sites.models import Site
>>> s = Site.objects.get_current()
>>> s
<Site: example.com>
>>> s.delete()
>>> Site.objects.get_current()
Traceback (most recent call last):
...
DoesNotExist: Site matching query does not exist.
"""

View File

@ -10,7 +10,7 @@ from django.core import validators
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
from django.db.models.query import delete_objects, Q
from django.db.models.query import delete_objects, Q, CollectedObjects
from django.db.models.options import Options
from django.db import connection, transaction
from django.db.models import signals
@ -365,17 +365,16 @@ class Model(object):
error_dict[f.name] = errors
return error_dict
def _collect_sub_objects(self, seen_objs):
def _collect_sub_objects(self, seen_objs, parent=None, nullable=False):
"""
Recursively populates seen_objs with all objects related to this object.
When done, seen_objs will be in the format:
{model_class: {pk_val: obj, pk_val: obj, ...},
model_class: {pk_val: obj, pk_val: obj, ...}, ...}
When done, seen_objs.items() will be in the format:
[(model_class, {pk_val: obj, pk_val: obj, ...}),
(model_class, {pk_val: obj, pk_val: obj, ...}),...]
"""
pk_val = self._get_pk_val()
if pk_val in seen_objs.setdefault(self.__class__, {}):
if seen_objs.add(self.__class__, pk_val, self, parent, nullable):
return
seen_objs.setdefault(self.__class__, {})[pk_val] = self
for related in self._meta.get_all_related_objects():
rel_opts_name = related.get_accessor_name()
@ -385,16 +384,16 @@ class Model(object):
except ObjectDoesNotExist:
pass
else:
sub_obj._collect_sub_objects(seen_objs)
sub_obj._collect_sub_objects(seen_objs, self.__class__, related.field.null)
else:
for sub_obj in getattr(self, rel_opts_name).all():
sub_obj._collect_sub_objects(seen_objs)
sub_obj._collect_sub_objects(seen_objs, self.__class__, related.field.null)
def delete(self):
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
# Find all the objects than need to be deleted
seen_objs = SortedDict()
seen_objs = CollectedObjects()
self._collect_sub_objects(seen_objs)
# Actually delete the objects

View File

@ -2,6 +2,8 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.datastructures import SortedDict
import sys
import os
import threading
@ -18,10 +20,10 @@ class AppCache(object):
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531.
__shared_state = dict(
# Keys of app_store are the model modules for each application.
app_store = {},
app_store = SortedDict(),
# Mapping of app_labels to a dictionary of model names to model code.
app_models = {},
app_models = SortedDict(),
# Mapping of app_labels to errors raised when trying to import the app.
app_errors = {},
@ -133,7 +135,7 @@ class AppCache(object):
"""
self._populate()
if app_mod:
return self.app_models.get(app_mod.__name__.split('.')[-2], {}).values()
return self.app_models.get(app_mod.__name__.split('.')[-2], SortedDict()).values()
else:
model_list = []
for app_entry in self.app_models.itervalues():
@ -149,7 +151,7 @@ class AppCache(object):
"""
if seed_cache:
self._populate()
return self.app_models.get(app_label, {}).get(model_name.lower())
return self.app_models.get(app_label, SortedDict()).get(model_name.lower())
def register_models(self, app_label, *models):
"""
@ -159,7 +161,7 @@ class AppCache(object):
# Store as 'name: model' pair in a dictionary
# in the app_models dictionary
model_name = model._meta.object_name.lower()
model_dict = self.app_models.setdefault(app_label, {})
model_dict = self.app_models.setdefault(app_label, SortedDict())
if model_name in model_dict:
# The same model may be imported via different paths (e.g.
# appname.models and project.appname.models). We use the source

View File

@ -16,6 +16,92 @@ ITER_CHUNK_SIZE = CHUNK_SIZE
# Pull into this namespace for backwards compatibility
EmptyResultSet = sql.EmptyResultSet
class CyclicDependency(Exception):
pass
class CollectedObjects(object):
"""
A container that stores keys and lists of values along with
remembering the parent objects for all the keys.
This is used for the database object deletion routines so that we
can calculate the 'leaf' objects which should be deleted first.
"""
def __init__(self):
self.data = {}
self.children = {}
def add(self, model, pk, obj, parent_model, nullable=False):
"""
Adds an item.
model is the class of the object being added,
pk is the primary key, obj is the object itself,
parent_model is the model of the parent object
that this object was reached through, nullable should
be True if this relation is nullable.
If the item already existed in the structure,
returns true, otherwise false.
"""
d = self.data.setdefault(model, SortedDict())
retval = pk in d
d[pk] = obj
# Nullable relationships can be ignored -- they
# are nulled out before deleting, and therefore
# do not affect the order in which objects have
# to be deleted.
if parent_model is not None and not nullable:
self.children.setdefault(parent_model, []).append(model)
return retval
def __contains__(self, key):
return self.data.__contains__(key)
def __getitem__(self, key):
return self.data[key]
def __nonzero__(self):
return bool(self.data)
def iteritems(self):
for k in self.ordered_keys():
yield k, self[k]
def items(self):
return list(self.iteritems())
def keys(self):
return self.ordered_keys()
def ordered_keys(self):
"""
Returns the models in the order that they should be
dealth with i.e. models with no dependencies first.
"""
dealt_with = SortedDict()
# Start with items that have no children
models = self.data.keys()
while len(dealt_with) < len(models):
found = False
for model in models:
children = self.children.setdefault(model, [])
if len([c for c in children if c not in dealt_with]) == 0:
dealt_with[model] = None
found = True
if not found:
raise CyclicDependency("There is a cyclic dependency of items to be processed.")
return dealt_with.keys()
def unordered_keys(self):
"""
Fallback for the case where is a cyclic dependency but we
don't care.
"""
return self.data.keys()
class QuerySet(object):
"Represents a lazy database lookup for a set of objects"
def __init__(self, model=None, query=None):
@ -275,7 +361,7 @@ class QuerySet(object):
while 1:
# Collect all the objects to be deleted in this chunk, and all the
# objects that are related to the objects that are to be deleted.
seen_objs = SortedDict()
seen_objs = CollectedObjects()
for object in del_query[:CHUNK_SIZE]:
object._collect_sub_objects(seen_objs)
@ -682,19 +768,27 @@ def delete_objects(seen_objs):
Iterate through a list of seen classes, and remove any instances that are
referred to.
"""
ordered_classes = seen_objs.keys()
ordered_classes.reverse()
try:
ordered_classes = seen_objs.keys()
except CyclicDependency:
# if there is a cyclic dependency, we cannot in general delete
# the objects. However, if an appropriate transaction is set
# up, or if the database is lax enough, it will succeed.
# So for now, we go ahead and try anway.
ordered_classes = seen_objs.unordered_keys()
obj_pairs = {}
for cls in ordered_classes:
seen_objs[cls] = seen_objs[cls].items()
seen_objs[cls].sort()
items = seen_objs[cls].items()
items.sort()
obj_pairs[cls] = items
# Pre notify all instances to be deleted
for pk_val, instance in seen_objs[cls]:
for pk_val, instance in items:
dispatcher.send(signal=signals.pre_delete, sender=cls,
instance=instance)
pk_list = [pk for pk,instance in seen_objs[cls]]
pk_list = [pk for pk,instance in items]
del_query = sql.DeleteQuery(cls, connection)
del_query.delete_batch_related(pk_list)
@ -705,15 +799,17 @@ def delete_objects(seen_objs):
# Now delete the actual data
for cls in ordered_classes:
seen_objs[cls].reverse()
pk_list = [pk for pk,instance in seen_objs[cls]]
items = obj_pairs[cls]
items.reverse()
pk_list = [pk for pk,instance in items]
del_query = sql.DeleteQuery(cls, connection)
del_query.delete_batch(pk_list)
# Last cleanup; set NULLs where there once was a reference to the
# object, NULL the primary key of the found objects, and perform
# post-notification.
for pk_val, instance in seen_objs[cls]:
for pk_val, instance in items:
for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_objs:
setattr(instance, field.attname, None)

View File

@ -483,11 +483,17 @@ class ModelChoiceIterator(object):
def __iter__(self):
if self.field.empty_label is not None:
yield (u"", self.field.empty_label)
for obj in self.queryset:
yield (obj.pk, self.field.label_from_instance(obj))
# Clear the QuerySet cache if required.
if not self.field.cache_choices:
self.queryset._result_cache = None
if self.field.cache_choices:
if self.field.choice_cache is None:
self.field.choice_cache = [
(obj.pk, self.field.label_from_instance(obj))
for obj in self.queryset.all()
]
for choice in self.field.choice_cache:
yield choice
else:
for obj in self.queryset.all():
yield (obj.pk, self.field.label_from_instance(obj))
class ModelChoiceField(ChoiceField):
"""A ChoiceField whose choices are a model QuerySet."""
@ -509,6 +515,7 @@ class ModelChoiceField(ChoiceField):
Field.__init__(self, required, widget, label, initial, help_text,
*args, **kwargs)
self.queryset = queryset
self.choice_cache = None
def _get_queryset(self):
return self._queryset

View File

@ -702,13 +702,8 @@ Django tarball. It's our policy to make sure all tests pass at all times.
The tests cover:
* Models and the database API (``tests/modeltests/``).
* The cache system (``tests/regressiontests/cache.py``).
* The ``django.utils.dateformat`` module (``tests/regressiontests/dateformat/``).
* Database typecasts (``tests/regressiontests/db_typecasts/``).
* The template system (``tests/regressiontests/templates/`` and
``tests/regressiontests/defaultfilters/``).
* ``QueryDict`` objects (``tests/regressiontests/httpwrappers/``).
* Markup template tags (``tests/regressiontests/markup/``).
* Everything else in core Django code (``tests/regressiontests``)
* Contrib apps (``django/contrib/<contribapp>/tests``, see below)
We appreciate any and all contributions to the test suite!
@ -744,6 +739,26 @@ If you're using another backend:
deleted when the tests are finished. This means your user account needs
permission to execute ``CREATE DATABASE``.
If you want to run the full suite of tests, there are a number of dependencies that
you should install:
* PyYAML_
* Markdown_
* Textile_
* Docutils_
* setuptools_
Of these dependencies, setuptools_ is the only dependency that is required - if
setuptools_ is not installed, you will get import errors when running one of
the template tests. The tests using the other libraries will be skipped if the
dependency can't be found.
.. _PyYAML: http://pyyaml.org/wiki/PyYAML
.. _Markdown: http://pypi.python.org/pypi/Markdown/1.7
.. _Textile: http://pypi.python.org/pypi/textile
.. _docutils: http://pypi.python.org/pypi/docutils/0.4
.. _setuptools: http://pypi.python.org/pypi/setuptools/
To run a subset of the unit tests, append the names of the test modules to the
``runtests.py`` command line. See the list of directories in
``tests/modeltests`` and ``tests/regressiontests`` for module names.
@ -755,6 +770,22 @@ for generic relations and internationalization, type::
PYTHONPATH=..
./runtests.py --settings=settings generic_relations i18n
Contrib apps
------------
Tests for apps in ``django/contrib/`` go in their respective directories,
in a ``tests.py`` file. (You can split the tests over multiple modules
by using a ``tests`` folder in the normal Python way).
For the tests to be found, a ``models.py`` file must exist (it doesn't
have to have anything in it). If you have URLs that need to be
mapped, you must add them in ``tests/urls.py``.
To run tests for just one contrib app (e.g. ``markup``), use the same
method as above::
./runtests.py --settings=settings markup
Requesting features
===================

View File

@ -1382,7 +1382,7 @@ and then converted into a query using the ``query`` attribute::
This queryset will be evaluated as subselect statement::
SELET ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
startswith
~~~~~~~~~~

View File

@ -648,8 +648,8 @@ Django will automatically add this field::
Thus, you don't need to set ``primary_key=True`` on any of your fields
unless you want to override the default primary-key behavior.
``primary_key=True`` implies ``blank=False``, ``null=False`` and
``unique=True``. Only one primary key is allowed on an object.
``primary_key=True`` implies ``null=False`` and ``unique=True``. Only
one primary key is allowed on an object.
``unique``
~~~~~~~~~~

View File

@ -182,6 +182,13 @@ supplied, ``save()`` will update that instance. If it's not supplied,
# Create a form to edit an existing Article.
>>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(instance=a)
>>> f.save()
# Create a form to edit an existing Article, but use
# POST data to populate the form.
>>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(request.POST, instance=a)
>>> f.save()
Note that ``save()`` will raise a ``ValueError`` if the data in the form
doesn't validate -- i.e., ``if form.errors``.

View File

@ -44,7 +44,7 @@ _django_completion()
# Standalone options
opts="--help --settings --pythonpath --noinput --noreload --format --indent --verbosity --adminmedia --version"
# Actions
actions="adminindex createcachetable dbshell diffsettings \
actions="adminindex createcachetable createsuperuser dbshell diffsettings \
dumpdata flush inspectdb loaddata reset runfcgi runserver \
shell sql sqlall sqlclear sqlcustom sqlflush sqlindexes \
sqlreset sqlsequencereset startapp startproject \

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,179 @@
# coding: utf-8
"""
Tests for some corner cases with deleting.
"""
from django.db import models
class DefaultRepr(object):
def __repr__(self):
return u"<%s: %s>" % (self.__class__.__name__, self.__dict__)
class A(DefaultRepr, models.Model):
pass
class B(DefaultRepr, models.Model):
a = models.ForeignKey(A)
class C(DefaultRepr, models.Model):
b = models.ForeignKey(B)
class D(DefaultRepr, models.Model):
c = models.ForeignKey(C)
a = models.ForeignKey(A)
# Simplified, we have:
# A
# B -> A
# C -> B
# D -> C
# D -> A
# So, we must delete Ds first of all, then Cs then Bs then As.
# However, if we start at As, we might find Bs first (in which
# case things will be nice), or find Ds first.
# Some mutually dependent models, but nullable
class E(DefaultRepr, models.Model):
f = models.ForeignKey('F', null=True, related_name='e_rel')
class F(DefaultRepr, models.Model):
e = models.ForeignKey(E, related_name='f_rel')
__test__ = {'API_TESTS': """
# First, some tests for the datastructure we use
>>> from django.db.models.query import CollectedObjects
>>> g = CollectedObjects()
>>> g.add("key1", 1, "item1", None)
False
>>> g["key1"]
{1: 'item1'}
>>> g.add("key2", 1, "item1", "key1")
False
>>> g.add("key2", 2, "item2", "key1")
False
>>> g["key2"]
{1: 'item1', 2: 'item2'}
>>> g.add("key3", 1, "item1", "key1")
False
>>> g.add("key3", 1, "item1", "key2")
True
>>> g.ordered_keys()
['key3', 'key2', 'key1']
>>> g.add("key2", 1, "item1", "key3")
True
>>> g.ordered_keys()
Traceback (most recent call last):
...
CyclicDependency: There is a cyclic dependency of items to be processed.
# Due to the way that transactions work in the test harness,
# doing m.delete() here can work but fail in a real situation,
# since it may delete all objects, but not in the right order.
# So we manually check that the order of deletion is correct.
# Also, it is possible that the order is correct 'accidentally', due
# solely to order of imports etc. To check this, we set the order
# that 'get_models()' will retrieve to a known 'nice' order, and
# then try again with a known 'tricky' order. Slightly naughty
# access to internals here :-)
>>> from django.db.models.loading import cache
# Nice order
>>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd']
>>> del A._meta._related_objects_cache
>>> del B._meta._related_objects_cache
>>> del C._meta._related_objects_cache
>>> del D._meta._related_objects_cache
>>> a1 = A()
>>> a1.save()
>>> b1 = B(a=a1)
>>> b1.save()
>>> c1 = C(b=b1)
>>> c1.save()
>>> d1 = D(c=c1, a=a1)
>>> d1.save()
>>> o = CollectedObjects()
>>> a1._collect_sub_objects(o)
>>> o.keys()
[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]
>>> a1.delete()
# Same again with a known bad order
>>> cache.app_models['delete'].keyOrder = ['d', 'c', 'b', 'a']
>>> del A._meta._related_objects_cache
>>> del B._meta._related_objects_cache
>>> del C._meta._related_objects_cache
>>> del D._meta._related_objects_cache
>>> a2 = A()
>>> a2.save()
>>> b2 = B(a=a2)
>>> b2.save()
>>> c2 = C(b=b2)
>>> c2.save()
>>> d2 = D(c=c2, a=a2)
>>> d2.save()
>>> o = CollectedObjects()
>>> a2._collect_sub_objects(o)
>>> o.keys()
[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]
>>> a2.delete()
# Tests for nullable related fields
>>> g = CollectedObjects()
>>> g.add("key1", 1, "item1", None)
False
>>> g.add("key2", 1, "item1", "key1", nullable=True)
False
>>> g.add("key1", 1, "item1", "key2")
True
>>> g.ordered_keys()
['key1', 'key2']
>>> e1 = E()
>>> e1.save()
>>> f1 = F(e=e1)
>>> f1.save()
>>> e1.f = f1
>>> e1.save()
# Since E.f is nullable, we should delete F first (after nulling out
# the E.f field), then E.
>>> o = CollectedObjects()
>>> e1._collect_sub_objects(o)
>>> o.keys()
[<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>]
>>> e1.delete()
>>> e2 = E()
>>> e2.save()
>>> f2 = F(e=e2)
>>> f2.save()
>>> e2.f = f2
>>> e2.save()
# Same deal as before, though we are starting from the other object.
>>> o = CollectedObjects()
>>> f2._collect_sub_objects(o)
>>> o.keys()
[<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>]
>>> f2.delete()
"""
}