1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

gis: Merged revisions 4669-4785 via svnmerge from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@4786 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jeremy Dunck 2007-03-23 16:35:57 +00:00
parent 8b279b63be
commit fa3ed6e134
99 changed files with 9509 additions and 2377 deletions

View File

@ -42,7 +42,6 @@ people who have submitted patches, reported bugs, added translations, helped
answer newbie questions, and generally made Django that much better:
adurdin@gmail.com
akaihola
Andreas
andy@jadedplanet.net
ant9000@netwise.it
@ -86,10 +85,12 @@ answer newbie questions, and generally made Django that much better:
Marc Fargas <telenieko@telenieko.com>
favo@exoweb.net
Eric Floehr <eric@intellovations.com>
Jorge Gajon <gajon@gajon.org>
gandalf@owca.info
Baishampayan Ghose
martin.glueck@gmail.com
Simon Greenhill <dev@simon.net.nz>
Owen Griffiths
Espen Grindhaug <http://grindhaug.org/>
Brian Harring <ferringb@gmail.com>
Brant Harris
@ -107,10 +108,12 @@ answer newbie questions, and generally made Django that much better:
Michael Josephson <http://www.sdjournal.com/>
jpellerin@gmail.com
junzhang.jn@gmail.com
Antti Kaihola <http://akaihola.blogspot.com/>
Ben Dean Kawamura <ben.dean.kawamura@gmail.com>
Garth Kidd <http://www.deadlybloodyserious.com/>
kilian <kilian.cavalotti@lip6.fr>
Sune Kirkeby <http://ibofobi.dk/>
Bastian Kleineidam <calvin@debian.org>
Cameron Knight (ckknight)
Meir Kriheli <http://mksoft.co.il/>
Bruce Kroeze <http://coderseye.com/>
@ -130,6 +133,7 @@ answer newbie questions, and generally made Django that much better:
masonsimon+django@gmail.com
Manuzhai
Petar Marić <http://www.petarmaric.com/>
Nuno Mariz <nmariz@gmail.com>
mark@junklight.com
Yasushi Masuda <whosaysni@gmail.com>
mattycakes@gmail.com
@ -148,6 +152,7 @@ answer newbie questions, and generally made Django that much better:
Neal Norwitz <nnorwitz@google.com>
oggie rob <oz.robharvey@gmail.com>
Jay Parlar <parlar@gmail.com>
pavithran s <pavithran.s@gmail.com>
pgross@thoughtworks.com
phaedo <http://phaedo.cx/>
phil@produxion.net

View File

@ -1,9 +1,10 @@
#!/usr/bin/env python
import optparse
import os
import sys
def compile_messages():
def compile_messages(locale=None):
basedir = None
if os.path.isdir(os.path.join('conf', 'locale')):
@ -14,6 +15,9 @@ def compile_messages():
print "This script should be run from the Django SVN tree or your project or app tree."
sys.exit(1)
if locale is not None:
basedir = os.path.join(basedir, locale, 'LC_MESSAGES')
for dirpath, dirnames, filenames in os.walk(basedir):
for f in filenames:
if f.endswith('.po'):
@ -32,5 +36,14 @@ def compile_messages():
cmd = 'msgfmt -o "$djangocompilemo" "$djangocompilepo"'
os.system(cmd)
def main():
parser = optparse.OptionParser()
parser.add_option('-l', '--locale', dest='locale',
help="The locale to process. Default is to process all.")
options, args = parser.parse_args()
if len(args):
parser.error("This program takes no arguments")
compile_messages(options.locale)
if __name__ == "__main__":
compile_messages()
main()

View File

