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

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,13 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django\n" "Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-12-09 11:51+0100\n" "POT-Creation-Date: 2008-06-23 20:52+0200\n"
"PO-Revision-Date: 2005-12-04 13:21+0100\n" "PO-Revision-Date: 2008-06-23 21:02+0100\n"
"Last-Translator: Dirk Eschler <dirk.eschler@gmx.net>\n" "Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language-Team: \n"
#: contrib/admin/media/js/SelectFilter2.js:33 #: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format #, perl-format
@ -38,82 +39,78 @@ msgstr "Ausgewählte %s"
#: contrib/admin/media/js/SelectFilter2.js:54 #: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click " 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 #: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all" msgid "Clear all"
msgstr "Alles abwählen" msgstr "Alles abwählen"
#: contrib/admin/media/js/dateparse.js:26
#: contrib/admin/media/js/calendar.js:24 #: contrib/admin/media/js/calendar.js:24
msgid "" #: contrib/admin/media/js/dateparse.js:32
"January February March April May June July August September October November " msgid "January February March April May June July August September October November December"
"December" msgstr "Januar Februar März April Mai Juni Juli August September Oktober November Dezember"
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/calendar.js:25 #: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S" msgid "S M T W T F S"
msgstr "S M D M D F S" msgstr "S M D M D F S"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45 #: contrib/admin/media/js/dateparse.js:33
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80 msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgid "Now" msgstr "Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag"
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/admin/CollapsedFieldsets.js:34 #: contrib/admin/media/js/admin/CollapsedFieldsets.js:34
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:72 #: contrib/admin/media/js/admin/CollapsedFieldsets.js:72
msgid "Show" msgid "Show"
msgstr "Anzeigen" msgstr "Einblenden"
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63 #: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
msgid "Hide" 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 sys
import time import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
try: try:
import cPickle as pickle import cPickle as pickle
except ImportError: except ImportError:
import pickle import pickle
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
class SessionBase(object): class SessionBase(object):
""" """
Base class for all Session classes. Base class for all Session classes.
@ -169,8 +170,8 @@ class SessionBase(object):
def set_expiry(self, value): def set_expiry(self, value):
""" """
Sets a custom expiration for the session. ``value`` can be an integer, a Sets a custom expiration for the session. ``value`` can be an integer,
Python ``datetime`` or ``timedelta`` object or ``None``. a Python ``datetime`` or ``timedelta`` object or ``None``.
If ``value`` is an integer, the session will expire after that many 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 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.contrib.sessions.backends.base import SessionBase
from django.core.cache import cache from django.core.cache import cache
class SessionStore(SessionBase): class SessionStore(SessionBase):
""" """
A cache-based session store. A cache-based session store.

View File

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

View File

@ -1,9 +1,11 @@
import os import os
import tempfile import tempfile
from django.conf import settings from django.conf import settings
from django.contrib.sessions.backends.base import SessionBase from django.contrib.sessions.backends.base import SessionBase
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
class SessionStore(SessionBase): class SessionStore(SessionBase):
""" """
Implements a file based session store. Implements a file based session store.
@ -15,10 +17,10 @@ class SessionStore(SessionBase):
# Make sure the storage path is valid. # Make sure the storage path is valid.
if not os.path.isdir(self.storage_path): if not os.path.isdir(self.storage_path):
raise ImproperlyConfigured("The session storage path %r doesn't exist. "\ raise ImproperlyConfigured(
"Please set your SESSION_FILE_PATH setting "\ "The session storage path %r doesn't exist. Please set your"
"to an existing directory in which Django "\ " SESSION_FILE_PATH setting to an existing directory in which"
"can store session data." % self.storage_path) " Django can store session data." % self.storage_path)
self.file_prefix = settings.SESSION_COOKIE_NAME self.file_prefix = settings.SESSION_COOKIE_NAME
super(SessionStore, self).__init__(session_key) super(SessionStore, self).__init__(session_key)
@ -31,9 +33,11 @@ class SessionStore(SessionBase):
session_key = self.session_key session_key = self.session_key
# Make sure we're not vulnerable to directory traversal. Session keys # 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: 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) 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_NAME = 'testcookie'
TEST_COOKIE_VALUE = 'worked' TEST_COOKIE_VALUE = 'worked'
class SessionMiddleware(object): class SessionMiddleware(object):
def process_request(self, request): def process_request(self, request):
@ -40,5 +41,4 @@ class SessionMiddleware(object):
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH, path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None) secure=settings.SESSION_COOKIE_SECURE or None)
return response return response

View File

@ -6,9 +6,12 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
class SessionManager(models.Manager): class SessionManager(models.Manager):
def encode(self, session_dict): 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 = pickle.dumps(session_dict)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5) return base64.encodestring(pickled + pickled_md5)
@ -21,6 +24,7 @@ class SessionManager(models.Manager):
s.delete() # Clear sessions with no data. s.delete() # Clear sessions with no data.
return s return s
class Session(models.Model): class Session(models.Model):
""" """
Django provides full support for anonymous sessions. The session 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 the sessions documentation that is shipped with Django (also available
on the Django website). 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')) session_data = models.TextField(_('session data'))
expire_date = models.DateTimeField(_('expire date')) expire_date = models.DateTimeField(_('expire date'))
objects = SessionManager() objects = SessionManager()

View File

@ -13,5 +13,6 @@ def create_default_site(app, created_models, verbosity):
print "Creating example.com Site object" print "Creating example.com Site object"
s = Site(domain="example.com", name="example.com") s = Site(domain="example.com", name="example.com")
s.save() s.save()
Site.objects.clear_cache()
dispatcher.connect(create_default_site, sender=site_app, signal=signals.post_syncdb) 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): def __unicode__(self):
return self.domain 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. # Register the admin options for these models.
# TODO: Maybe this should live in a separate module admin.py, but how would we # TODO: Maybe this should live in a separate module admin.py, but how would we
# ensure that module was loaded? # 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.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField 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.models.options import Options
from django.db import connection, transaction from django.db import connection, transaction
from django.db.models import signals from django.db.models import signals
@ -365,17 +365,16 @@ class Model(object):
error_dict[f.name] = errors error_dict[f.name] = errors
return error_dict 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. Recursively populates seen_objs with all objects related to this object.
When done, seen_objs will be in the format: 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, ...}),
model_class: {pk_val: obj, pk_val: obj, ...}, ...} (model_class, {pk_val: obj, pk_val: obj, ...}),...]
""" """
pk_val = self._get_pk_val() 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 return
seen_objs.setdefault(self.__class__, {})[pk_val] = self
for related in self._meta.get_all_related_objects(): for related in self._meta.get_all_related_objects():
rel_opts_name = related.get_accessor_name() rel_opts_name = related.get_accessor_name()
@ -385,16 +384,16 @@ class Model(object):
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
else: else:
sub_obj._collect_sub_objects(seen_objs) sub_obj._collect_sub_objects(seen_objs, self.__class__, related.field.null)
else: else:
for sub_obj in getattr(self, rel_opts_name).all(): 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): 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) 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 # Find all the objects than need to be deleted
seen_objs = SortedDict() seen_objs = CollectedObjects()
self._collect_sub_objects(seen_objs) self._collect_sub_objects(seen_objs)
# Actually delete the objects # Actually delete the objects

View File