@ -81,7 +81,7 @@ def make_messages():
src = pythonize_re.sub('\n#', src)
open(os.path.join(dirpath, '%s.py' % file), "wb").write(src)
thefile = '%s.py' % file
cmd = 'xgettext %s -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy -o - "%s"' % (
cmd = 'xgettext %s -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy --from-code UTF-8 -o - "%s"' % (
os.path.exists(potfile) and '--omit-header' or '', domain, os.path.join(dirpath, thefile))
(stdin, stdout, stderr) = os.popen3(cmd, 'b')
msgs = stdout.read()
@ -103,7 +103,7 @@ def make_messages():
open(os.path.join(dirpath, '%s.py' % file), "wb").write(templatize(src))
thefile = '%s.py' % file
if verbose: sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
cmd = 'xgettext %s -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy -o - "%s"' % (
cmd = 'xgettext %s -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy --from-code UTF-8 -o - "%s"' % (
os.path.exists(potfile) and '--omit-header' or '', domain, os.path.join(dirpath, thefile))
(stdin, stdout, stderr) = os.popen3(cmd, 'b')
msgs = stdout.read()

View File

@ -61,6 +61,7 @@ LANGUAGES = (
('nl', gettext_noop('Dutch')),
('no', gettext_noop('Norwegian')),
('pl', gettext_noop('Polish')),
('pt', gettext_noop('Portugese')),
('pt-br', gettext_noop('Brazilian')),
('ro', gettext_noop('Romanian')),
('ru', gettext_noop('Russian')),
@ -69,6 +70,7 @@ LANGUAGES = (
('sr', gettext_noop('Serbian')),
('sv', gettext_noop('Swedish')),
('ta', gettext_noop('Tamil')),
('te', gettext_noop('Telugu')),
('tr', gettext_noop('Turkish')),
('uk', gettext_noop('Ukrainian')),
('zh-cn', gettext_noop('Simplified Chinese')),

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-02-26 20:44+0100\n"
"PO-Revision-Date: 2007-02-27 20:06+0100\n"
"PO-Revision-Date: 2007-03-14 19:29+0100\n"
"Last-Translator: Nicola Larosa <nico@tekNico.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -523,7 +523,7 @@ msgstr ""
#: contrib/auth/forms.py:17 contrib/auth/forms.py:138
msgid "The two password fields didn't match."
msgstr "I due campi parola chiave non corrispondono."
msgstr "I due campi password non corrispondono."
#: contrib/auth/forms.py:25
msgid "A user with that username already exists."
@ -555,7 +555,7 @@ msgstr "Questo indirizzo email non è associato ad alcun account utente. Sei sic
#: contrib/auth/forms.py:117
msgid "The two 'new password' fields didn't match."
msgstr "I due campi 'nuova parola chiave' non corrispondono."
msgstr "I due campi 'nuova password' non corrispondono."
#: contrib/auth/forms.py:124
msgid "Your old password was entered incorrectly. Please enter it again."
@ -609,13 +609,13 @@ msgstr "indirizzo e-mail"
#: contrib/auth/models.py:94
msgid "password"
msgstr "parola chiave"
msgstr "password"
#: contrib/auth/models.py:94
msgid ""
"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
"password form</a>."
msgstr "Usare '[algo]$[salt]$[hexdigest]' oppure la maschera di <a href=\"password/\">cambio parola chiave</a>."
msgstr "Usare '[algo]$[salt]$[hexdigest]' oppure la maschera di <a href=\"password/\">cambio password</a>."
#: contrib/auth/models.py:95
msgid "staff status"
@ -1040,12 +1040,12 @@ msgstr "Aggiungi utente"
#: contrib/admin/views/auth.py:57
msgid "Password changed successfully."
msgstr "La parola chiave è stata cambiata correttamente."
msgstr "La password è stata cambiata correttamente."
#: contrib/admin/views/auth.py:64
#, python-format
msgid "Change password: %s"
msgstr "Cambia la parola chiave: %s"
msgstr "Cambia la password: %s"
#: contrib/admin/templatetags/admin_list.py:247
msgid "All dates"
@ -1088,7 +1088,7 @@ msgstr "Documentazione"
#: contrib/admin/templates/registration/password_change_form.html:3
#: contrib/admin/templates/registration/password_change_done.html:3
msgid "Change password"
msgstr "Cambia la parola chiave"
msgstr "Cambia la password"
#: contrib/admin/templates/admin/delete_confirmation.html:3
#: contrib/admin/templates/admin/change_form.html:10
@ -1335,11 +1335,11 @@ msgstr "Nome utente:"
#: contrib/admin/templates/admin/login.html:20
#: contrib/comments/templates/comments/form.html:8
msgid "Password:"
msgstr "Parola chiave:"
msgstr "Password:"
#: contrib/admin/templates/admin/login.html:22
msgid "Have you <a href=\"/password_reset/\">forgotten your password</a>?"
msgstr "Hai <a href=\"/password_reset/\">dimenticato la parola chiave</a>?"
msgstr "Hai <a href=\"/password_reset/\">dimenticato la password</a>?"
#: contrib/admin/templates/admin/base.html:25
msgid "Welcome,"
@ -1349,7 +1349,7 @@ msgstr "Benvenuto,"
msgid ""
"First, enter a username and password. Then, you'll be able to edit more user "
"options."
msgstr "Inserire innanzitutto nome utente e parola chiave. Si potrà quindi modificare le altre impostazioni dell'utente."
msgstr "Inserire innanzitutto nome utente e password. Si potrà quindi modificare le altre impostazioni dell'utente."
#: contrib/admin/templates/admin/auth/user/add_form.html:12
msgid "Username"
@ -1358,22 +1358,22 @@ msgstr "Nome utente"
#: contrib/admin/templates/admin/auth/user/add_form.html:18
#: contrib/admin/templates/admin/auth/user/change_password.html:34
msgid "Password"
msgstr "Parola chiave"
msgstr "Password"
#: contrib/admin/templates/admin/auth/user/add_form.html:23
#: contrib/admin/templates/admin/auth/user/change_password.html:39
msgid "Password (again)"
msgstr "Parola chiave (di nuovo)"
msgstr "Password (di nuovo)"
#: contrib/admin/templates/admin/auth/user/add_form.html:24
#: contrib/admin/templates/admin/auth/user/change_password.html:40
msgid "Enter the same password as above, for verification."
msgstr "Inserire la stessa parola chiave inserita sopra, come verifica."
msgstr "Inserire la stessa password inserita sopra, come verifica."
#: contrib/admin/templates/admin/auth/user/change_password.html:28
#, python-format
msgid "Enter a new password for the user <strong>%(username)s</strong>."
msgstr "Inserire una nuova parola chiave per l'utente <strong>%(username)s</strong>."
msgstr "Inserire una nuova password per l'utente <strong>%(username)s</strong>."
#: contrib/admin/templates/admin_doc/bookmarklets.html:3
msgid "Bookmarklets"
@ -1460,13 +1460,13 @@ msgstr "Modifica:"
#: contrib/admin/templates/registration/password_reset_form.html:10
#: contrib/admin/templates/registration/password_reset_done.html:4
msgid "Password reset"
msgstr "Reimposta la parola chiave"
msgstr "Reimposta la password"
#: contrib/admin/templates/registration/password_reset_form.html:12
msgid ""
"Forgotten your password? Enter your e-mail address below, and we'll reset "
"your password and e-mail the new one to you."
msgstr "Dimenticata la parola chiave? Inserire il proprio indirizzo e-mail qui sotto: la parola chiave sarà reimpostata, e la nuova ti verrà inviata per e-mail."
msgstr "Dimenticata la password? Inserire il proprio indirizzo e-mail qui sotto: la password sarà reimpostata, e la nuova ti verrà inviata per e-mail."
#: contrib/admin/templates/registration/password_reset_form.html:16
msgid "E-mail address:"
@ -1474,11 +1474,11 @@ msgstr "Indirizzo e-mail:"
#: contrib/admin/templates/registration/password_reset_form.html:16
msgid "Reset my password"
msgstr "Reimposta la mia parola chiave"
msgstr "Reimposta la mia password"
#: contrib/admin/templates/registration/password_reset_email.html:2
msgid "You're receiving this e-mail because you requested a password reset"
msgstr "Hai ricevuto questa e-mail perché hai chiesto di reimpostare la parola chiave"
msgstr "Hai ricevuto questa e-mail perché hai chiesto di reimpostare la password"
#: contrib/admin/templates/registration/password_reset_email.html:3
#, python-format
@ -1488,11 +1488,11 @@ msgstr "per il tuo account utente su %(site_name)s"
#: contrib/admin/templates/registration/password_reset_email.html:5
#, python-format
msgid "Your new password is: %(new_password)s"
msgstr "La tua nuova parola chiave è: %(new_password)s"
msgstr "La tua nuova password è: %(new_password)s"
#: contrib/admin/templates/registration/password_reset_email.html:7
msgid "Feel free to change this password by going to this page:"
msgstr "Puoi liberamente cambiare la tua parola chiave tramite questa pagina:"
msgstr "Puoi liberamente cambiare la tua password tramite questa pagina:"
#: contrib/admin/templates/registration/password_reset_email.html:11
msgid "Your username, in case you've forgotten:"
@ -1518,51 +1518,51 @@ msgstr "Accedi di nuovo"
#: contrib/admin/templates/registration/password_reset_done.html:6
#: contrib/admin/templates/registration/password_reset_done.html:10
msgid "Password reset successful"
msgstr "Parola chiave reimpostata correttamente"
msgstr "Password reimpostata correttamente"
#: contrib/admin/templates/registration/password_reset_done.html:12
msgid ""
"We've e-mailed a new password to the e-mail address you submitted. You "
"should be receiving it shortly."
msgstr "La nuova parola chiave è stata inviata all'indirizzo e-mail inserito. Arriverà a breve."
msgstr "La nuova password è stata inviata all'indirizzo e-mail inserito. Arriverà a breve."
#: contrib/admin/templates/registration/password_change_form.html:4
#: contrib/admin/templates/registration/password_change_form.html:6
#: contrib/admin/templates/registration/password_change_form.html:10
#: contrib/admin/templates/registration/password_change_done.html:4
msgid "Password change"
msgstr "Cambio di parola chiave"
msgstr "Cambio password"
#: contrib/admin/templates/registration/password_change_form.html:12
msgid ""
"Please enter your old password, for security's sake, and then enter your new "
"password twice so we can verify you typed it in correctly."
msgstr "Inserire l'attuale parola chiave, per ragioni di sicurezza, e poi la nuova parola chiave due volte, per verificare di averla scritta correttamente."
msgstr "Inserire l'attuale password, per ragioni di sicurezza, e poi la nuova password due volte, per verificare di averla scritta correttamente."
#: contrib/admin/templates/registration/password_change_form.html:17
msgid "Old password:"
msgstr "Parola chiave attuale:"
msgstr "Password attuale:"
#: contrib/admin/templates/registration/password_change_form.html:19
msgid "New password:"
msgstr "Nuova parola chiave:"
msgstr "Nuova password:"
#: contrib/admin/templates/registration/password_change_form.html:21
msgid "Confirm password:"
msgstr "Confermare la parola chiave:"
msgstr "Confermare la password:"
#: contrib/admin/templates/registration/password_change_form.html:23
msgid "Change my password"
msgstr "Modifica la mia parola chiave"
msgstr "Modifica la mia password"
#: contrib/admin/templates/registration/password_change_done.html:6
#: contrib/admin/templates/registration/password_change_done.html:10
msgid "Password change successful"
msgstr "Cambio di parola chiave avvenuto correttamente"
msgstr "Cambio di password avvenuto correttamente"
#: contrib/admin/templates/registration/password_change_done.html:12
msgid "Your password was changed."
msgstr "La parola chiave è stata cambiata."
msgstr "La password è stata cambiata."
#: contrib/sites/models.py:10
msgid "domain name"
@ -1905,7 +1905,7 @@ msgstr "Il modulo di commento non fornisce né 'anteprima' né 'invia'"
#: contrib/comments/templates/comments/form.html:8
msgid "Forgotten your password?"
msgstr "Hai dimenticato la parola chiave?"
msgstr "Hai dimenticato la password?"
#: contrib/comments/templates/comments/form.html:12
msgid "Ratings"

Binary file not shown.

View File

@ -0,0 +1,112 @@
# translation of djangojs.po to Polish
# Copyright (C) 2007 Michal Chruszcz
# This file is distributed under the same license as the django package.
#
# Michal Chruszcz <troll@pld-linux.org>, 2007.
msgid ""
msgstr ""
"Project-Id-Version: 0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-12-09 11:51+0100\n"
"PO-Revision-Date: 2007-03-12 11:42+0100\n"
"Last-Translator: Michal Chruszcz <troll@pld-linux.org>\n"
"Language-Team: Polish <pl@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: KBabel 1.11.4\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format
msgid "Available %s"
msgstr "Dostępne %s"
#: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all"
msgstr "Wybierz wszystko"
#: contrib/admin/media/js/SelectFilter2.js:46
msgid "Add"
msgstr "Dodaj"
#: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove"
msgstr "Usuń"
#: contrib/admin/media/js/SelectFilter2.js:53
#, perl-format
msgid "Chosen %s"
msgstr "Wybrano %s"
#: contrib/admin/media/js/SelectFilter2.js:54
#, fuzzy
msgid "Select your choice(s) and click "
msgstr "Zaznacz swój wybór i kliknij "
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "Wyczyść wszystko"
#: 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 "Styczeń Luty Marzec Kwiecień Maj Czerwiec Lipiec Sierpień Wrzesień Październik Listopad Grudzień"
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Niedziela Poniedziałek Wtorek Środa Czwartek Piątek Sobota"
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
msgstr "N Pn Wt Śr Cz Pt So"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
msgid "Now"
msgstr "Teraz"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
msgid "Clock"
msgstr "Zegar"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
msgid "Choose a time"
msgstr "Wybierz czas"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Midnight"
msgstr "Północ"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "6 a.m."
msgstr "6 rano"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "Noon"
msgstr "Południe"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
msgid "Cancel"
msgstr "Anuluj"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
msgid "Today"
msgstr "Dzisiaj"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
msgid "Calendar"
msgstr "Kalendarz"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
msgid "Yesterday"
msgstr "Wczoraj"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
msgid "Tomorrow"
msgstr "Jutro"

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,108 @@
# Portuguese translation of Django.
# Copyright (C) 2007 the Lawrence Journal-World
# This file is distributed under the same license as the PACKAGE package.
# Nuno Mariz <nmariz@gmail.com>, 2007.
#
msgid ""
msgstr ""
"Project-Id-Version: Django 0.96pre\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-03-15 11:51+0100\n"
"PO-Revision-Date: 2007-03-16 10:01+0000\n"
"Last-Translator: Nuno Mariz <nmariz@gmail.com>\n"
"Language-Team: pt_PT <nmariz@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format
msgid "Available %s"
msgstr "Disponível %s"
#: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all"
msgstr "Escolher todos"
#: contrib/admin/media/js/SelectFilter2.js:46
msgid "Add"
msgstr "Adicionar"
#: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove"
msgstr "Remover"
#: contrib/admin/media/js/SelectFilter2.js:53
#, perl-format
msgid "Chosen %s"
msgstr "Escolhido %s"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
msgstr "Seleccione a(s) sua(s) escolha(s) e clique "
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "Limpar tudo"
#: 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 "Janeiro Fevereiro Março Abril Maio Junho Julho Agosto Setembro Outubro Novembro Dezembro"
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Domingo Segunda Terça Quarta Quinta Sexta Sábado"
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
msgstr "D S T Q Q S S"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
msgid "Now"
msgstr "Agora"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
msgid "Clock"
msgstr "Relógio"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
msgid "Choose a time"
msgstr "Escolha a hora"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Midnight"
msgstr "Meia-noite"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "6 a.m."
msgstr "6 a.m."
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "Noon"
msgstr "Meio-dia"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
msgid "Cancel"
msgstr "Cancelar"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
msgid "Today"
msgstr "Hoje"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
msgid "Calendar"
msgstr "Calendário"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
msgid "Yesterday"
msgstr "Ontem"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
msgid "Tomorrow"
msgstr "Amanhã"

View File

@ -11,9 +11,9 @@ msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-03-06 00:17+0100\n"
"PO-Revision-Date: 2007-03-06 01:46+0100\n"
"PO-Revision-Date: 2007-03-06 10:30+0100\n"
"Last-Translator: Mikko Hellsing <mikko@sorl.net>\n"
"Language-Team: Django translators <djangoi18n@googlegroups.com>\n"
"Language-Team: Django I18N <Django-I18N@googlegroups.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -27,7 +27,7 @@ msgstr ""
#: newforms/fields.py:78 newforms/fields.py:374 newforms/fields.py:450
#: newforms/fields.py:461
msgid "This field is required."
msgstr "Det här fältet är obligatoriskt."
msgstr "Detta fältet är obligatoriskt."
#: oldforms/__init__.py:387
#, python-format
@ -122,7 +122,7 @@ msgstr "Fyll i ett giltigt filnamn."
#: db/models/fields/related.py:53
#, python-format
msgid "Please enter a valid %s."
msgstr "Var god fyll i ett giltigt %s."
msgstr "Var god fyll i giltigt %s."
#: db/models/fields/related.py:642
msgid "Separate multiple IDs with commas."
@ -317,7 +317,7 @@ msgstr "Fyll enbart i siffror avskilda med kommatecken."
#: core/validators.py:99
msgid "Enter valid e-mail addresses separated by commas."
msgstr "Fyll i giltiga e-postadresser avskilda med kommatecken."
msgstr "Fyll i giltiga e-mailadresser avskilda med kommatecken."
#: core/validators.py:103
msgid "Please enter a valid IP address."
@ -358,7 +358,7 @@ msgstr "Fyll i en giltig tid i formatet HH:MM"
#: core/validators.py:161 newforms/fields.py:269
msgid "Enter a valid e-mail address."
msgstr "Fyll i en giltig e-postadress."
msgstr "Fyll i en giltig e-mailadress."
#: core/validators.py:177
msgid ""
@ -652,7 +652,7 @@ msgstr "Detta URL verkar vara en trasig länk."
#: contrib/contenttypes/models.py:26
msgid "python model class name"
msgstr "python model class namn"
msgstr "python modell klass namn"
#: contrib/contenttypes/models.py:29
msgid "content type"
@ -710,7 +710,7 @@ msgstr "efternamn"
#: contrib/auth/models.py:93
msgid "e-mail address"
msgstr "e-postadress"
msgstr "e-mailadress"
#: contrib/auth/models.py:94
msgid "password"
@ -728,7 +728,7 @@ msgstr "personalstatus"
#: contrib/auth/models.py:95
msgid "Designates whether the user can log into this admin site."
msgstr "Avgör om användaren kan logga in till den här administrationssidan."
msgstr "Avgör om användaren kan logga in på den här admin-siten."
#: contrib/auth/models.py:96
msgid "active"
@ -823,8 +823,8 @@ msgid ""
"Please enter a correct username and password. Note that both fields are case-"
"sensitive."
msgstr ""
"V.G. ange ett korrekt användarnamn och lösenord. Notera att båda fält är "
"skiftlägeskänsliga."
"V.G. ange ett korrekt användarnamn och lösenord. Observera att båda fälten gör "
"skillnad på versaler och gemener."
#: contrib/auth/forms.py:62
msgid "This account is inactive."
@ -835,7 +835,7 @@ msgid ""
"That e-mail address doesn't have an associated user account. Are you sure "
"you've registered?"
msgstr ""
"Den e-postadressen har inte något konto associerat med sig. Är du säker på "
"Den e-mailadressen har inte något konto associerat med sig. Är du säker på "
"att du har registrerat dig?"
#: contrib/auth/forms.py:117
@ -848,7 +848,7 @@ msgstr "Ditt gamla lösenord var felaktigt ifyllt. Var vänlig fyll i det igen"
#: contrib/redirects/models.py:7
msgid "redirect from"
msgstr "vidarebefordra från"
msgstr "omdirigera från"
#: contrib/redirects/models.py:8
msgid ""
@ -860,7 +860,7 @@ msgstr ""
#: contrib/redirects/models.py:9
msgid "redirect to"
msgstr "vidarebefordra till"
msgstr "omdirigera till"
#: contrib/redirects/models.py:10
msgid ""
@ -872,11 +872,11 @@ msgstr ""
#: contrib/redirects/models.py:13
msgid "redirect"
msgstr "vidarebefordra"
msgstr "omdirigera"
#: contrib/redirects/models.py:14
msgid "redirects"
msgstr "vidarebefordringar"
msgstr "omdirigeringar"
#: contrib/comments/models.py:67 contrib/comments/models.py:166
msgid "object ID"
@ -1113,7 +1113,7 @@ msgstr ""
#: contrib/comments/views/comments.py:188
#: contrib/comments/views/comments.py:280
msgid "Only POSTs are allowed"
msgstr "Endast POSTningar är tillåtna"
msgstr "Endast POST tillåtet"
#: contrib/comments/views/comments.py:192
#: contrib/comments/views/comments.py:284
@ -1131,7 +1131,7 @@ msgid ""
"The comment form had an invalid 'target' parameter -- the object ID was "
"invalid"
msgstr ""
"Kommentars-formuläret har en ogiltig 'target'-parameter -- objektets ID är "
"Kommentars-formuläret hade en ogiltig 'mål'-parameter -- objektets ID var "
"ogiltigt"
#: contrib/comments/views/comments.py:257
@ -1286,11 +1286,11 @@ msgstr "visat namn"
#: contrib/sites/models.py:15
msgid "site"
msgstr "sida"
msgstr "site"
#: contrib/sites/models.py:16
msgid "sites"
msgstr "sidor"
msgstr "siter"
#: contrib/admin/filterspecs.py:40
#, python-format
@ -1386,7 +1386,7 @@ msgstr "Användarnamn kan inte innehålla tecknet '@'."
#: contrib/admin/views/decorators.py:85
#, python-format
msgid "Your e-mail address is not your username. Try '%s' instead."
msgstr "Din e-postadress är inte ditt användarnamn. Försök med '%s' istället."
msgstr "Din e-mailadress är inte ditt användarnamn. Försök med '%s' istället."
#: contrib/admin/views/auth.py:19 contrib/admin/views/main.py:257
#, python-format
@ -1594,11 +1594,11 @@ msgstr "Decimaltal"
#: contrib/admin/views/doc.py:306
msgid "Boolean (Either True, False or None)"
msgstr "Boolesk (antingen Sann, Falsk eller Inget)"
msgstr "Boolesk (antingen True, False eller None)"
#: contrib/admin/views/doc.py:307
msgid "Relation to parent model"
msgstr "Relation till förälder modell"
msgstr "Relation till förälder-modell"
#: contrib/admin/views/doc.py:308
msgid "Phone number"
@ -1614,7 +1614,7 @@ msgstr "Tid"
#: contrib/admin/views/doc.py:316
msgid "U.S. state (two uppercase letters)"
msgstr "Stat i USA (två stora bokstäver)"
msgstr "Stat i USA (två versaler)"
#: contrib/admin/views/doc.py:317
msgid "XML text"
@ -1720,7 +1720,7 @@ msgid ""
"admin site."
msgstr ""
"Det här objektet har ingen ändringshistorik. Det lades antagligen inte till "
"i den här admin-sidan"
"i den här admin-siten"
#: contrib/admin/templates/admin/change_list.html:12
#, python-format
@ -1749,7 +1749,7 @@ msgid ""
"There's been an error. It's been reported to the site administrators via e-"
"mail and should be fixed shortly. Thanks for your patience."
msgstr ""
"Ett fel har uppstått. Administratören har meddelats via e-post och "
"Ett fel har uppstått. Administratören har meddelats via e-mail och "
"felet bör åtgärdas snart. Tack för ditt tålamod."
#: contrib/admin/templates/admin/invalid_setup.html:8
@ -1783,7 +1783,7 @@ msgstr "Visa alla"
#: contrib/admin/templates/admin/base_site.html:4
msgid "Django site admin"
msgstr "Django sidadministration"
msgstr "Django site-administration"
#: contrib/admin/templates/admin/base_site.html:7
msgid "Django administration"
@ -1827,11 +1827,11 @@ msgstr "Du har inte rättigheter att ändra något."
#: contrib/admin/templates/admin/index.html:52
msgid "Recent Actions"
msgstr "Senaste händelserna"
msgstr "Senaste Händelserna"
#: contrib/admin/templates/admin/index.html:53
msgid "My Actions"
msgstr "Mina händelser"
msgstr "Mina Händelser"
#: contrib/admin/templates/admin/index.html:57
msgid "None available"
@ -1839,7 +1839,7 @@ msgstr "Inga tillgängliga"
#: contrib/admin/templates/admin/change_form.html:22
msgid "View on site"
msgstr "Visa på hemsidan"
msgstr "Visa på siten"
#: contrib/admin/templates/admin/change_form.html:32
#: contrib/admin/templates/admin/auth/user/change_password.html:24
@ -1918,7 +1918,7 @@ msgstr "Lösenord"
#: contrib/admin/templates/admin/auth/user/change_password.html:39
#: contrib/admin/templates/admin/auth/user/add_form.html:23
msgid "Password (again)"
msgstr "lösenord (igen)"
msgstr "Lösenord (igen)"
#: contrib/admin/templates/admin/auth/user/change_password.html:40
#: contrib/admin/templates/admin/auth/user/add_form.html:24
@ -1930,8 +1930,8 @@ msgid ""
"First, enter a username and password. Then, you'll be able to edit more user "
"options."
msgstr ""
"Ange först ett användarnamn och ett lösenord sedan kommer du att kunna "
"ändra fler användaralternativ."
"Ange först ett användarnamn och ett lösenord. Sedan kommer du att kunna ändra "
"fler användaralternativ."
#: contrib/admin/templates/admin/auth/user/add_form.html:12
msgid "Username"
@ -1965,7 +1965,7 @@ msgid ""
"Forgotten your password? Enter your e-mail address below, and we'll reset "
"your password and e-mail the new one to you."
msgstr ""
"Har du glömt ditt lösenord? Fyll i din e-postadress nedan, så nollställer vi "
"Har du glömt ditt lösenord? Fyll i din e-mailadress nedan, så nollställer vi "
"ditt lösenord och mailar det nya till dig."
#: contrib/admin/templates/registration/password_reset_form.html:16
@ -1978,7 +1978,7 @@ msgstr "Nollställ mitt lösenord"
#: contrib/admin/templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today."
msgstr "Tack för att du spenderade kvalitetstid med websidan idag."
msgstr "Tack för att du spenderade kvalitetstid med web-siten idag."
#: contrib/admin/templates/registration/logged_out.html:10
msgid "Log in again"
@ -1994,7 +1994,7 @@ msgid ""
"We've e-mailed a new password to the e-mail address you submitted. You "
"should be receiving it shortly."
msgstr ""
"Vi har skickat ett nytt lösenord till e-postadressen du fyllde i. Det bör "
"Vi har skickat ett nytt lösenord till e-mailadressen du fyllde i. Det bör "
"anlända snarast."
#: contrib/admin/templates/registration/password_change_form.html:12
@ -2047,7 +2047,7 @@ msgstr "Ditt användarnamn, om du har glömt:"
#: contrib/admin/templates/registration/password_reset_email.html:13
msgid "Thanks for using our site!"
msgstr "Tack för att du använder vår sida!"
msgstr "Tack för att du använder vår site!"
#: contrib/admin/templates/registration/password_reset_email.html:15
#, python-format
@ -2076,7 +2076,7 @@ msgstr ""
"<p class=\"help\">För att installera smarta bokmärken, dra länken till din\n"
"verktygsrad med bokmärken, eller högerklicka på länken och lägg till den\n"
"till dina bokmärken. Nu kan du välja det smarta bokmärket från alla sidor\n"
"på hemsidan. Observera att några av dessa smarta bokmärken kräver att du besöker\n"
"på siten. Observera att några av dessa smarta bokmärken kräver att du besöker\n"
"sidan från en dator som är \"intern\" (tala med din systemadministratör\n"
"om du inte är säker på om din dator är \"intern\").</p>\n"

View File

@ -10,9 +10,9 @@ msgstr ""
"Project-Id-Version: djangojs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-03-06 02:29+0100\n"
"PO-Revision-Date: 2007-03-06 02:30+0100\n"
"PO-Revision-Date: 2007-03-06 10:30+0100\n"
"Last-Translator: Mikko Hellsing <mikko@sorl.net>\n"
"Language-Team: Django translators <djangoi18n@googlegroups.com>\n"
"Language-Team: Django I18N <Django-I18N@googlegroups.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -27,7 +27,7 @@ msgstr "Tillgänglig %s"
#: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all"
msgstr "Visa alla"
msgstr "Välj alla"
#: contrib/admin/media/js/SelectFilter2.js:46
msgid "Add"
@ -35,20 +35,20 @@ msgstr "Lägg till"
#: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove"
msgstr "Tag bort"
msgstr "Ta bort"
#: contrib/admin/media/js/SelectFilter2.js:53
#, perl-format
msgid "Chosen %s"
msgstr "Valde %s"
msgstr "Vald %s"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
msgstr "Välj ditt/dina val och klicka "
msgstr "Gör dina val och klicka på "
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "Avmarkera alla"
msgstr "Ta bort alla"
#: contrib/admin/media/js/dateparse.js:32
#: contrib/admin/media/js/calendar.js:24

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,112 @@
# translation of djangojs.po to tamil
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# PONNUSAMY.A <ponnusamy.simpleman@gmail.com>, 2007.
msgid ""
msgstr ""
"Project-Id-Version: djangojs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-12-09 11:51+0100\n"
"PO-Revision-Date: 2007-03-14 16:40+0530\n"
"Last-Translator: PONNUSAMY <ponnusamy.simpleman@gmail.com>\n"
"Language-Team: tamil <tamilinix@yahoogroups.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: KBabel 1.11.4\n"
#: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format
msgid "Available %s"
msgstr "%s இருக்கிறதா "
#: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all"
msgstr "எல்லாவற்றையும் தேர்ந்த்தெடுக்க"
#: contrib/admin/media/js/SelectFilter2.js:46
msgid "Add"
msgstr "சேர்க்க"
#: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove"
msgstr "அழிக்க"
#: contrib/admin/media/js/SelectFilter2.js:53
#, perl-format
msgid "Chosen %s"
msgstr "%s தேர்ந்த்தெடுக்கப்பட்ட"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
msgstr "தேவையானவற்றை தேர்ந்த்தெடுத்து கிளிக் செய்க"
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "எல்லாவற்றையும் அழிக்க "
#: 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 ""
"ஜனவரி பிப்ரவரி மார்ச் ஏப்ரல் மே ஜூன் ஜூலை ஆகஸ்டு செப்டம்பர் அக்டோபர் நவம்பர் "
"டிசம்பர்"
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "ஞாயிறு திங்கள் செவ்வாய் புதன் வியாழன் வெள்ளி சனி "
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
msgstr "ஞா தி செ பு வி வெ ச"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
msgid "Now"
msgstr "இப்பொழுது "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
msgid "Clock"
msgstr "கடிகாரம் "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
msgid "Choose a time"
msgstr "ஒரு நேரத்தை தேர்ந்த்தெடுக்க "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Midnight"
msgstr "நடு இரவு "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "6 a.m."
msgstr "காலை 6 மணி "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "Noon"
msgstr "மதியம் "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
msgid "Cancel"
msgstr "வேண்டாம் "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
msgid "Today"
msgstr "இன்று "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
msgid "Calendar"
msgstr "நாள்காட்டி "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
msgid "Yesterday"
msgstr "நேற்று "
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
msgid "Tomorrow"
msgstr "நாளை"

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,110 @@
# translation of djangojs.po to Telugu
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# pavithran <pavithran.s@gmail.com>, 2007.
msgid ""
msgstr ""
"Project-Id-Version: djangojs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-12-09 11:51+0100\n"
"PO-Revision-Date: 2007-03-06 16:08+0530\n"
"Last-Translator: pavithran <pavithran.s@gmail.com>\n"
"Language-Team: Telugu <indlinux-telugu@lists.sourceforge.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: KBabel 1.11.4\n"
#: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format
msgid "Available %s"
msgstr "ఆందుబాతులోఉన్న %s "
#: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all"
msgstr "అన్నీ ఎన్నుకోండి"
#: contrib/admin/media/js/SelectFilter2.js:46
msgid "Add"
msgstr "ఙత చేయి"
#: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove"
msgstr "తీసివేయండి"
#: contrib/admin/media/js/SelectFilter2.js:53
#, perl-format
msgid "Chosen %s"
msgstr "ఎన్నుకున్న %s"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
msgstr "మీ ఇష్టాలు ఎన్నుకోండి"
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "అన్ని తీసివేయు"
#: 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 "ఙాన్వరి ఫిబ్రవరి మార్చి ఎప్రిల్ మే ఙూను ఙులై ఆగష్టు సెప్టెంబర్ అక్టోబర్ నవంబర్ డిసెంబర్"
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "ఆదివారము సోమవారము మంగళవారము బుధవారము గురువారము శుక్రవారము శనివారము"
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
msgstr "ఆ సో మం భు గు శు శ"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
msgid "Now"
msgstr "ఇప్పుడు"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
msgid "Clock"
msgstr "గడియారము"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
msgid "Choose a time"
msgstr "ఒక సమయము ఎన్నుకోండి"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Midnight"
msgstr "ఆర్ధరాత్రి"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "6 a.m."
msgstr "6"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "Noon"
msgstr "మధ్యాహ్నము"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
msgid "Cancel"
msgstr "రద్దు చేయు"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
msgid "Today"
msgstr "ఈనాడు"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
msgid "Calendar"
msgstr "కాలెండర్"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
msgid "Yesterday"
msgstr "నిన్న"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
msgid "Tomorrow"
msgstr "రేపు"

File diff suppressed because it is too large Load Diff

View File

@ -16,8 +16,9 @@ DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# Local time zone for this installation. All choices can be found here:
# Local time zone for this installation. Choices can be found here:
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
# although not all variations may be possible on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/Chicago'

View File

@ -11,6 +11,7 @@ import re
register = template.Library()
word_re = re.compile('[A-Z][a-z]+')
absolute_url_re = re.compile(r'^(?:http(?:s)?:/)?/', re.IGNORECASE)
def class_name_to_underscored(name):
return '_'.join([s.lower() for s in word_re.findall(name)[:-1]])
@ -18,18 +19,19 @@ def class_name_to_underscored(name):
def include_admin_script(script_path):
"""
Returns an HTML script element for including a script from the admin
media url.
media url (or other location if an absolute url is given).
Example usage::
{% include_admin_script js/calendar.js %}
{% include_admin_script "js/calendar.js" %}
could return::
<script type="text/javascript" src="/media/admin/js/calendar.js">
"""
return '<script type="text/javascript" src="%s%s"></script>' % (settings.ADMIN_MEDIA_PREFIX, script_path)
if not absolute_url_re.match(script_path):
script_path = '%s%s' % (settings.ADMIN_MEDIA_PREFIX, script_path)
return '<script type="text/javascript" src="%s"></script>' % script_path
include_admin_script = register.simple_tag(include_admin_script)
def submit_row(context):

View File

@ -168,7 +168,7 @@ def model_detail(request, app_label, model_name):
model = m
break
if model is None:
raise Http404, _("Model %r not found in app %r") % (model_name, app_label)
raise Http404, _("Model %(name)r not found in app %(label)r") % {'name': model_name, 'label': app_label}
opts = model._meta
@ -180,7 +180,7 @@ def model_detail(request, app_label, model_name):
if isinstance(field, models.ForeignKey):
data_type = related_object_name = field.rel.to.__name__
app_label = field.rel.to._meta.app_label
verbose = utils.parse_rst((_("the related `%s.%s` object") % (app_label, data_type)), 'model', _('model:') + data_type)
verbose = utils.parse_rst((_("the related `%(label)s.%(type)s` object") % {'label': app_label, 'type': data_type}), 'model', _('model:') + data_type)
else:
data_type = get_readable_field_data_type(field)
verbose = field.verbose_name
@ -211,7 +211,7 @@ def model_detail(request, app_label, model_name):
# Gather related objects
for rel in opts.get_all_related_objects():
verbose = _("related `%s.%s` objects") % (rel.opts.app_label, rel.opts.object_name)
verbose = _("related `%(label)s.%(name)s` objects") % {'label': rel.opts.app_label, 'name': rel.opts.object_name}
accessor = rel.get_accessor_name()
fields.append({
'name' : "%s.all" % accessor,

View File

@ -7,6 +7,7 @@ from django.db.models import get_apps, get_models, signals
def create_contenttypes(app, created_models, verbosity=2):
from django.contrib.contenttypes.models import ContentType
ContentType.objects.clear_cache()
app_models = get_models(app)
if not app_models:
return

View File

@ -19,6 +19,16 @@ class ContentTypeManager(models.Manager):
model=key[1], defaults={'name': str(opts.verbose_name)})
CONTENT_TYPE_CACHE[key] = ct
return ct
def clear_cache(self):
"""
Clear out the content-type cache. This needs to happen during database
flushes to prevent caching of "stale" content type IDs (see
django.contrib.contenttypes.management.create_contenttypes for where
this gets called).
"""
global CONTENT_TYPE_CACHE
CONTENT_TYPE_CACHE = {}
class ContentType(models.Model):
name = models.CharField(maxlength=100)

View File

@ -9,7 +9,7 @@
{{ form }}
</table>
<input type="hidden" name="{{ stage_field }}" value="1" />
<p><input type="submit" value="Submit" /></p>
<p><input type="submit" value="Preview" /></p>
</form>
{% endblock %}

View File

@ -7,7 +7,7 @@
<table>
{% for field in form %}
<tr>
<th>{{ field.verbose_name }}:</th>
<th>{{ field.label }}:</th>
<td>{{ field.data|escape }}</td>
</tr>
{% endfor %}
@ -30,7 +30,7 @@
{{ form }}
</table>
<input type="hidden" name="{{ stage_field }}" value="1" />
<p><input type="submit" value="Submit changes" /></p>
<p><input type="submit" value="Preview" /></p>
</form>
{% endblock %}

View File

@ -1,3 +1,5 @@
from django.utils.translation import ngettext
from django.utils.translation import gettext_lazy as _
from django import template
import re
@ -12,9 +14,9 @@ def ordinal(value):
value = int(value)
except ValueError:
return value
t = ('th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th')
t = (_('th'), _('st'), _('nd'), _('rd'), _('th'), _('th'), _('th'), _('th'), _('th'), _('th'))
if value % 100 in (11, 12, 13): # special case
return '%dth' % value
return "%d%s" % (value, t[0])
return '%d%s' % (value, t[value % 10])
register.filter(ordinal)
@ -41,11 +43,14 @@ def intword(value):
if value < 1000000:
return value
if value < 1000000000:
return '%.1f million' % (value / 1000000.0)
new_value = value / 1000000.0
return ngettext('%(value).1f million', '%(value).1f million', new_value) % {'value': new_value}
if value < 1000000000000:
return '%.1f billion' % (value / 1000000000.0)
new_value = value / 1000000000.0
return ngettext('%(value).1f billion', '%(value).1f billion', new_value) % {'value': new_value}
if value < 1000000000000000:
return '%.1f trillion' % (value / 1000000000000.0)
new_value = value / 1000000000000.0
return ngettext('%(value).1f trillion', '%(value).1f trillion', new_value) % {'value': new_value}
return value
register.filter(intword)
@ -60,5 +65,5 @@ def apnumber(value):
return value
if not 0 < value < 10:
return value
return ('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine')[value-1]
return (_('one'), _('two'), _('three'), _('four'), _('five'), _('six'), _('seven'), _('eight'), _('nine'))[value-1]
register.filter(apnumber)

View File

@ -10,6 +10,7 @@ TEST_COOKIE_VALUE = 'worked'
class SessionWrapper(object):
def __init__(self, session_key):
self.session_key = session_key
self.accessed = False
self.modified = False
def __contains__(self, key):
@ -46,6 +47,7 @@ class SessionWrapper(object):
def _get_session(self):
# Lazily loads session from storage.
self.accessed = True
try:
return self._session_cache
except AttributeError:
@ -72,14 +74,21 @@ class SessionMiddleware(object):
def process_response(self, request, response):
# If request.session was modified, or if response.session was set, save
# those changes and set a session cookie.
patch_vary_headers(response, ('Cookie',))
try:
accessed = request.session.accessed
modified = request.session.modified
except AttributeError:
pass
else:
if accessed:
patch_vary_headers(response, ('Cookie',))
if modified or settings.SESSION_SAVE_EVERY_REQUEST:
session_key = request.session.session_key or Session.objects.get_new_session_key()
if request.session.session_key:
session_key = request.session.session_key
else:
obj = Session.objects.get_new_session_object()
session_key = obj.session_key
if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
max_age = None
expires = None

View File

@ -1,4 +1,4 @@
import base64, md5, random, sys
import base64, md5, random, sys, datetime
import cPickle as pickle
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -23,6 +23,23 @@ class SessionManager(models.Manager):
break
return session_key
def get_new_session_object(self):
"""
Returns a new session object.
"""
# FIXME: There is a *small* chance of collision here, meaning we will
# return an existing object. That can be fixed when we add a way to
# validate (and guarantee) that non-auto primary keys are unique. For
# now, we save immediately in order to reduce the "window of
# misfortune" as much as possible.
created = False
while not created:
obj, created = self.get_or_create(session_key=self.get_new_session_key(),
expire_date = datetime.datetime.now())
# Collision in key generation, so re-seed the generator
random.seed()
return obj
def save(self, session_key, session_dict, expire_date):
s = self.model(session_key, self.encode(session_dict), expire_date)
if session_dict:

View File

@ -280,7 +280,7 @@ def get_sql_delete(app):
from django.db import backend, connection, models, get_introspection_module
introspection = get_introspection_module()
# This should work even if a connecton isn't available
# This should work even if a connection isn't available
try:
cursor = connection.cursor()
except:
@ -516,6 +516,7 @@ def syncdb(verbosity=1, interactive=True):
created_models = set()
pending_references = {}
# Create the tables for each model
for app in models.get_apps():
app_name = app.__name__.split('.')[-2]
model_list = models.get_models(app)
@ -537,6 +538,11 @@ def syncdb(verbosity=1, interactive=True):
cursor.execute(statement)
table_list.append(model._meta.db_table)
# Create the m2m tables. This must be done after all tables have been created
# to ensure that all referred tables will exist.
for app in models.get_apps():
app_name = app.__name__.split('.')[-2]
model_list = models.get_models(app)
for model in model_list:
if model in created_models:
sql = _get_many_to_many_sql_for_model(model)
@ -546,7 +552,7 @@ def syncdb(verbosity=1, interactive=True):
for statement in sql:
cursor.execute(statement)
transaction.commit_unless_managed()
transaction.commit_unless_managed()
# Send the post_syncdb signal, so individual apps can do whatever they need
# to do at this point.
@ -1358,13 +1364,14 @@ def load_data(fixture_labels, verbosity=1):
for fixture_dir in app_fixtures + list(settings.FIXTURE_DIRS) + ['']:
if verbosity > 1:
print "Checking %s for fixtures..." % humanize(fixture_dir)
try:
fixture_name, format = fixture_label.rsplit('.', 1)
formats = [format]
except ValueError:
parts = fixture_label.split('.')
if len(parts) == 1:
fixture_name = fixture_label
formats = serializers.get_serializer_formats()
else:
fixture_name, format = '.'.join(parts[:-1]), parts[-1]
formats = [format]
label_found = False
for format in formats:
serializer = serializers.get_serializer(format)
@ -1439,7 +1446,7 @@ def dump_data(app_labels, format='json', indent=None):
for model in get_models(app):
objects.extend(model.objects.all())
try:
print serializers.serialize(format, objects, indent=indent)
return serializers.serialize(format, objects, indent=indent)
except Exception, e:
sys.stderr.write(style.ERROR("Unable to serialize database: %s\n" % e))
dump_data.help_doc = 'Output the contents of the database as a fixture of the given format'
@ -1585,7 +1592,7 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
parser.print_usage_and_exit()
elif action == 'dumpdata':
try:
action_mapping[action](args[1:], options.format, options.indent)
print action_mapping[action](args[1:], options.format, options.indent)
except IndexError:
parser.print_usage_and_exit()
elif action in ('startapp', 'startproject'):

View File

@ -25,6 +25,13 @@ BUILTIN_SERIALIZERS = {
"json" : "django.core.serializers.json",
}
# Check for PyYaml and register the serializer if it's available.
try:
import yaml
BUILTIN_SERIALIZERS["yaml"] = "django.core.serializers.pyyaml"
except ImportError:
pass
_serializers = {}
def register_serializer(format, serializer_module):

View File

@ -34,17 +34,17 @@ class Serializer(object):
for obj in queryset:
self.start_object(obj)
for field in obj._meta.fields:
if field is obj._meta.pk:
continue
elif field.rel is None:
if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_field(obj, field)
else:
if self.selected_fields is None or field.attname[:-3] in self.selected_fields:
self.handle_fk_field(obj, field)
if field.serialize:
if field.rel is None:
if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_field(obj, field)
else:
if self.selected_fields is None or field.attname[:-3] in self.selected_fields:
self.handle_fk_field(obj, field)
for field in obj._meta.many_to_many:
if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_m2m_field(obj, field)
if field.serialize:
if self.selected_fields is None or field.attname in self.selected_fields:
self.handle_m2m_field(obj, field)
self.end_object(obj)
self.end_serialization()
return self.getvalue()
@ -54,11 +54,7 @@ class Serializer(object):
Convert a field's value to a string.
"""
if isinstance(field, models.DateTimeField):
value = getattr(obj, field.name)
if value is None:
value = ''
else:
value = value.strftime("%Y-%m-%d %H:%M:%S")
value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S")
elif isinstance(field, models.FileField):
value = getattr(obj, "get_%s_url" % field.name, lambda: None)()
else:

View File

@ -57,7 +57,7 @@ def Deserializer(object_list, **options):
for d in object_list:
# Look up the model and starting build a dict of data for it.
Model = _get_model(d["model"])
data = {Model._meta.pk.attname : d["pk"]}
data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])}
m2m_data = {}
# Handle each field
@ -70,16 +70,17 @@ def Deserializer(object_list, **options):
# Handle M2M relations
if field.rel and isinstance(field.rel, models.ManyToManyRel):
pks = []
m2m_convert = field.rel.to._meta.pk.to_python
for pk in field_value:
if isinstance(pk, unicode):
pks.append(pk.encode(options.get("encoding", settings.DEFAULT_CHARSET)))
pks.append(m2m_convert(pk.encode(options.get("encoding", settings.DEFAULT_CHARSET))))
else:
pks.append(pk)
pks.append(m2m_convert(pk))
m2m_data[field.name] = pks
# Handle FK fields
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
data[field.attname] = field_value
data[field.attname] = field.rel.to._meta.pk.to_python(field_value)
# Handle all other fields
else:

View File

@ -0,0 +1,36 @@
"""
YAML serializer.
Requires PyYaml (http://pyyaml.org/), but that's checked for in __init__.
"""
import datetime
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
import yaml
class Serializer(PythonSerializer):
"""
Convert a queryset to YAML.
"""
def end_serialization(self):
yaml.dump(self.objects, self.stream, **self.options)
def getvalue(self):
return self.stream.getvalue()
def Deserializer(stream_or_string, **options):
"""
Deserialize a stream or string of YAML data.
"""
if isinstance(stream_or_string, basestring):
stream = StringIO(stream_or_string)
else:
stream = stream_or_string
for obj in PythonDeserializer(yaml.load(stream)):
yield obj

View File

@ -13,6 +13,10 @@ class Serializer(base.Serializer):
Serializes a QuerySet to XML.
"""
def indent(self, level):
if self.options.get('indent', None) is not None:
self.xml.ignorableWhitespace('\n' + ' ' * self.options.get('indent', None) * level)
def start_serialization(self):
"""
Start serialization -- open the XML document and the root element.
@ -25,6 +29,7 @@ class Serializer(base.Serializer):
"""
End serialization -- end the document.
"""
self.indent(0)
self.xml.endElement("django-objects")
self.xml.endDocument()
@ -35,6 +40,7 @@ class Serializer(base.Serializer):
if not hasattr(obj, "_meta"):
raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj))
self.indent(1)
self.xml.startElement("object", {
"pk" : str(obj._get_pk_val()),
"model" : str(obj._meta),
@ -44,6 +50,7 @@ class Serializer(base.Serializer):
"""
Called after handling all fields for an object.
"""
self.indent(1)
self.xml.endElement("object")
def handle_field(self, obj, field):
@ -51,16 +58,19 @@ class Serializer(base.Serializer):
Called to handle each field on an object (except for ForeignKeys and
ManyToManyFields)
"""
self.indent(2)
self.xml.startElement("field", {
"name" : field.name,
"type" : field.get_internal_type()
})
# Get a "string version" of the object's data (this is handled by the
# serializer base class). None is handled specially.
value = self.get_string_value(obj, field)
if value is not None:
# serializer base class).
if getattr(obj, field.name) is not None:
value = self.get_string_value(obj, field)
self.xml.characters(str(value))
else:
self.xml.addQuickElement("None")
self.xml.endElement("field")
@ -92,6 +102,7 @@ class Serializer(base.Serializer):
"""
Helper to output the <field> element for relational fields
"""
self.indent(2)
self.xml.startElement("field", {
"name" : field.name,
"rel" : field.rel.__class__.__name__,
@ -127,7 +138,8 @@ class Deserializer(base.Deserializer):
pk = node.getAttribute("pk")
if not pk:
raise base.DeserializationError("<object> node is missing the 'pk' attribute")
data = {Model._meta.pk.name : pk}
data = {Model._meta.pk.attname : Model._meta.pk.to_python(pk)}
# Also start building a dict of m2m data (this is saved as
# {m2m_accessor_attribute : [list_of_related_objects]})
@ -148,17 +160,20 @@ class Deserializer(base.Deserializer):
# As is usually the case, relation fields get the special treatment.
if field.rel and isinstance(field.rel, models.ManyToManyRel):
m2m_data[field.name] = self._handle_m2m_field_node(field_node)
m2m_data[field.name] = self._handle_m2m_field_node(field_node, field)
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
data[field.attname] = self._handle_fk_field_node(field_node)
data[field.attname] = self._handle_fk_field_node(field_node, field)
else:
value = field.to_python(getInnerText(field_node).strip().encode(self.encoding))
if len(field_node.childNodes) == 1 and field_node.childNodes[0].nodeName == 'None':
value = None
else:
value = field.to_python(getInnerText(field_node).strip().encode(self.encoding))
data[field.name] = value
# Return a DeserializedObject so that the m2m data has a place to live.
return base.DeserializedObject(Model(**data), m2m_data)
def _handle_fk_field_node(self, node):
def _handle_fk_field_node(self, node, field):
"""
Handle a <field> node for a ForeignKey
"""
@ -166,13 +181,16 @@ class Deserializer(base.Deserializer):
if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None':
return None
else:
return getInnerText(node).strip().encode(self.encoding)
return field.rel.to._meta.pk.to_python(
getInnerText(node).strip().encode(self.encoding))
def _handle_m2m_field_node(self, node):
def _handle_m2m_field_node(self, node, field):
"""
Handle a <field> node for a ManyToManyField
"""
return [c.getAttribute("pk").encode(self.encoding) for c in node.getElementsByTagName("object")]
return [field.rel.to._meta.pk.to_python(
c.getAttribute("pk").encode(self.encoding))
for c in node.getElementsByTagName("object")]
def _get_model_from_node(self, node, attr):
"""

View File

@ -140,7 +140,8 @@ def _isValidDate(date_string):
try:
date(year, month, day)
except ValueError, e:
raise ValidationError, gettext('Invalid date: %s.' % e)
msg = gettext('Invalid date: %s') % gettext(str(e))
raise ValidationError, msg
def isValidANSIDate(field_data, all_data):
if not ansi_date_re.search(field_data):
@ -363,7 +364,7 @@ class NumberIsInRange(object):
self.lower, self.upper = lower, upper
if not error_message:
if lower and upper:
self.error_message = gettext("This value must be between %s and %s.") % (lower, upper)
self.error_message = gettext("This value must be between %(lower)s and %(upper)s.") % {'lower': lower, 'upper': upper}
elif lower:
self.error_message = gettext("This value must be at least %s.") % lower
elif upper:

View File

@ -76,10 +76,11 @@ class DatabaseWrapper(local):
return cursor
def _commit(self):
return self.connection.commit()
if self.connection is not None:
return self.connection.commit()
def _rollback(self):
if self.connection:
if self.connection is not None:
return self.connection.rollback()
def close(self):

View File

@ -10,6 +10,15 @@ try:
except ImportError, e:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured, "Error loading MySQLdb module: %s" % e
# We want version (1, 2, 1, 'final', 2) or later. We can't just use
# lexicographic ordering in this check because then (1, 2, 1, 'gamma')
# inadvertently passes the version test.
version = Database.version_info
if (version < (1,2,1) or (version[:3] == (1, 2, 1) and
(len(version) < 5 or version[3] != 'final' or version[4] < 2))):
raise ImportError, "MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__
from MySQLdb.converters import conversions
from MySQLdb.constants import FIELD_TYPE
import types
@ -17,11 +26,14 @@ import re
DatabaseError = Database.DatabaseError
# MySQLdb-1.2.1 supports the Python boolean type, and only uses datetime
# module for time-related columns; older versions could have used mx.DateTime
# or strings if there were no datetime module. However, MySQLdb still returns
# TIME columns as timedelta -- they are more like timedelta in terms of actual
# behavior as they are signed and include days -- and Django expects time, so
# we still need to override that.
django_conversions = conversions.copy()
django_conversions.update({
types.BooleanType: util.rev_typecast_boolean,
FIELD_TYPE.DATETIME: util.typecast_timestamp,
FIELD_TYPE.DATE: util.typecast_date,
FIELD_TYPE.TIME: util.typecast_time,
})
@ -31,31 +43,12 @@ django_conversions.update({
# http://dev.mysql.com/doc/refman/5.0/en/news.html .
server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
# This is an extra debug layer over MySQL queries, to display warnings.
# It's only used when DEBUG=True.
class MysqlDebugWrapper:
def __init__(self, cursor):
self.cursor = cursor
def execute(self, sql, params=()):
try:
return self.cursor.execute(sql, params)
except Database.Warning, w:
self.cursor.execute("SHOW WARNINGS")
raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall())
def executemany(self, sql, param_list):
try:
return self.cursor.executemany(sql, param_list)
except Database.Warning, w:
self.cursor.execute("SHOW WARNINGS")
raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall())
def __getattr__(self, attr):
if self.__dict__.has_key(attr):
return self.__dict__[attr]
else:
return getattr(self.cursor, attr)
# MySQLdb-1.2.1 and newer automatically makes use of SHOW WARNINGS on
# MySQL-4.1 and newer, so the MysqlDebugWrapper is unnecessary. Since the
# point is to raise Warnings as exceptions, this can be done with the Python
# warning module, and this is setup when the connection is created, and the
# standard util.CursorDebugWrapper can be used. Also, using sql_mode
# TRADITIONAL will automatically cause most warnings to be treated as errors.
try:
# Only exists in Python 2.4+
@ -83,35 +76,41 @@ class DatabaseWrapper(local):
def cursor(self):
from django.conf import settings
from warnings import filterwarnings
if not self._valid_connection():
kwargs = {
'user': settings.DATABASE_USER,
'db': settings.DATABASE_NAME,
'passwd': settings.DATABASE_PASSWORD,
'conv': django_conversions,
'charset': 'utf8',
'use_unicode': False,
}
if settings.DATABASE_USER:
kwargs['user'] = settings.DATABASE_USER
if settings.DATABASE_NAME:
kwargs['db'] = settings.DATABASE_NAME
if settings.DATABASE_PASSWORD:
kwargs['passwd'] = settings.DATABASE_PASSWORD
if settings.DATABASE_HOST.startswith('/'):
kwargs['unix_socket'] = settings.DATABASE_HOST
else:
elif settings.DATABASE_HOST:
kwargs['host'] = settings.DATABASE_HOST
if settings.DATABASE_PORT:
kwargs['port'] = int(settings.DATABASE_PORT)
kwargs.update(self.options)
self.connection = Database.connect(**kwargs)
cursor = self.connection.cursor()
if self.connection.get_server_info() >= '4.1':
cursor.execute("SET NAMES 'utf8'")
else:
cursor = self.connection.cursor()
if settings.DEBUG:
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
filterwarnings("error", category=Database.Warning)
return util.CursorDebugWrapper(cursor, self)
return cursor
def _commit(self):
self.connection.commit()
if self.connection is not None:
self.connection.commit()
def _rollback(self):
if self.connection:
if self.connection is not None:
try:
self.connection.rollback()
except Database.NotSupportedError:

View File

@ -3,12 +3,25 @@ import os
def runshell():
args = ['']
args += ["--user=%s" % settings.DATABASE_USER]
if settings.DATABASE_PASSWORD:
args += ["--password=%s" % settings.DATABASE_PASSWORD]
if settings.DATABASE_HOST:
args += ["--host=%s" % settings.DATABASE_HOST]
if settings.DATABASE_PORT:
args += ["--port=%s" % settings.DATABASE_PORT]
args += [settings.DATABASE_NAME]
db = settings.DATABASE_OPTIONS.get('db', settings.DATABASE_NAME)
user = settings.DATABASE_OPTIONS.get('user', settings.DATABASE_USER)
passwd = settings.DATABASE_OPTIONS.get('passwd', settings.DATABASE_PASSWORD)
host = settings.DATABASE_OPTIONS.get('host', settings.DATABASE_HOST)
port = settings.DATABASE_OPTIONS.get('port', settings.DATABASE_PORT)
defaults_file = settings.DATABASE_OPTIONS.get('read_default_file')
# Seems to be no good way to set sql_mode with CLI
if defaults_file:
args += ["--defaults-file=%s" % defaults_file]
if user:
args += ["--user=%s" % user]
if passwd:
args += ["--password=%s" % passwd]
if host:
args += ["--host=%s" % host]
if port:
args += ["--port=%s" % port]
if db:
args += [db]
os.execvp('mysql', args)

View File

View File

@ -0,0 +1,233 @@
"""
MySQL database backend for Django.
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
"""
from django.db.backends import util
try:
import MySQLdb as Database
except ImportError, e:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured, "Error loading MySQLdb module: %s" % e
from MySQLdb.converters import conversions
from MySQLdb.constants import FIELD_TYPE
import types
import re
DatabaseError = Database.DatabaseError
django_conversions = conversions.copy()
django_conversions.update({
types.BooleanType: util.rev_typecast_boolean,
FIELD_TYPE.DATETIME: util.typecast_timestamp,
FIELD_TYPE.DATE: util.typecast_date,
FIELD_TYPE.TIME: util.typecast_time,
})
# This should match the numerical portion of the version numbers (we can treat
# versions like 5.0.24 and 5.0.24a as the same). Based on the list of version
# at http://dev.mysql.com/doc/refman/4.1/en/news.html and
# http://dev.mysql.com/doc/refman/5.0/en/news.html .
server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
# This is an extra debug layer over MySQL queries, to display warnings.
# It's only used when DEBUG=True.
class MysqlDebugWrapper:
def __init__(self, cursor):
self.cursor = cursor
def execute(self, sql, params=()):
try:
return self.cursor.execute(sql, params)
except Database.Warning, w:
self.cursor.execute("SHOW WARNINGS")
raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall())
def executemany(self, sql, param_list):
try:
return self.cursor.executemany(sql, param_list)
except Database.Warning, w:
self.cursor.execute("SHOW WARNINGS")
raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall())
def __getattr__(self, attr):
if self.__dict__.has_key(attr):
return self.__dict__[attr]
else:
return getattr(self.cursor, attr)
try:
# Only exists in Python 2.4+
from threading import local
except ImportError:
# Import copy of _thread_local.py from Python 2.4
from django.utils._threading_local import local
class DatabaseWrapper(local):
def __init__(self, **kwargs):
self.connection = None
self.queries = []
self.server_version = None
self.options = kwargs
def _valid_connection(self):
if self.connection is not None:
try:
self.connection.ping()
return True
except DatabaseError:
self.connection.close()
self.connection = None
return False
def cursor(self):
from django.conf import settings
if not self._valid_connection():
kwargs = {
'user': settings.DATABASE_USER,
'db': settings.DATABASE_NAME,
'passwd': settings.DATABASE_PASSWORD,
'conv': django_conversions,
}
if settings.DATABASE_HOST.startswith('/'):
kwargs['unix_socket'] = settings.DATABASE_HOST
else:
kwargs['host'] = settings.DATABASE_HOST
if settings.DATABASE_PORT:
kwargs['port'] = int(settings.DATABASE_PORT)
kwargs.update(self.options)
self.connection = Database.connect(**kwargs)
cursor = self.connection.cursor()
if self.connection.get_server_info() >= '4.1':
cursor.execute("SET NAMES 'utf8'")
else:
cursor = self.connection.cursor()
if settings.DEBUG:
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
return cursor
def _commit(self):
if self.connection is not None:
self.connection.commit()
def _rollback(self):
if self.connection is not None:
try:
self.connection.rollback()
except Database.NotSupportedError:
pass
def close(self):
if self.connection is not None:
self.connection.close()
self.connection = None
def get_server_version(self):
if not self.server_version:
if not self._valid_connection():
self.cursor()
m = server_version_re.match(self.connection.get_server_info())
if not m:
raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
self.server_version = tuple([int(x) for x in m.groups()])
return self.server_version
supports_constraints = True
def quote_name(name):
if name.startswith("`") and name.endswith("`"):
return name # Quoting once is enough.
return "`%s`" % name
dictfetchone = util.dictfetchone
dictfetchmany = util.dictfetchmany
dictfetchall = util.dictfetchall
def get_last_insert_id(cursor, table_name, pk_name):
return cursor.lastrowid
def get_date_extract_sql(lookup_type, table_name):
# lookup_type is 'year', 'month', 'day'
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name)
def get_date_trunc_sql(lookup_type, field_name):
# lookup_type is 'year', 'month', 'day'
fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
try:
i = fields.index(lookup_type) + 1
except ValueError:
sql = field_name
else:
format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
return sql
def get_limit_offset_sql(limit, offset=None):
sql = "LIMIT "
if offset and offset != 0:
sql += "%s," % offset
return sql + str(limit)
def get_random_function_sql():
return "RAND()"
def get_deferrable_sql():
return ""
def get_fulltext_search_sql(field_name):
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
def get_drop_foreignkey_sql():
return "DROP FOREIGN KEY"
def get_pk_default_value():
return "DEFAULT"
def get_sql_flush(style, tables, sequences):
"""Return a list of SQL statements required to remove all data from
all tables in the database (without actually removing the tables
themselves) and put the database in an empty 'initial' state
"""
# NB: The generated SQL below is specific to MySQL
# 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
# to clear all tables of all data
if tables:
sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + \
['%s %s;' % \
(style.SQL_KEYWORD('TRUNCATE'),
style.SQL_FIELD(quote_name(table))
) for table in tables] + \
['SET FOREIGN_KEY_CHECKS = 1;']
# 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
# to reset sequence indices
sql.extend(["%s %s %s %s %s;" % \
(style.SQL_KEYWORD('ALTER'),
style.SQL_KEYWORD('TABLE'),
style.SQL_TABLE(quote_name(sequence['table'])),
style.SQL_KEYWORD('AUTO_INCREMENT'),
style.SQL_FIELD('= 1'),
) for sequence in sequences])
return sql
else:
return []
OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': 'LIKE %s',
'contains': 'LIKE BINARY %s',
'icontains': 'LIKE %s',
'gt': '> %s',
'gte': '>= %s',
'lt': '< %s',
'lte': '<= %s',
'startswith': 'LIKE BINARY %s',
'endswith': 'LIKE BINARY %s',
'istartswith': 'LIKE %s',
'iendswith': 'LIKE %s',
}

View File

@ -0,0 +1,14 @@
from django.conf import settings
import os
def runshell():
args = ['']
args += ["--user=%s" % settings.DATABASE_USER]
if settings.DATABASE_PASSWORD:
args += ["--password=%s" % settings.DATABASE_PASSWORD]
if settings.DATABASE_HOST:
args += ["--host=%s" % settings.DATABASE_HOST]
if settings.DATABASE_PORT:
args += ["--port=%s" % settings.DATABASE_PORT]
args += [settings.DATABASE_NAME]
os.execvp('mysql', args)

View File

@ -0,0 +1,29 @@
# This dictionary maps Field objects to their associated MySQL column
# types, as strings. Column-type strings can contain format strings; they'll
# be interpolated against the values of Field.__dict__ before being output.
# If a column type is set to None, it won't be included in the output.
DATA_TYPES = {
'AutoField': 'integer AUTO_INCREMENT',
'BooleanField': 'bool',
'CharField': 'varchar(%(maxlength)s)',
'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'FileField': 'varchar(100)',
'FilePathField': 'varchar(100)',
'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'ImageField': 'varchar(100)',
'IntegerField': 'integer',
'IPAddressField': 'char(15)',
'ManyToManyField': None,
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(maxlength)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'USStateField': 'varchar(2)',
}

View File

@ -0,0 +1,95 @@
from django.db.backends.mysql_old.base import quote_name
from MySQLdb import ProgrammingError, OperationalError
from MySQLdb.constants import FIELD_TYPE
import re
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
def get_table_list(cursor):
"Returns a list of table names in the current database."
cursor.execute("SHOW TABLES")
return [row[0] for row in cursor.fetchall()]
def get_table_description(cursor, table_name):
"Returns a description of the table, with the DB-API cursor.description interface."
cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name))
return cursor.description
def _name_to_index(cursor, table_name):
"""
Returns a dictionary of {field_name: field_index} for the given table.
Indexes are 0-based.
"""
return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name))])
def get_relations(cursor, table_name):
"""
Returns a dictionary of {field_index: (field_index_other_table, other_table)}
representing all relationships to the given table. Indexes are 0-based.
"""
my_field_dict = _name_to_index(cursor, table_name)
constraints = []
relations = {}
try:
# This should work for MySQL 5.0.
cursor.execute("""
SELECT column_name, referenced_table_name, referenced_column_name
FROM information_schema.key_column_usage
WHERE table_name = %s
AND table_schema = DATABASE()
AND referenced_table_name IS NOT NULL
AND referenced_column_name IS NOT NULL""", [table_name])
constraints.extend(cursor.fetchall())
except (ProgrammingError, OperationalError):
# Fall back to "SHOW CREATE TABLE", for previous MySQL versions.
# Go through all constraints and save the equal matches.
cursor.execute("SHOW CREATE TABLE %s" % quote_name(table_name))
for row in cursor.fetchall():
pos = 0
while True:
match = foreign_key_re.search(row[1], pos)
if match == None:
break
pos = match.end()
constraints.append(match.groups())
for my_fieldname, other_table, other_field in constraints:
other_field_index = _name_to_index(cursor, other_table)[other_field]
my_field_index = my_field_dict[my_fieldname]
relations[my_field_index] = (other_field_index, other_table)
return relations
def get_indexes(cursor, table_name):
"""
Returns a dictionary of fieldname -> infodict for the given table,
where each infodict is in the format:
{'primary_key': boolean representing whether it's the primary key,
'unique': boolean representing whether it's a unique index}
"""
cursor.execute("SHOW INDEX FROM %s" % quote_name(table_name))
indexes = {}
for row in cursor.fetchall():
indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
return indexes
DATA_TYPES_REVERSE = {
FIELD_TYPE.BLOB: 'TextField',
FIELD_TYPE.CHAR: 'CharField',
FIELD_TYPE.DECIMAL: 'FloatField',
FIELD_TYPE.DATE: 'DateField',
FIELD_TYPE.DATETIME: 'DateTimeField',
FIELD_TYPE.DOUBLE: 'FloatField',
FIELD_TYPE.FLOAT: 'FloatField',
FIELD_TYPE.INT24: 'IntegerField',
FIELD_TYPE.LONG: 'IntegerField',
FIELD_TYPE.LONGLONG: 'IntegerField',
FIELD_TYPE.SHORT: 'IntegerField',
FIELD_TYPE.STRING: 'TextField',
FIELD_TYPE.TIMESTAMP: 'DateTimeField',
FIELD_TYPE.TINY: 'IntegerField',
FIELD_TYPE.TINY_BLOB: 'TextField',
FIELD_TYPE.MEDIUM_BLOB: 'TextField',
FIELD_TYPE.LONG_BLOB: 'TextField',
FIELD_TYPE.VAR_STRING: 'CharField',
}

View File

@ -43,10 +43,11 @@ class DatabaseWrapper(local):
return FormatStylePlaceholderCursor(self.connection)
def _commit(self):
self.connection.commit()
if self.connection is not None:
self.connection.commit()
def _rollback(self):
if self.connection:
if self.connection is not None:
try:
self.connection.rollback()
except Database.NotSupportedError:

View File

@ -92,10 +92,11 @@ class DatabaseWrapper(local):
return cursor
def _commit(self):
return self.connection.commit()
if self.connection is not None:
return self.connection.commit()
def _rollback(self):
if self.connection:
if self.connection is not None:
return self.connection.rollback()
def close(self):

View File

@ -60,10 +60,11 @@ class DatabaseWrapper(local):
return cursor
def _commit(self):
return self.connection.commit()
if self.connection is not None:
return self.connection.commit()
def _rollback(self):
if self.connection:
if self.connection is not None:
return self.connection.rollback()
def close(self):

View File

@ -67,10 +67,11 @@ class DatabaseWrapper(local):
return cursor
def _commit(self):
self.connection.commit()
if self.connection is not None:
self.connection.commit()
def _rollback(self):
if self.connection:
if self.connection is not None:
self.connection.rollback()
def close(self):

View File

@ -67,7 +67,7 @@ class Field(object):
def __init__(self, verbose_name=None, name=None, primary_key=False,
maxlength=None, unique=False, blank=False, null=False, db_index=False,
core=False, rel=None, default=NOT_PROVIDED, editable=True,
core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True,
prepopulate_from=None, unique_for_date=None, unique_for_month=None,
unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
help_text='', db_column=None):
@ -78,6 +78,7 @@ class Field(object):
self.blank, self.null = blank, null
self.core, self.rel, self.default = core, rel, default
self.editable = editable
self.serialize = serialize
self.validator_list = validator_list or []
self.prepopulate_from = prepopulate_from
self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month
@ -742,6 +743,13 @@ class NullBooleanField(Field):
kwargs['null'] = True
Field.__init__(self, *args, **kwargs)
def to_python(self, value):
if value in (None, True, False): return value
if value in ('None'): return None
if value in ('t', 'True', '1'): return True
if value in ('f', 'False', '0'): return False
raise validators.ValidationError, gettext("This value must be either None, True or False.")
def get_manipulator_field_objs(self):
return [oldforms.NullBooleanField]
@ -819,7 +827,7 @@ class TimeField(Field):
if value is not None:
# MySQL will throw a warning if microseconds are given, because it
# doesn't support microseconds.
if settings.DATABASE_ENGINE == 'mysql':
if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
value = value.replace(microsecond=0)
value = str(value)
return Field.get_db_prep_save(self, value)

View File

@ -94,6 +94,7 @@ class GenericRelation(RelatedField, Field):
kwargs['blank'] = True
kwargs['editable'] = False
kwargs['serialize'] = False
Field.__init__(self, **kwargs)
def get_manipulator_field_objs(self):

View File

@ -84,6 +84,7 @@ class Options(object):
self.fields.insert(bisect(self.fields, field), field)
if not self.pk and field.primary_key:
self.pk = field
field.serialize = False
def __repr__(self):
return '<Options for %s>' % self.object_name

View File

@ -109,6 +109,8 @@ class QuerySet(object):
def __getitem__(self, k):
"Retrieve an item or slice from the set of results."
if not isinstance(k, (slice, int)):
raise TypeError
assert (not isinstance(k, slice) and (k >= 0)) \
or (isinstance(k, slice) and (k.start is None or k.start >= 0) and (k.stop is None or k.stop >= 0)), \
"Negative indexing is not supported."
@ -780,7 +782,7 @@ def fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen,
def parse_lookup(kwarg_items, opts):
# Helper function that handles converting API kwargs
# (e.g. "name__exact": "tom") to SQL.
# Returns a tuple of (tables, joins, where, params).
# Returns a tuple of (joins, where, params).
# 'joins' is a sorted dictionary describing the tables that must be joined
# to complete the query. The dictionary is sorted because creation order

View File

@ -1,7 +1,7 @@
class BoundRelatedObject(object):
def __init__(self, related_object, field_mapping, original):
self.relation = related_object
self.field_mappings = field_mapping[related_object.opts.module_name]
self.field_mappings = field_mapping[related_object.name]
def template_name(self):
raise NotImplementedError
@ -16,7 +16,7 @@ class RelatedObject(object):
self.opts = model._meta
self.field = field
self.edit_inline = field.rel.edit_inline
self.name = self.opts.module_name
self.name = '%s:%s' % (self.opts.app_label, self.opts.module_name)
self.var_name = self.opts.object_name.lower()
def flatten_data(self, follow, obj=None):

View File

@ -130,7 +130,9 @@ class FormWrapper(object):
if self.edit_inline:
self.fill_inline_collections()
for inline_collection in self._inline_collections:
if inline_collection.name == key:
# The 'orig_name' comparison is for backwards compatibility
# with hand-crafted forms.
if inline_collection.name == key or (':' not in key and inline_collection.orig_name == key):
return inline_collection
raise KeyError, "Could not find Formfield or InlineObjectCollection named %r" % key
@ -226,6 +228,9 @@ class InlineObjectCollection(object):
self.errors = errors
self._collections = None
self.name = rel_obj.name
# This is the name used prior to fixing #1839. Needs for backwards
# compatibility.
self.orig_name = rel_obj.opts.module_name
def __len__(self):
self.fill()

View File

@ -19,7 +19,7 @@ def get_object_or_404(klass, *args, **kwargs):
try:
return manager.get(*args, **kwargs)
except klass.DoesNotExist:
raise Http404
raise Http404('No %s matches the given query.' % klass._meta.object_name)
def get_list_or_404(klass, *args, **kwargs):
if isinstance(klass, Manager):
@ -28,5 +28,5 @@ def get_list_or_404(klass, *args, **kwargs):
manager = klass._default_manager
obj_list = list(manager.filter(*args, **kwargs))
if not obj_list:
raise Http404
raise Http404('No %s matches the given query.' % manager.model._meta.object_name)
return obj_list

View File

@ -91,6 +91,8 @@ UNKNOWN_SOURCE="&lt;unknown source&gt;"
tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
# matches if the string is valid number
number_re = re.compile(r'[-+]?(\d+|\d*\.\d+)$')
# global dictionary of libraries that have been loaded using get_library
libraries = {}
@ -632,12 +634,9 @@ def resolve_variable(path, context):
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
"""
if path[0].isdigit():
if number_re.match(path):
number_type = '.' in path and float or int
try:
current = number_type(path)
except ValueError:
current = settings.TEMPLATE_STRING_IF_INVALID
current = number_type(path)
elif path[0] in ('"', "'") and path[0] == path[-1]:
current = path[1:-1]
else:

View File

@ -70,14 +70,15 @@ def floatformat(text, arg=-1):
With a negative numeric argument, it will display that many decimal
places -- but only if there's places to be displayed.
Examples:
num1 = 34.23234
num2 = 34.00000
num1|floatformat results in 34.2
num2|floatformat is 34
num1|floatformat:3 is 34.232
num2|floatformat:3 is 34.000
num1|floatformat:-3 is 34.232
num2|floatformat:-3 is 34
* num1 = 34.23234
* num2 = 34.00000
* num1|floatformat results in 34.2
* num2|floatformat is 34
* num1|floatformat:3 is 34.232
* num2|floatformat:3 is 34.000
* num1|floatformat:-3 is 34.232
* num2|floatformat:-3 is 34
"""
try:
f = float(text)

View File

@ -435,6 +435,15 @@ def cycle(parser, token):
cycle = register.tag(cycle)
def debug(parser, token):
"""
Output a whole load of debugging information, including the current context and imported modules.
Sample usage::
<pre>
{% debug %}
</pre>
"""
return DebugNode()
debug = register.tag(debug)
@ -538,21 +547,6 @@ def do_for(parser, token):
do_for = register.tag("for", do_for)
def do_ifequal(parser, token, negate):
"""
Output the contents of the block if the two arguments equal/don't equal each other.
Examples::
{% ifequal user.id comment.user_id %}
...
{% endifequal %}
{% ifnotequal user.id comment.user_id %}
...
{% else %}
...
{% endifnotequal %}
"""
bits = list(token.split_contents())
if len(bits) != 3:
raise TemplateSyntaxError, "%r takes two arguments" % bits[0]
@ -568,11 +562,27 @@ def do_ifequal(parser, token, negate):
#@register.tag
def ifequal(parser, token):
"""
Output the contents of the block if the two arguments equal each other.
Examples::
{% ifequal user.id comment.user_id %}
...
{% endifequal %}
{% ifnotequal user.id comment.user_id %}
...
{% else %}
...
{% endifnotequal %}
"""
return do_ifequal(parser, token, False)
ifequal = register.tag(ifequal)
#@register.tag
def ifnotequal(parser, token):
"""Output the contents of the block if the two arguments are not equal. See ifequal."""
return do_ifequal(parser, token, True)
ifnotequal = register.tag(ifnotequal)
@ -889,8 +899,9 @@ templatetag = register.tag(templatetag)
def url(parser, token):
"""
Returns an absolute URL matching given view with its parameters. This is a
way to define links that aren't tied to a particular url configuration:
Returns an absolute URL matching given view with its parameters.
This is a way to define links that aren't tied to a particular URL configuration::
{% url path.to.some_view arg1,arg2,name1=value1 %}
@ -901,16 +912,16 @@ def url(parser, token):
URL. All arguments for the URL should be present.
For example if you have a view ``app_name.client`` taking client's id and
the corresponding line in a urlconf looks like this:
the corresponding line in a URLconf looks like this::
('^client/(\d+)/$', 'app_name.client')
and this app's urlconf is included into the project's urlconf under some
path:
and this app's URLconf is included into the project's URLconf under some
path::
('^clients/', include('project_name.app_name.urls'))
then in a template you can create a link for a certain client like this:
then in a template you can create a link for a certain client like this::
{% url app_name.client client.id %}

View File

@ -69,6 +69,14 @@ def encode_multipart(boundary, data):
'',
value.read()
])
elif hasattr(value, '__iter__'):
for item in value:
lines.extend([
'--' + boundary,
'Content-Disposition: form-data; name="%s"' % key,
'',
str(item)
])
else:
lines.extend([
'--' + boundary,

View File

@ -92,6 +92,13 @@ class SortedDict(dict):
"Returns the value of the item at the given zero-based index."
return self[self.keyOrder[index]]
def copy(self):
"Returns a copy of this object."
# This way of initializing the copy means it works for subclasses, too.
obj = self.__class__(self)
obj.keyOrder = self.keyOrder
return obj
class MultiValueDictKeyError(KeyError):
pass

View File

@ -13,6 +13,7 @@ Usage:
from django.utils.dates import MONTHS, MONTHS_3, MONTHS_AP, WEEKDAYS
from django.utils.tzinfo import LocalTimezone
from django.utils.translation import gettext as _
from calendar import isleap, monthrange
import re, time
@ -36,14 +37,14 @@ class TimeFormat(Formatter):
def a(self):
"'a.m.' or 'p.m.'"
if self.data.hour > 11:
return 'p.m.'
return 'a.m.'
return _('p.m.')
return _('a.m.')
def A(self):
"'AM' or 'PM'"
if self.data.hour > 11:
return 'PM'
return 'AM'
return _('PM')
return _('AM')
def B(self):
"Swatch Internet time"
@ -91,9 +92,9 @@ class TimeFormat(Formatter):
Proprietary extension.
"""
if self.data.minute == 0 and self.data.hour == 0:
return 'midnight'
return _('midnight')
if self.data.minute == 0 and self.data.hour == 12:
return 'noon'
return _('noon')
return '%s %s' % (self.f(), self.a())
def s(self):

View File

@ -17,7 +17,7 @@ def wrap(text, width):
pos = len(word) - word.rfind('\n') - 1
for word in it:
if "\n" in word:
lines = word.splitlines()
lines = word.split('\n')
else:
lines = (word,)
pos += len(lines[0]) + 1

View File

@ -9,16 +9,16 @@ def set_language(request):
"""
Redirect to a given url while setting the chosen language in the
session or cookie. The url and the language code need to be
specified in the GET paramters.
specified in the GET parameters.
"""
lang_code = request.GET['language']
lang_code = request.GET.get('language', None)
next = request.GET.get('next', None)
if not next:
next = request.META.get('HTTP_REFERER', None)
if not next:
next = '/'
response = http.HttpResponseRedirect(next)
if check_for_language(lang_code):
if lang_code and check_for_language(lang_code):
if hasattr(request, 'session'):
request.session['django_language'] = lang_code
else:

View File

@ -1,6 +1,6 @@
=====================
The "contrib" add-ons
=====================
============================
The "django.contrib" add-ons
============================
Django aims to follow Python's `"batteries included" philosophy`_. It ships
with a variety of extra, optional tools that solve common Web-development
@ -153,12 +153,16 @@ markup
A collection of template filters that implement these common markup languages:
* Textile
* Markdown
* ReST (ReStructured Text)
* `Textile`_
* `Markdown`_
* `ReST (ReStructured Text)`_
For documentation, read the source code in django/contrib/markup/templatetags/markup.py.
.. _Textile: http://en.wikipedia.org/wiki/Textile_%28markup_language%29
.. _Markdown: http://en.wikipedia.org/wiki/Markdown
.. _ReST (ReStructured Text): http://en.wikipedia.org/wiki/ReStructuredText
redirects
=========

View File

@ -195,7 +195,7 @@ The second part of this workflow involves a set of flags the describe what the
ticket has or needs in order to be "ready for checkin":
"Has patch"
The means the ticket has an associated patch_. These will be
This means the ticket has an associated patch_. These will be
reviewed to see if the patch is "good".
"Needs documentation"
@ -212,6 +212,33 @@ ticket has or needs in order to be "ready for checkin":
ready for checkin. This could mean the patch no longer applies
cleanly, or that the code doesn't live up to our standards.
A ticket can be resolved in a number of ways:
"fixed"
Used by one of the core developers once a patch has been rolled into
Django and the issue is fixed.
"invalid"
Used if the ticket is found to be incorrect or a user error.
"wontfix"
Used when a core developer decides that this request is not
appropriate for consideration in Django. This is usually chosen after
discussion in the ``django-developers`` mailing list, and you should
feel free to join in when it's something you care about.
"duplicate"
Used when another ticket covers the same issue. By closing duplicate
tickets, we keep all the discussion in one place, which helps everyone.
"worksforme"
Used when the triage team is unable to replicate the original bug.
If you believe that the ticket was closed in error -- because you're
still having the issue, or it's popped up somewhere else, or the triagers have
-- made a mistake, please reopen the ticket and tell us why. Please do not
reopen tickets that have been marked as "wontfix" by core developers.
.. _required details: `Reporting bugs`_
.. _good patch: `Patch style`_
.. _patch: `Submitting patches`_
@ -276,9 +303,11 @@ Please follow these coding standards when writing code for inclusion in Django:
def my_view(req, foo):
# ...
* Please don't put your name in the code. While we appreciate all
contributions to Django, our policy is not to publish individual
developer names in code -- for instance, at the top of Python modules.
* Please don't put your name in the code you contribute. Our policy is to
keep contributors' names in the ``AUTHORS`` file distributed with Django
-- not scattered throughout the codebase itself. Feel free to include a
change to the ``AUTHORS`` file in your patch if you make more than a
single trivial change.
Committing code
===============
@ -498,12 +527,12 @@ sure all other lines are commented::
# http://code.djangoproject.com/svn/django/trunk/
#
/path/to/trunk
# <branch> is a svn checkout of:
# http://code.djangoproject.com/svn/django/branches/<branch>/
#
#/path/to/<branch>
# On windows a path may look like this:
# C:/path/to/<branch>

162
docs/databases.txt Normal file
View File

@ -0,0 +1,162 @@
===============================
Notes about supported databases
===============================
Django attempts to support as many features as possible on all database
backends. However, not all database backends are alike, and we've had to make
design decisions on which features to support and which assumptions we can make
safely.
This file describes some of the features that might be relevant to Django
usage. Of course, it is not intended as a replacement for server-specific
documentation or reference manuals.
MySQL notes
===========
Django expects the database to support transactions, referential integrity,
and Unicode support (UTF-8 encoding). Fortunately, MySQL_ has all these
features as available as far back as 3.23. While it may be possible to use
3.23 or 4.0, you'll probably have less trouble if you use 4.1 or 5.0.
MySQL 4.1
---------
`MySQL 4.1`_ has greatly improved support for character sets. It is possible to
set different default character sets on the database, table, and column.
Previous versions have only a server-wide character set setting. It's also the
first version where the character set can be changed on the fly. 4.1 also has
support for views, but Django currently doesn't use views.
MySQL 5.0
---------
`MySQL 5.0`_ adds the ``information_schema`` database, which contains detailed
data on all database schema. Django's ``inspectdb`` feature uses this
``information_schema`` if it's available. 5.0 also has support for stored
procedures, but Django currently doesn't use stored procedures.
.. _MySQL: http://www.mysql.com/
.. _MySQL 4.1: http://dev.mysql.com/doc/refman/4.1/en/index.html
.. _MySQL 5.0: http://dev.mysql.com/doc/refman/5.0/en/index.html
Storage engines
---------------
MySQL has several `storage engines`_ (previously called table types). You can
change the default storage engine in the server configuration.
The default engine is MyISAM_. The main drawback of MyISAM is that it doesn't
currently support transactions or foreign keys. On the plus side, it's
currently the only engine that supports full-text indexing and searching.
The InnoDB_ engine is fully transactional and supports foreign key references.
The BDB_ engine, like InnoDB, is also fully transactional and supports foreign
key references. However, its use seems to be deprecated.
`Other storage engines`_, including SolidDB_ and Falcon_, are on the horizon.
For now, InnoDB is probably your best choice.
.. _storage engines: http://dev.mysql.com/doc/refman/5.0/en/storage-engines.html
.. _MyISAM: http://dev.mysql.com/doc/refman/5.0/en/myisam-storage-engine.html
.. _BDB: http://dev.mysql.com/doc/refman/5.0/en/bdb-storage-engine.html
.. _InnoDB: http://dev.mysql.com/doc/refman/5.0/en/innodb.html
.. _Other storage engines: http://dev.mysql.com/doc/refman/5.1/en/storage-engines-other.html
.. _SolidDB: http://forge.mysql.com/projects/view.php?id=139
.. _Falcon: http://dev.mysql.com/doc/falcon/en/index.html
MySQLdb
-------
`MySQLdb`_ is the Python interface to MySQL. 1.2.1 is the first version that
has support for MySQL 4.1 and newer. If you are trying to use an older version
of MySQL, then 1.2.0 *might* work for you.
.. _MySQLdb: http://sourceforge.net/projects/mysql-python
Creating your database
----------------------
You can `create your database`_ using the command-line tools and this SQL::
CREATE DATABASE <dbname> CHARACTER SET utf8;
This ensures all tables and columns will use UTF-8 by default.
.. _create your database: http://dev.mysql.com/doc/refman/5.0/en/create-database.html
Connecting to the database
--------------------------
Refer to the `settings documentation`_.
Connection settings are used in this order:
1. ``DATABASE_OPTIONS``
2. ``DATABASE_NAME``, ``DATABASE_USER``, ``DATABASE_PASSWORD``, ``DATABASE_HOST``,
``DATABASE_PORT``
3. MySQL option files.
In other words, if you set the name of the database in ``DATABASE_OPTIONS``,
this will take precedence over ``DATABASE_NAME``, which would override
anything in a `MySQL option file`_.
Here's a sample configuration which uses a MySQL option file::
# settings.py
DATABASE_ENGINE = "mysql"
DATABASE_OPTIONS = {
'read_default_file': '/path/to/my.cnf',
}
# my.cnf
[client]
database = DATABASE_NAME
user = DATABASE_USER
passwd = DATABASE_PASSWORD
default-character-set = utf8
Several other MySQLdb connection options may be useful, such as ``ssl``,
``use_unicode``, ``init_command``, and ``sql_mode``. Consult the
`MySQLdb documentation`_ for more details.
.. _settings documentation: http://www.djangoproject.com/documentation/settings/#database-engine
.. _MySQL option file: http://dev.mysql.com/doc/refman/5.0/en/option-files.html
.. _MySQLdb documentation: http://mysql-python.sourceforge.net/
Creating your tables
--------------------
When Django generates the schema, it doesn't specify a storage engine, so
tables will be created with whatever default storage engine your database
server is configured for. The easiest solution is to set your database server's
default storage engine to the desired engine.
If you're using a hosting service and can't change your server's default
storage engine, you have a couple of options.
* After the tables are created, execute an ``ALTER TABLE`` statement to
convert a table to a new storage engine (such as InnoDB)::
ALTER TABLE <tablename> ENGINE=INNODB;
This can be tedious if you have a lot of tables.
* Another option is to use the ``init_command`` option for MySQLdb prior to
creating your tables::
DATABASE_OPTIONS = {
# ...
"init_command": "SET storage_engine=INNODB",
# ...
}
This sets the default storage engine upon connecting to the database.
After your tables have been created, you should remove this option.
* Another method for changing the storage engine is described in
AlterModelOnSyncDB_.
.. _AlterModelOnSyncDB: http://code.djangoproject.com/wiki/AlterModelOnSyncDB

View File

@ -6,7 +6,7 @@ Once you've created your `data models`_, Django automatically gives you a
database-abstraction API that lets you create, retrieve, update and delete
objects. This document explains that API.
.. _`data models`: http://www.djangoproject.com/documentation/model_api/
.. _`data models`: ../model_api/
Throughout this reference, we'll refer to the following models, which comprise
a weblog application::
@ -85,7 +85,7 @@ There's no way to tell what the value of an ID will be before you call
unless you explicitly specify ``primary_key=True`` on a field. See the
`AutoField documentation`_.)
.. _AutoField documentation: http://www.djangoproject.com/documentation/model_api/#autofield
.. _AutoField documentation: ../model_api/#autofield
Explicitly specifying auto-primary-key values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1801,4 +1801,4 @@ interface to your database. You can access your database via other tools,
programming languages or database frameworks; there's nothing Django-specific
about your database.
.. _Executing custom SQL: http://www.djangoproject.com/documentation/model_api/#executing-custom-sql
.. _Executing custom SQL: ../model_api/#executing-custom-sql

View File

@ -57,7 +57,7 @@ Gentoo
------
A Django build is available for `Gentoo Linux`_, and is based on Django 0.95.1.
The `current Gentoo build`_ can be installed by typing ``emerge Django``.
The `current Gentoo build`_ can be installed by typing ``emerge django``.
.. _Gentoo Linux: http://www.gentoo.org/
.. _current Gentoo build: http://packages.gentoo.org/packages/?category=dev-python;name=django

View File

@ -216,7 +216,7 @@ installation will be aborted, and any data installed in the call to
``loaddata`` will be removed from the database.
The fixtures that are named can include directory components. These
directories will be inluded in the search path. For example::
directories will be included in the search path. For example::
django-admin.py loaddata foo/bar/mydata.json

View File

@ -304,3 +304,14 @@ If you have access to a command shell on a Unix system, you can accomplish this
easily by using the ``touch`` command::
touch mysite.fcgi
Serving admin media files
=========================
Regardless of the server and configuration you eventually decide to use, you will
also need to give some thought to how to serve the admin media files. The
advice given in the modpython_ documentation is also applicable in the setups
detailed above.
.. _modpython: ../modpython/#serving-the-admin-files

View File

@ -417,6 +417,27 @@ Here's a simple function that might drive the above form::
form = forms.FormWrapper(manipulator, new_data, errors)
return render_to_response('contact_form.html', {'form': form})
Implementing ``flatten_data`` for custom manipulators
------------------------------------------------------
It is possible (although rarely needed) to replace the default automatically
created manipulators on a model with your own custom manipulators. If you do
this and you are intending to use those models in generic views, you should
also define a ``flatten_data`` method in any ``ChangeManipulator`` replacement.
This should act like the default ``flatten_data`` and return a dictionary
mapping field names to their values, like so::
def flatten_data(self):
obj = self.original_object
return dict(
from = obj.from,
subject = obj.subject,
...
)
In this way, your new change manipulator will act exactly like the default
version.
``FileField`` and ``ImageField`` special cases
==============================================

View File

@ -282,6 +282,17 @@ How to create language files
Once you've tagged your strings for later translation, you need to write (or
obtain) the language translations themselves. Here's how that works.
.. admonition:: Locale restrictions
Django does not support localizing your application into a locale for
which Django itself has not been translated. In this case, it will ignore
your translation files. If you were to try this and Django supported it,
you would inevitably see a mixture of translated strings (from your
application) and English strings (from Django itself). If you want to
support a locale for your application that is not already part of
Django, you'll need to make at least a minimal translation of the Django
core.
Message files
-------------

View File

@ -1216,6 +1216,10 @@ screen via ``<script src="">`` tags. This can be used to tweak a given type of
admin page in JavaScript or to provide "quick links" to fill in default values
for certain fields.
If you use relative URLs -- URLs that don't start with ``http://`` or ``/`` --
then the admin site will automatically prefix these links with
``settings.ADMIN_MEDIA_PREFIX``.
``list_display``
----------------

264
docs/release_notes_0.96.txt Normal file
View File

@ -0,0 +1,264 @@
=================================
Django version 0.96 release notes
=================================
Welcome to Django 0.96!
The primary goal for 0.96 is a cleanup and stabilization of the features
introduced in 0.95. There have been a few small `backwards-incompatible
changes`_ since 0.95, but the upgrade process should be fairly simple
and should not require major changes to existing applications.
However, we're also releasing 0.96 now because we have a set of
backwards-incompatible changes scheduled for the near future. Once
completed, they will involve some code changes for application
developers, so we recommend that you stick with Django 0.96 until the
next official release; then you'll be able to upgrade in one step
instead of needing to make incremental changes to keep up with the
development version of Django.
What's new in 0.96?
===================
This revision represents over a thousand source commits and over four hundred
bug fixes, so we can't possibly catalog all the changes. Here, we describe the
most notable changes in this release.
New forms library
-----------------
``django.newforms`` is Django's new form-handling library. It's a
replacement for ``django.forms``, the old form/manipulator/validation
framework. Both APIs are available in 0.96, but over the next two
releases we plan to switch completely to the new forms system, and
deprecate and remove the old system.
There are three elements to this transition:
* We've copied the current ``django.forms`` to
``django.oldforms``. This allows you to upgrade your code *now*
rather than waiting for the backwards-incompatible change and
rushing to fix your code after the fact. Just change your
import statements like this::
from django import forms # 0.95-style
from django import oldforms as forms # 0.96-style
* The next official release of Django will move the current
``django.newforms`` to ``django.forms``. This will be a
backwards-incompatible change, and anyone still using the old
version of ``django.forms`` at that time will need to change
their import statements as described above.
* The next release after that will completely remove
``django.oldforms``.
Although the ``newforms`` library will continue to evolve, it's ready for use
for most common cases. We recommend that anyone new to form handling skip the
old forms system and start with the new.
For more information about ``django.newforms``, read the `newforms
documentation`_.
.. _newforms documentation: ../newforms/
URLconf improvements
--------------------
You can now use any callable as the callback in URLconfs (previously, only
strings that referred to callables were allowed). This allows a much more
natural use of URLconfs. For example, this URLconf::
from django.conf.urls.defaults import *
urlpatterns = patterns('',
('^myview/$', 'mysite.myapp.views.myview')
)
can now be rewritten as::
from django.conf.urls.defaults import *
from mysite.myapp.views import myview
urlpatterns = patterns('',
('^myview/$', myview)
)
One useful application of this can be seen when using decorators; this
change allows you to apply decorators to views *in your
URLconf*. Thus, you can make a generic view require login very
easily::
from django.conf.urls.defaults import *
from django.contrib.auth.decorators import login_required
from django.views.generic.list_detail import object_list
from mysite.myapp.models import MyModel
info = {
"queryset" : MyModel.objects.all(),
}
urlpatterns = patterns('',
('^myview/$', login_required(object_list), info)
)
Note that both syntaxes (strings and callables) are valid, and will continue to
be valid for the foreseeable future.
The test framework
------------------
Django now includes a test framework so you can start transmuting fear into
boredom (with apologies to Kent Beck). You can write tests based on doctest_
or unittest_ and test your views with a simple test client.
There is also new support for "fixtures" -- initial data, stored in any of the
supported `serialization formats`_, that will be loaded into your database at the
start of your tests. This makes testing with real data much easier.
See `the testing documentation`_ for the full details.
.. _doctest: http://docs.python.org/lib/module-doctest.html
.. _unittest: http://docs.python.org/lib/module-unittest.html
.. _the testing documentation: ../testing/
.. _serialization formats: ../serialization/
Improvements to the admin interface
-----------------------------------
A small change, but a very nice one: dedicated views for adding and
updating users have been added to the admin interface, so you no
longer need to worry about working with hashed passwords in the admin.
Backwards-incompatible changes
==============================
The following changes may require you to update your code when you switch from
0.95 to 0.96:
`MySQLdb` version requirement
-----------------------------
Due to a bug in older versions of the `MySQLdb` Python module (which
Django uses to connect to MySQL databases), Django's MySQL backend now
requires version 1.2.1p2 or higher of `MySQLdb`, and will raise
exceptions if you attempt to use an older version.
If you're currently unable to upgrade your copy of `MySQLdb` to meet
this requirement, a separate, backwards-compatible backend, called
"mysql_old", has been added to Django. To use this backend, change
the ``DATABASE_ENGINE`` setting in your Django settings file from
this::
DATABASE_ENGINE = "mysql"
to this::
DATABASE_ENGINE = "mysql_old"
However, we strongly encourage MySQL users to upgrade to a more recent
version of `MySQLdb` as soon as possible, The "mysql_old" backend is
provided only to ease this transition, and is considered deprecated;
aside from any necessary security fixes, it will not be actively
maintained, and it will be removed in a future release of Django.
Also, note that some features, like the new ``DATABASE_OPTIONS``
setting (see the `databases documentation`_ for details), are only
available on the "mysql" backend, and will not be made available for
"mysql_old".
.. _databases: ../databases/
Database constraint names changed
---------------------------------
The format of the constraint names Django generates for foreign key
references have changed slightly. These names are generally only used
when it is not possible to put the reference directly on the affected
column, so they is not always visible.
The effect of this change is that running ``manage.py reset`` and
similar commands against an existing database may generate SQL with
the new form of constraint name, while the database itself contains
constraints named in the old form; this will cause the database server
to raise an error message about modifying non-existent constraints.
If you need to work around this, there are two methods available:
1. Redirect the output of ``manage.py`` to a file, and edit the
generated SQL to use the correct constraint names before
executing it.
2. Examine the output of ``manage.py sqlall`` to see the new-style
constraint names, and use that as a guide to rename existing
constraints in your database.
Names changes in ``manage.py``
------------------------------
A few of the options to ``manage.py`` have changed with the addition of fixture
support:
* There are new ``dumpdata`` and ``loaddata`` commands which, as
you might expect, will dump and load data to/from the
database. These commands can operate against any of Django's
supported serialization formats.
* The ``sqlinitialdata`` command has been renamed to ``sqlcustom`` to
emphasize that ``loaddata`` should be used for data (and ``sqlcustom`` for
other custom SQL -- views, stored procedures, etc.).
* The vestigial ``install`` command has been removed. Use ``syncdb``.
Backslash escaping changed
--------------------------
The Django database API now escapes backslashes given as query parameters. If
you have any database API code that matches backslashes, and it was working before
(despite the lack of escaping), you'll have to change your code to "unescape" the
slashes one level.
For example, this used to work::
# Find text containing a single backslash
MyModel.objects.filter(text__contains='\\\\')
The above is now incorrect, and should be rewritten as::
# Find text containing a single backslash
MyModel.objects.filter(text__contains='\\')
Removed ENABLE_PSYCO setting
----------------------------
The ``ENABLE_PSYCO`` setting no longer exists. If your settings file includes
``ENABLE_PSYCO`` it will have no effect; to use Psyco, we recommend
writing a middleware class to activate it.
.. _psyco: http://psyco.sourceforge.net/
Thanks
======
Since 0.95, a number of people have stepped forward and taken a major
new role in Django's development. We'd like to thank these people for
all their hard work:
* Russell Keith-Magee and Malcolm Tredinnick for their major code
contributions. This release wouldn't have been possible without them.
* Our new release manager, James Bennett, for his work in getting out
0.95.1, 0.96, and (hopefully) future release.
* Our ticket managers Chris Beaven (aka SmileyChris), Simon Greenhill,
Michael Radziej, and Gary Wilson. They agreed to take on the monumental
task of wrangling our tickets into nicely cataloged submission. Figuring
out what to work on is now about a million times easier; thanks again,
guys.
* Everyone who submitted a bug report, patch or ticket comment. We can't
possibly thank everyone by name -- over 200 developers submitted patches
that went into 0.96 -- but everyone who's contributed to Django is listed
in AUTHORS_.
.. _AUTHORS: http://code.djangoproject.com/browser/django/trunk/AUTHORS

View File

@ -31,7 +31,8 @@ but it'll almost always be a QuerySet).
You can also use a serializer object directly::
xml_serializer = serializers.get_serializer("xml")
XMLSerializer = serializers.get_serializer("xml")
xml_serializer = XMLSerializer()
xml_serializer.serialize(queryset)
data = xml_serializer.getvalue()

View File

@ -162,10 +162,13 @@ a model object and return its URL. This is a way of overriding
``get_absolute_url()`` methods on a per-installation basis. Example::
ABSOLUTE_URL_OVERRIDES = {
'blogs.Weblog': lambda o: "/blogs/%s/" % o.slug,
'news.Story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
'blogs.weblog': lambda o: "/blogs/%s/" % o.slug,
'news.story': lambda o: "/stories/%s/%s/" % (o.pub_year, o.slug),
}
Note that the model name used in this setting should be all lower-case, regardless
of the case of the actual model class name.
ADMIN_FOR
---------
@ -242,7 +245,8 @@ DATABASE_ENGINE
Default: ``''`` (Empty string)
Which database backend to use. Either ``'postgresql_psycopg2'``,
``'postgresql'``, ``'mysql'``, ``'sqlite3'`` or ``'ado_mssql'``.
``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'sqlite3'`` or
``'ado_mssql'``.
DATABASE_HOST
-------------

View File

@ -227,6 +227,12 @@ can be invoked on the ``Client`` instance.
The key-value pairs in the data dictionary will be encoded as a multipart
message and used to create the POST data payload.
To submit multiple values for a given key (for example, to specify
the selections for a multiple selection list), provide the values as a
list or tuple for the required key. For example, a data dictionary of
``{'choices': ('a','b','d')}`` would submit three selected rows for the
field named ``choices``.
Submitting files is a special case. To POST a file, you need only
provide the file field name as a key, and a file handle to the file you wish to
upload as a value. The Test Client will populate the two POST fields (i.e.,

View File

@ -147,7 +147,7 @@ complete -F _django_completion django-admin.py manage.py
# Support for multiple interpreters.
unset pythons
if command -v whereis &>/dev/null; then
python_interpreters=$(whereis -b python | cut -d " " -f 2-)
python_interpreters=$(whereis python | cut -d " " -f 2-)
for python in $python_interpreters; do
pythons="${pythons} $(basename $python)"
done

View File

@ -67,13 +67,13 @@ __test__ = {'API_TESTS': """
# Try to load fixture 2 using format discovery; this will fail
# because there are two fixture2's in the fixtures directory
>>> management.load_data(['fixture2'], verbosity=0) # doctest: +ELLIPSIS
Multiple fixtures named 'fixture2' in '.../fixtures'. Aborting.
Multiple fixtures named 'fixture2' in '...fixtures'. Aborting.
>>> Article.objects.all()
[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
# Dump the current contents of the database as a JSON fixture
>>> management.dump_data(['fixtures'], format='json')
>>> print management.dump_data(['fixtures'], format='json')
[{"pk": "3", "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": "2", "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": "1", "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
"""}

View File

@ -44,7 +44,7 @@ __test__ = {'API_TESTS':"""
>>> get_object_or_404(Article, title="Foo")
Traceback (most recent call last):
...
Http404
Http404: No Article matches the given query.
# Create an Article.
>>> article = Article.objects.create(title="Run away!")
@ -63,7 +63,7 @@ Http404
>>> get_object_or_404(a.article_set, title__contains="Camelot")
Traceback (most recent call last):
...
Http404
Http404: No Article matches the given query.
# Custom managers can be used too.
>>> get_object_or_404(Article.by_a_sir, title="Run away!")
@ -77,7 +77,7 @@ Http404
>>> get_list_or_404(a.article_set, title__icontains='Shrubbery')
Traceback (most recent call last):
...
Http404
Http404: No Article matches the given query.
# Custom managers can be used too.
>>> get_list_or_404(Article.by_a_sir, title__icontains="Run")

View File

@ -81,7 +81,43 @@ class ClientTest(TestCase):
# Check that the response was a 302 (redirect)
self.assertEqual(response.status_code, 302)
def test_valid_form(self):
"POST valid data to a form"
post_data = {
'text': 'Hello World',
'email': 'foo@example.com',
'value': 37,
'single': 'b',
'multi': ('b','c','e')
}
response = self.client.post('/test_client/form_view/', post_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, "Valid POST Template")
def test_incomplete_data_form(self):
"POST incomplete data to a form"
post_data = {
'text': 'Hello World',
'value': 37
}
response = self.client.post('/test_client/form_view/', post_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, "Invalid POST Template")
def test_form_error(self):
"POST erroneous data to a form"
post_data = {
'text': 'Hello World',
'email': 'not an email address',
'value': 37,
'single': 'b',
'multi': ('b','c','e')
}
response = self.client.post('/test_client/form_view/', post_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, "Invalid POST Template")
def test_unknown_page(self):
"GET an invalid URL"
response = self.client.get('/test_client/unknown_view/')

View File

@ -6,6 +6,7 @@ urlpatterns = patterns('',
(r'^post_view/$', views.post_view),
(r'^raw_post_view/$', views.raw_post_view),
(r'^redirect_view/$', views.redirect_view),
(r'^form_view/$', views.form_view),
(r'^login_protected_view/$', views.login_protected_view),
(r'^session_view/$', views.session_view),
(r'^broken_view/$', views.broken_view)

View File

@ -2,6 +2,8 @@ from xml.dom.minidom import parseString
from django.template import Context, Template
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.newforms.forms import Form
from django.newforms import fields
def get_view(request):
"A simple view that expects a GET request, and returns a rendered template"
@ -45,7 +47,39 @@ def raw_post_view(request):
def redirect_view(request):
"A view that redirects all requests to the GET view"
return HttpResponseRedirect('/test_client/get_view/')
TestChoices = (
('a', 'First Choice'),
('b', 'Second Choice'),
('c', 'Third Choice'),
('d', 'Fourth Choice'),
('e', 'Fifth Choice')
)
class TestForm(Form):
text = fields.CharField()
email = fields.EmailField()
value = fields.IntegerField()
single = fields.ChoiceField(choices=TestChoices)
multi = fields.MultipleChoiceField(choices=TestChoices)
def form_view(request):
"A view that tests a simple form"
if request.method == 'POST':
form = TestForm(request.POST)
if form.is_valid():
t = Template('Valid POST data.', name='Valid POST Template')
c = Context()
else:
t = Template('Invalid POST data. {{ form.errors }}', name='Invalid POST Template')
c = Context({'form': form})
else:
form = TestForm()
t = Template('Viewing base form. {{ form }}.', name='Form GET Template')
c = Context({'form': form})
return HttpResponse(t.render(c))
def login_protected_view(request):
"A simple view that is login protected."
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')

View File

@ -31,4 +31,35 @@
'nonexistent'
>>> d.setlist('lastname', ['Holovaty', 'Willison'])
"""
### SortedDict #################################################################
>>> d = SortedDict()
>>> d['one'] = 'one'
>>> d['two'] = 'two'
>>> d['three'] = 'three'
>>> d['one']
'one'
>>> d['two']
'two'
>>> d['three']
'three'
>>> d.keys()
['one', 'two', 'three']
>>> d.values()
['one', 'two', 'three']
>>> d['one'] = 'not one'
>>> d['one']
'not one'
>>> d.keys() == d.copy().keys()
True
### DotExpandedDict ############################################################
>>> d = DotExpandedDict({'person.1.firstname': ['Simon'], 'person.1.lastname': ['Willison'], 'person.2.firstname': ['Adrian'], 'person.2.lastname': ['Holovaty']})
>>> d['person']['1']['lastname']
['Willison']
>>> d['person']['2']['lastname']
['Holovaty']
>>> d['person']['2']['firstname']
['Adrian']
"""

View File

@ -133,6 +133,12 @@ u'\xcb'
>>> wordwrap('this is a long paragraph of text that really needs to be wrapped I\'m afraid', 14)
"this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\nI'm afraid"
>>> wordwrap('this is a short paragraph of text.\n But this line should be indented',14)
'this is a\nshort\nparagraph of\ntext.\n But this\nline should be\nindented'
>>> wordwrap('this is a short paragraph of text.\n But this line should be indented',15)
'this is a short\nparagraph of\ntext.\n But this line\nshould be\nindented'
>>> ljust('test', 10)
'test '

View File

@ -1,13 +1,34 @@
from django.db import models
# If ticket #1578 ever slips back in, these models will not be able to be
# created (the field names being lower-cased versions of their opposite
# classes is important here).
class First(models.Model):
second = models.IntegerField()
class Second(models.Model):
first = models.ForeignKey(First, related_name = 'the_first')
# If ticket #1578 ever slips back in, these models will not be able to be
# created (the field names being lower-cased versions of their opposite
# classes is important here).
# Protect against repetition of #1839, #2415 and #2536.
class Third(models.Model):
name = models.CharField(maxlength=20)
third = models.ForeignKey('self', null=True, related_name='child_set')
__test__ = {'API_TESTS':""}
class Parent(models.Model):
name = models.CharField(maxlength=20)
bestchild = models.ForeignKey('Child', null=True, related_name='favored_by')
class Child(models.Model):
name = models.CharField(maxlength=20)
parent = models.ForeignKey(Parent)
__test__ = {'API_TESTS':"""
>>> Third.AddManipulator().save(dict(id='3', name='An example', another=None))
<Third: Third object>
>>> parent = Parent(name = 'fred')
>>> parent.save()
>>> Child.AddManipulator().save(dict(name='bam-bam', parent=parent.id))
<Child: Child object>
"""}

View File

@ -0,0 +1,187 @@
"""
A test spanning all the capabilities of all the serializers.
This class sets up a model for each model field type
(except for image types, because of the PIL dependency).
"""
from django.db import models
from django.contrib.contenttypes.models import ContentType
# The following classes are for testing basic data
# marshalling, including NULL values.
class BooleanData(models.Model):
data = models.BooleanField(null=True)
class CharData(models.Model):
data = models.CharField(maxlength=30, null=True)
class DateData(models.Model):
data = models.DateField(null=True)
class DateTimeData(models.Model):
data = models.DateTimeField(null=True)
class EmailData(models.Model):
data = models.EmailField(null=True)
class FileData(models.Model):
data = models.FileField(null=True, upload_to='/foo/bar')
class FilePathData(models.Model):
data = models.FilePathField(null=True)
class FloatData(models.Model):
data = models.FloatField(null=True, decimal_places=3, max_digits=5)
class IntegerData(models.Model):
data = models.IntegerField(null=True)
# class ImageData(models.Model):
# data = models.ImageField(null=True)
class IPAddressData(models.Model):
data = models.IPAddressField(null=True)
class NullBooleanData(models.Model):
data = models.NullBooleanField(null=True)
class PhoneData(models.Model):
data = models.PhoneNumberField(null=True)
class PositiveIntegerData(models.Model):
data = models.PositiveIntegerField(null=True)
class PositiveSmallIntegerData(models.Model):
data = models.PositiveSmallIntegerField(null=True)
class SlugData(models.Model):
data = models.SlugField(null=True)
class SmallData(models.Model):
data = models.SmallIntegerField(null=True)
class TextData(models.Model):
data = models.TextField(null=True)
class TimeData(models.Model):
data = models.TimeField(null=True)
class USStateData(models.Model):
data = models.USStateField(null=True)
class XMLData(models.Model):
data = models.XMLField(null=True)
class Tag(models.Model):
"""A tag on an item."""
data = models.SlugField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = models.GenericForeignKey()
class Meta:
ordering = ["data"]
class GenericData(models.Model):
data = models.CharField(maxlength=30)
tags = models.GenericRelation(Tag)
# The following test classes are all for validation
# of related objects; in particular, forward, backward,
# and self references.
class Anchor(models.Model):
"""This is a model that can be used as
something for other models to point at"""
data = models.CharField(maxlength=30)
class FKData(models.Model):
data = models.ForeignKey(Anchor, null=True)
class M2MData(models.Model):
data = models.ManyToManyField(Anchor, null=True)
class O2OData(models.Model):
data = models.OneToOneField(Anchor, null=True)
class FKSelfData(models.Model):
data = models.ForeignKey('self', null=True)
class M2MSelfData(models.Model):
data = models.ManyToManyField('self', null=True, symmetrical=False)
# The following test classes are for validating the
# deserialization of objects that use a user-defined
# field as the primary key.
# Some of these data types have been commented out
# because they can't be used as a primary key on one
# or all database backends.
class BooleanPKData(models.Model):
data = models.BooleanField(primary_key=True)
class CharPKData(models.Model):
data = models.CharField(maxlength=30, primary_key=True)
# class DatePKData(models.Model):
# data = models.DateField(primary_key=True)
# class DateTimePKData(models.Model):
# data = models.DateTimeField(primary_key=True)
class EmailPKData(models.Model):
data = models.EmailField(primary_key=True)
class FilePKData(models.Model):
data = models.FileField(primary_key=True, upload_to='/foo/bar')
class FilePathPKData(models.Model):
data = models.FilePathField(primary_key=True)
class FloatPKData(models.Model):
data = models.FloatField(primary_key=True, decimal_places=3, max_digits=5)
class IntegerPKData(models.Model):
data = models.IntegerField(primary_key=True)
# class ImagePKData(models.Model):
# data = models.ImageField(primary_key=True)
class IPAddressPKData(models.Model):
data = models.IPAddressField(primary_key=True)
class NullBooleanPKData(models.Model):
data = models.NullBooleanField(primary_key=True)
class PhonePKData(models.Model):
data = models.PhoneNumberField(primary_key=True)
class PositiveIntegerPKData(models.Model):
data = models.PositiveIntegerField(primary_key=True)
class PositiveSmallIntegerPKData(models.Model):
data = models.PositiveSmallIntegerField(primary_key=True)
class SlugPKData(models.Model):
data = models.SlugField(primary_key=True)
class SmallPKData(models.Model):
data = models.SmallIntegerField(primary_key=True)
# class TextPKData(models.Model):
# data = models.TextField(primary_key=True)
# class TimePKData(models.Model):
# data = models.TimeField(primary_key=True)
class USStatePKData(models.Model):
data = models.USStateField(primary_key=True)
# class XMLPKData(models.Model):
# data = models.XMLField(primary_key=True)

View File

@ -0,0 +1,263 @@
"""
A test spanning all the capabilities of all the serializers.
This class defines sample data and a dynamically generated
test case that is capable of testing the capabilities of
the serializers. This includes all valid data values, plus
forward, backwards and self references.
"""
import unittest, datetime
from django.utils.functional import curry
from django.core import serializers
from django.db import transaction
from django.core import management
from models import *
# A set of functions that can be used to recreate
# test data objects of various kinds
def data_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data
instance.save()
return instance
def generic_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data[0]
instance.save()
for tag in data[1:]:
instance.tags.create(data=tag)
return instance
def fk_create(pk, klass, data):
instance = klass(id=pk)
setattr(instance, 'data_id', data)
instance.save()
return instance
def m2m_create(pk, klass, data):
instance = klass(id=pk)
instance.save()
instance.data = data
return instance
def o2o_create(pk, klass, data):
instance = klass()
instance.data_id = data
instance.save()
return instance
def pk_create(pk, klass, data):
instance = klass()
instance.data = data
instance.save()
return instance
# A set of functions that can be used to compare
# test data objects of various kinds
def data_compare(testcase, pk, klass, data):
instance = klass.objects.get(id=pk)
testcase.assertEqual(data, instance.data,
"Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % (pk,data, type(data), instance.data, type(instance.data)))
def generic_compare(testcase, pk, klass, data):
instance = klass.objects.get(id=pk)
testcase.assertEqual(data[0], instance.data)
testcase.assertEqual(data[1:], [t.data for t in instance.tags.all()])
def fk_compare(testcase, pk, klass, data):
instance = klass.objects.get(id=pk)
testcase.assertEqual(data, instance.data_id)
def m2m_compare(testcase, pk, klass, data):
instance = klass.objects.get(id=pk)
testcase.assertEqual(data, [obj.id for obj in instance.data.all()])
def o2o_compare(testcase, pk, klass, data):
instance = klass.objects.get(data=data)
testcase.assertEqual(data, instance.data_id)
def pk_compare(testcase, pk, klass, data):
instance = klass.objects.get(data=data)
testcase.assertEqual(data, instance.data)
# Define some data types. Each data type is
# actually a pair of functions; one to create
# and one to compare objects of that type
data_obj = (data_create, data_compare)
generic_obj = (generic_create, generic_compare)
fk_obj = (fk_create, fk_compare)
m2m_obj = (m2m_create, m2m_compare)
o2o_obj = (o2o_create, o2o_compare)
pk_obj = (pk_create, pk_compare)
test_data = [
# Format: (data type, PK value, Model Class, data)
(data_obj, 1, BooleanData, True),
(data_obj, 2, BooleanData, False),
(data_obj, 10, CharData, "Test Char Data"),
(data_obj, 11, CharData, ""),
(data_obj, 12, CharData, "None"),
(data_obj, 13, CharData, "null"),
(data_obj, 14, CharData, "NULL"),
(data_obj, 15, CharData, None),
(data_obj, 20, DateData, datetime.date(2006,6,16)),
(data_obj, 21, DateData, None),
(data_obj, 30, DateTimeData, datetime.datetime(2006,6,16,10,42,37)),
(data_obj, 31, DateTimeData, None),
(data_obj, 40, EmailData, "hovercraft@example.com"),
(data_obj, 41, EmailData, None),
(data_obj, 50, FileData, 'file:///foo/bar/whiz.txt'),
(data_obj, 51, FileData, None),
(data_obj, 60, FilePathData, "/foo/bar/whiz.txt"),
(data_obj, 61, FilePathData, None),
(data_obj, 70, FloatData, 12.345),
(data_obj, 71, FloatData, -12.345),
(data_obj, 72, FloatData, 0.0),
(data_obj, 73, FloatData, None),
(data_obj, 80, IntegerData, 123456789),
(data_obj, 81, IntegerData, -123456789),
(data_obj, 82, IntegerData, 0),
(data_obj, 83, IntegerData, None),
#(XX, ImageData
(data_obj, 90, IPAddressData, "127.0.0.1"),
(data_obj, 91, IPAddressData, None),
(data_obj, 100, NullBooleanData, True),
(data_obj, 101, NullBooleanData, False),
(data_obj, 102, NullBooleanData, None),
(data_obj, 110, PhoneData, "212-634-5789"),
(data_obj, 111, PhoneData, None),
(data_obj, 120, PositiveIntegerData, 123456789),
(data_obj, 121, PositiveIntegerData, None),
(data_obj, 130, PositiveSmallIntegerData, 12),
(data_obj, 131, PositiveSmallIntegerData, None),
(data_obj, 140, SlugData, "this-is-a-slug"),
(data_obj, 141, SlugData, None),
(data_obj, 150, SmallData, 12),
(data_obj, 151, SmallData, -12),
(data_obj, 152, SmallData, 0),
(data_obj, 153, SmallData, None),
(data_obj, 160, TextData, """This is a long piece of text.
It contains line breaks.
Several of them.
The end."""),
(data_obj, 161, TextData, ""),
(data_obj, 162, TextData, None),
(data_obj, 170, TimeData, datetime.time(10,42,37)),
(data_obj, 171, TimeData, None),
(data_obj, 180, USStateData, "MA"),
(data_obj, 181, USStateData, None),
(data_obj, 190, XMLData, "<foo></foo>"),
(data_obj, 191, XMLData, None),
(generic_obj, 200, GenericData, ['Generic Object 1', 'tag1', 'tag2']),
(generic_obj, 201, GenericData, ['Generic Object 2', 'tag2', 'tag3']),
(data_obj, 300, Anchor, "Anchor 1"),
(data_obj, 301, Anchor, "Anchor 2"),
(fk_obj, 400, FKData, 300), # Post reference
(fk_obj, 401, FKData, 500), # Pre reference
(fk_obj, 402, FKData, None), # Empty reference
(m2m_obj, 410, M2MData, []), # Empty set
(m2m_obj, 411, M2MData, [300,301]), # Post reference
(m2m_obj, 412, M2MData, [500,501]), # Pre reference
(m2m_obj, 413, M2MData, [300,301,500,501]), # Pre and Post reference
(o2o_obj, None, O2OData, 300), # Post reference
(o2o_obj, None, O2OData, 500), # Pre reference
(fk_obj, 430, FKSelfData, 431), # Pre reference
(fk_obj, 431, FKSelfData, 430), # Post reference
(fk_obj, 432, FKSelfData, None), # Empty reference
(m2m_obj, 440, M2MSelfData, []),
(m2m_obj, 441, M2MSelfData, []),
(m2m_obj, 442, M2MSelfData, [440, 441]),
(m2m_obj, 443, M2MSelfData, [445, 446]),
(m2m_obj, 444, M2MSelfData, [440, 441, 445, 446]),
(m2m_obj, 445, M2MSelfData, []),
(m2m_obj, 446, M2MSelfData, []),
(data_obj, 500, Anchor, "Anchor 3"),
(data_obj, 501, Anchor, "Anchor 4"),
(pk_obj, 601, BooleanPKData, True),
(pk_obj, 602, BooleanPKData, False),
(pk_obj, 610, CharPKData, "Test Char PKData"),
# (pk_obj, 620, DatePKData, datetime.date(2006,6,16)),
# (pk_obj, 630, DateTimePKData, datetime.datetime(2006,6,16,10,42,37)),
(pk_obj, 640, EmailPKData, "hovercraft@example.com"),
(pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'),
(pk_obj, 660, FilePathPKData, "/foo/bar/whiz.txt"),
(pk_obj, 670, FloatPKData, 12.345),
(pk_obj, 671, FloatPKData, -12.345),
(pk_obj, 672, FloatPKData, 0.0),
(pk_obj, 680, IntegerPKData, 123456789),
(pk_obj, 681, IntegerPKData, -123456789),
(pk_obj, 682, IntegerPKData, 0),
# (XX, ImagePKData
(pk_obj, 690, IPAddressPKData, "127.0.0.1"),
(pk_obj, 700, NullBooleanPKData, True),
(pk_obj, 701, NullBooleanPKData, False),
(pk_obj, 710, PhonePKData, "212-634-5789"),
(pk_obj, 720, PositiveIntegerPKData, 123456789),
(pk_obj, 730, PositiveSmallIntegerPKData, 12),
(pk_obj, 740, SlugPKData, "this-is-a-slug"),
(pk_obj, 750, SmallPKData, 12),
(pk_obj, 751, SmallPKData, -12),
(pk_obj, 752, SmallPKData, 0),
# (pk_obj, 760, TextPKData, """This is a long piece of text.
# It contains line breaks.
# Several of them.
# The end."""),
# (pk_obj, 770, TimePKData, datetime.time(10,42,37)),
(pk_obj, 780, USStatePKData, "MA"),
# (pk_obj, 790, XMLPKData, "<foo></foo>"),
]
# Dynamically create serializer tests to ensure that all
# registered serializers are automatically tested.
class SerializerTests(unittest.TestCase):
pass
def serializerTest(format, self):
# Clear the database first
management.flush(verbosity=0, interactive=False)
# Create all the objects defined in the test data
objects = []
transaction.enter_transaction_management()
transaction.managed(True)
for (func, pk, klass, datum) in test_data:
objects.append(func[0](pk, klass, datum))
transaction.commit()
transaction.leave_transaction_management()
# Add the generic tagged objects to the object list
objects.extend(Tag.objects.all())
# Serialize the test database
serialized_data = serializers.serialize(format, objects, indent=2)
# Flush the database and recreate from the serialized data
management.flush(verbosity=0, interactive=False)
transaction.enter_transaction_management()
transaction.managed(True)
for obj in serializers.deserialize(format, serialized_data):
obj.save()
transaction.commit()
transaction.leave_transaction_management()
# Assert that the deserialized data is the same
# as the original source
for (func, pk, klass, datum) in test_data:
func[1](self, pk, klass, datum)
for format in serializers.get_serializer_formats():
setattr(SerializerTests, 'test_'+format+'_serializer', curry(serializerTest, format))

View File

@ -401,6 +401,20 @@ class Templates(unittest.TestCase):
'ifequal-split09': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slash\man"}, "yes"),
'ifequal-split10': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slashman"}, "no"),
# NUMERIC RESOLUTION
'ifequal-numeric01': ('{% ifequal x 5 %}yes{% endifequal %}', {'x': '5'}, ''),
'ifequal-numeric02': ('{% ifequal x 5 %}yes{% endifequal %}', {'x': 5}, 'yes'),
'ifequal-numeric03': ('{% ifequal x 5.2 %}yes{% endifequal %}', {'x': 5}, ''),
'ifequal-numeric04': ('{% ifequal x 5.2 %}yes{% endifequal %}', {'x': 5.2}, 'yes'),
'ifequal-numeric05': ('{% ifequal x 0.2 %}yes{% endifequal %}', {'x': .2}, 'yes'),
'ifequal-numeric06': ('{% ifequal x .2 %}yes{% endifequal %}', {'x': .2}, 'yes'),
'ifequal-numeric07': ('{% ifequal x 2. %}yes{% endifequal %}', {'x': 2}, ''),
'ifequal-numeric08': ('{% ifequal x "5" %}yes{% endifequal %}', {'x': 5}, ''),
'ifequal-numeric09': ('{% ifequal x "5" %}yes{% endifequal %}', {'x': '5'}, 'yes'),
'ifequal-numeric10': ('{% ifequal x -5 %}yes{% endifequal %}', {'x': -5}, 'yes'),
'ifequal-numeric11': ('{% ifequal x -5.2 %}yes{% endifequal %}', {'x': -5.2}, 'yes'),
'ifequal-numeric12': ('{% ifequal x +5 %}yes{% endifequal %}', {'x': 5}, 'yes'),
### IFNOTEQUAL TAG ########################################################
'ifnotequal01': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
'ifnotequal02': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 1}, ""),