@ -2,6 +2,8 @@
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.datastructures import SortedDict
import sys import sys
import os import os
import threading import threading
@ -18,10 +20,10 @@ class AppCache(object):
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531. # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531.
__shared_state = dict( __shared_state = dict(
# Keys of app_store are the model modules for each application. # 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. # 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. # Mapping of app_labels to errors raised when trying to import the app.
app_errors = {}, app_errors = {},
@ -133,7 +135,7 @@ class AppCache(object):
""" """
self._populate() self._populate()
if app_mod: 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: else:
model_list = [] model_list = []
for app_entry in self.app_models.itervalues(): for app_entry in self.app_models.itervalues():
@ -149,7 +151,7 @@ class AppCache(object):
""" """
if seed_cache: if seed_cache:
self._populate() 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): def register_models(self, app_label, *models):
""" """
@ -159,7 +161,7 @@ class AppCache(object):
# Store as 'name: model' pair in a dictionary # Store as 'name: model' pair in a dictionary
# in the app_models dictionary # in the app_models dictionary
model_name = model._meta.object_name.lower() 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: if model_name in model_dict:
# The same model may be imported via different paths (e.g. # The same model may be imported via different paths (e.g.
# appname.models and project.appname.models). We use the source # 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 # Pull into this namespace for backwards compatibility
EmptyResultSet = sql.EmptyResultSet 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): class QuerySet(object):
"Represents a lazy database lookup for a set of objects" "Represents a lazy database lookup for a set of objects"
def __init__(self, model=None, query=None): def __init__(self, model=None, query=None):
@ -275,7 +361,7 @@ class QuerySet(object):
while 1: while 1:
# Collect all the objects to be deleted in this chunk, and all the # 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. # 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]: for object in del_query[:CHUNK_SIZE]:
object._collect_sub_objects(seen_objs) 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 Iterate through a list of seen classes, and remove any instances that are
referred to. referred to.
""" """
try:
ordered_classes = seen_objs.keys() ordered_classes = seen_objs.keys()
ordered_classes.reverse() 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: for cls in ordered_classes:
seen_objs[cls] = seen_objs[cls].items() items = seen_objs[cls].items()
seen_objs[cls].sort() items.sort()
obj_pairs[cls] = items
# Pre notify all instances to be deleted # 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, dispatcher.send(signal=signals.pre_delete, sender=cls,
instance=instance) 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 = sql.DeleteQuery(cls, connection)
del_query.delete_batch_related(pk_list) del_query.delete_batch_related(pk_list)
@ -705,15 +799,17 @@ def delete_objects(seen_objs):
# Now delete the actual data # Now delete the actual data
for cls in ordered_classes: for cls in ordered_classes:
seen_objs[cls].reverse() items = obj_pairs[cls]
pk_list = [pk for pk,instance in seen_objs[cls]] items.reverse()
pk_list = [pk for pk,instance in items]
del_query = sql.DeleteQuery(cls, connection) del_query = sql.DeleteQuery(cls, connection)
del_query.delete_batch(pk_list) del_query.delete_batch(pk_list)
# Last cleanup; set NULLs where there once was a reference to the # Last cleanup; set NULLs where there once was a reference to the
# object, NULL the primary key of the found objects, and perform # object, NULL the primary key of the found objects, and perform
# post-notification. # post-notification.
for pk_val, instance in seen_objs[cls]: for pk_val, instance in items:
for field in cls._meta.fields: for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_objs: if field.rel and field.null and field.rel.to in seen_objs:
setattr(instance, field.attname, None) setattr(instance, field.attname, None)

View File

@ -483,11 +483,17 @@ class ModelChoiceIterator(object):
def __iter__(self): def __iter__(self):
if self.field.empty_label is not None: if self.field.empty_label is not None:
yield (u"", self.field.empty_label) yield (u"", self.field.empty_label)
for obj in self.queryset: 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)) 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
class ModelChoiceField(ChoiceField): class ModelChoiceField(ChoiceField):
"""A ChoiceField whose choices are a model QuerySet.""" """A ChoiceField whose choices are a model QuerySet."""
@ -509,6 +515,7 @@ class ModelChoiceField(ChoiceField):
Field.__init__(self, required, widget, label, initial, help_text, Field.__init__(self, required, widget, label, initial, help_text,
*args, **kwargs) *args, **kwargs)
self.queryset = queryset self.queryset = queryset
self.choice_cache = None
def _get_queryset(self): def _get_queryset(self):
return self._queryset 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: The tests cover:
* Models and the database API (``tests/modeltests/``). * Models and the database API (``tests/modeltests/``).
* The cache system (``tests/regressiontests/cache.py``). * Everything else in core Django code (``tests/regressiontests``)
* The ``django.utils.dateformat`` module (``tests/regressiontests/dateformat/``). * Contrib apps (``django/contrib/<contribapp>/tests``, see below)
* 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/``).
We appreciate any and all contributions to the test suite! 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 deleted when the tests are finished. This means your user account needs
permission to execute ``CREATE DATABASE``. 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 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 ``runtests.py`` command line. See the list of directories in
``tests/modeltests`` and ``tests/regressiontests`` for module names. ``tests/modeltests`` and ``tests/regressiontests`` for module names.
@ -755,6 +770,22 @@ for generic relations and internationalization, type::
PYTHONPATH=.. PYTHONPATH=..
./runtests.py --settings=settings generic_relations i18n ./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 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:: 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 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 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. unless you want to override the default primary-key behavior.
``primary_key=True`` implies ``blank=False``, ``null=False`` and ``primary_key=True`` implies ``null=False`` and ``unique=True``. Only
``unique=True``. Only one primary key is allowed on an object. one primary key is allowed on an object.
``unique`` ``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. # Create a form to edit an existing Article.
>>> a = Article.objects.get(pk=1) >>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(instance=a) >>> 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 Note that ``save()`` will raise a ``ValueError`` if the data in the form
doesn't validate -- i.e., ``if form.errors``. doesn't validate -- i.e., ``if form.errors``.

View File

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