mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +00:00
gis: Merged revisions 7837-7838,7842-7852,7856-7869,7871,7876-7877,7882-7891,7900-7917 via svnmerge from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7918 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
314d19db66
commit
5bf3565a26
4
AUTHORS
4
AUTHORS
@ -52,7 +52,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
andy@jadedplanet.net
|
andy@jadedplanet.net
|
||||||
Fabrice Aneche <akh@nobugware.com>
|
Fabrice Aneche <akh@nobugware.com>
|
||||||
ant9000@netwise.it
|
ant9000@netwise.it
|
||||||
Florian Apolloner
|
Florian Apolloner <florian@apolloner.eu>
|
||||||
arien <regexbot@gmail.com>
|
arien <regexbot@gmail.com>
|
||||||
David Ascher <http://ascher.ca/>
|
David Ascher <http://ascher.ca/>
|
||||||
Jökull Sólberg Auðunsson <jokullsolberg@gmail.com>
|
Jökull Sólberg Auðunsson <jokullsolberg@gmail.com>
|
||||||
@ -280,6 +280,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Neal Norwitz <nnorwitz@google.com>
|
Neal Norwitz <nnorwitz@google.com>
|
||||||
Todd O'Bryan <toddobryan@mac.com>
|
Todd O'Bryan <toddobryan@mac.com>
|
||||||
oggie rob <oz.robharvey@gmail.com>
|
oggie rob <oz.robharvey@gmail.com>
|
||||||
|
oggy <ognjen.maric@gmail.com>
|
||||||
Jay Parlar <parlar@gmail.com>
|
Jay Parlar <parlar@gmail.com>
|
||||||
Carlos Eduardo de Paula <carlosedp@gmail.com>
|
Carlos Eduardo de Paula <carlosedp@gmail.com>
|
||||||
pavithran s <pavithran.s@gmail.com>
|
pavithran s <pavithran.s@gmail.com>
|
||||||
@ -350,6 +351,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Swaroop C H <http://www.swaroopch.info>
|
Swaroop C H <http://www.swaroopch.info>
|
||||||
Aaron Swartz <http://www.aaronsw.com/>
|
Aaron Swartz <http://www.aaronsw.com/>
|
||||||
Ville Säävuori <http://www.unessa.net/>
|
Ville Säävuori <http://www.unessa.net/>
|
||||||
|
Christian Tanzer <tanzer@swing.co.at>
|
||||||
Tyler Tarabula <tyler.tarabula@gmail.com>
|
Tyler Tarabula <tyler.tarabula@gmail.com>
|
||||||
Tyson Tate <tyson@fallingbullets.com>
|
Tyson Tate <tyson@fallingbullets.com>
|
||||||
Frank Tegtmeyer <fte@fte.to>
|
Frank Tegtmeyer <fte@fte.to>
|
||||||
|
@ -1,64 +1,11 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import optparse
|
if __name__ == "__main__":
|
||||||
import os
|
import sys
|
||||||
import sys
|
name = sys.argv[0]
|
||||||
|
args = ' '.join(sys.argv[1:])
|
||||||
try:
|
print >> sys.stderr, "%s has been moved into django-admin.py" % name
|
||||||
set
|
print >> sys.stderr, 'Please run "django-admin.py compilemessages %s" instead.'% args
|
||||||
except NameError:
|
print >> sys.stderr
|
||||||
from sets import Set as set # For Python 2.3
|
|
||||||
|
|
||||||
|
|
||||||
def compile_messages(locale=None):
|
|
||||||
basedirs = (os.path.join('conf', 'locale'), 'locale')
|
|
||||||
if os.environ.get('DJANGO_SETTINGS_MODULE'):
|
|
||||||
from django.conf import settings
|
|
||||||
basedirs += settings.LOCALE_PATHS
|
|
||||||
|
|
||||||
# Gather existing directories.
|
|
||||||
basedirs = set(map(os.path.abspath, filter(os.path.isdir, basedirs)))
|
|
||||||
|
|
||||||
if not basedirs:
|
|
||||||
print "This script should be run from the Django SVN tree or your project or app tree, or with the settings module specified."
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
for basedir in basedirs:
|
|
||||||
if locale:
|
|
||||||
basedir = os.path.join(basedir, locale, 'LC_MESSAGES')
|
|
||||||
compile_messages_in_dir(basedir)
|
|
||||||
|
|
||||||
def compile_messages_in_dir(basedir):
|
|
||||||
for dirpath, dirnames, filenames in os.walk(basedir):
|
|
||||||
for f in filenames:
|
|
||||||
if f.endswith('.po'):
|
|
||||||
sys.stderr.write('processing file %s in %s\n' % (f, dirpath))
|
|
||||||
pf = os.path.splitext(os.path.join(dirpath, f))[0]
|
|
||||||
# Store the names of the .mo and .po files in an environment
|
|
||||||
# variable, rather than doing a string replacement into the
|
|
||||||
# command, so that we can take advantage of shell quoting, to
|
|
||||||
# quote any malicious characters/escaping.
|
|
||||||
# See http://cyberelk.net/tim/articles/cmdline/ar01s02.html
|
|
||||||
os.environ['djangocompilemo'] = pf + '.mo'
|
|
||||||
os.environ['djangocompilepo'] = pf + '.po'
|
|
||||||
if sys.platform == 'win32': # Different shell-variable syntax
|
|
||||||
cmd = 'msgfmt --check-format -o "%djangocompilemo%" "%djangocompilepo%"'
|
|
||||||
else:
|
|
||||||
cmd = 'msgfmt --check-format -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.")
|
|
||||||
parser.add_option('--settings',
|
|
||||||
help='Python path to settings module, e.g. "myproject.settings". If provided, all LOCALE_PATHS will be processed. If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be checked as well.')
|
|
||||||
options, args = parser.parse_args()
|
|
||||||
if len(args):
|
|
||||||
parser.error("This program takes no arguments")
|
|
||||||
if options.settings:
|
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
|
|
||||||
compile_messages(options.locale)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
@ -7,14 +7,7 @@ Can be run as a cronjob to clean out old data from the database (only expired
|
|||||||
sessions at the moment).
|
sessions at the moment).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
from django.core import management
|
||||||
from django.db import transaction
|
|
||||||
from django.contrib.sessions.models import Session
|
|
||||||
|
|
||||||
def clean_up():
|
|
||||||
"""Clean up expired sessions."""
|
|
||||||
Session.objects.filter(expire_date__lt=datetime.datetime.now()).delete()
|
|
||||||
transaction.commit_unless_managed()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
clean_up()
|
management.call_command('cleanup')
|
||||||
|
@ -1,159 +1,11 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
# Need to ensure that the i18n framework is enabled
|
|
||||||
from django.conf import settings
|
|
||||||
settings.configure(USE_I18N = True)
|
|
||||||
|
|
||||||
from django.utils.translation import templatize
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import getopt
|
|
||||||
from itertools import dropwhile
|
|
||||||
|
|
||||||
pythonize_re = re.compile(r'\n\s*//')
|
|
||||||
|
|
||||||
def make_messages():
|
|
||||||
localedir = None
|
|
||||||
|
|
||||||
if os.path.isdir(os.path.join('conf', 'locale')):
|
|
||||||
localedir = os.path.abspath(os.path.join('conf', 'locale'))
|
|
||||||
elif os.path.isdir('locale'):
|
|
||||||
localedir = os.path.abspath('locale')
|
|
||||||
else:
|
|
||||||
print "This script should be run from the django svn tree or your project or app tree."
|
|
||||||
print "If you did indeed run it from the svn checkout or your project or application,"
|
|
||||||
print "maybe you are just missing the conf/locale (in the django tree) or locale (for project"
|
|
||||||
print "and application) directory?"
|
|
||||||
print "make-messages.py doesn't create it automatically, you have to create it by hand if"
|
|
||||||
print "you want to enable i18n for your project or application."
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
(opts, args) = getopt.getopt(sys.argv[1:], 'l:d:va')
|
|
||||||
|
|
||||||
lang = None
|
|
||||||
domain = 'django'
|
|
||||||
verbose = False
|
|
||||||
all = False
|
|
||||||
|
|
||||||
for o, v in opts:
|
|
||||||
if o == '-l':
|
|
||||||
lang = v
|
|
||||||
elif o == '-d':
|
|
||||||
domain = v
|
|
||||||
elif o == '-v':
|
|
||||||
verbose = True
|
|
||||||
elif o == '-a':
|
|
||||||
all = True
|
|
||||||
|
|
||||||
if domain not in ('django', 'djangojs'):
|
|
||||||
print "currently make-messages.py only supports domains 'django' and 'djangojs'"
|
|
||||||
sys.exit(1)
|
|
||||||
if (lang is None and not all) or domain is None:
|
|
||||||
print "usage: make-messages.py -l <language>"
|
|
||||||
print " or: make-messages.py -a"
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
languages = []
|
|
||||||
|
|
||||||
if lang is not None:
|
|
||||||
languages.append(lang)
|
|
||||||
elif all:
|
|
||||||
languages = [el for el in os.listdir(localedir) if not el.startswith('.')]
|
|
||||||
|
|
||||||
for lang in languages:
|
|
||||||
|
|
||||||
print "processing language", lang
|
|
||||||
basedir = os.path.join(localedir, lang, 'LC_MESSAGES')
|
|
||||||
if not os.path.isdir(basedir):
|
|
||||||
os.makedirs(basedir)
|
|
||||||
|
|
||||||
pofile = os.path.join(basedir, '%s.po' % domain)
|
|
||||||
potfile = os.path.join(basedir, '%s.pot' % domain)
|
|
||||||
|
|
||||||
if os.path.exists(potfile):
|
|
||||||
os.unlink(potfile)
|
|
||||||
|
|
||||||
all_files = []
|
|
||||||
for (dirpath, dirnames, filenames) in os.walk("."):
|
|
||||||
all_files.extend([(dirpath, f) for f in filenames])
|
|
||||||
all_files.sort()
|
|
||||||
for dirpath, file in all_files:
|
|
||||||
if domain == 'djangojs' and file.endswith('.js'):
|
|
||||||
if verbose: sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
|
|
||||||
src = open(os.path.join(dirpath, file), "rb").read()
|
|
||||||
src = pythonize_re.sub('\n#', src)
|
|
||||||
open(os.path.join(dirpath, '%s.py' % file), "wb").write(src)
|
|
||||||
thefile = '%s.py' % file
|
|
||||||
cmd = 'xgettext -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % (domain, os.path.join(dirpath, thefile))
|
|
||||||
(stdin, stdout, stderr) = os.popen3(cmd, 't')
|
|
||||||
msgs = stdout.read()
|
|
||||||
errors = stderr.read()
|
|
||||||
if errors:
|
|
||||||
print "errors happened while running xgettext on %s" % file
|
|
||||||
print errors
|
|
||||||
sys.exit(8)
|
|
||||||
old = '#: '+os.path.join(dirpath, thefile)[2:]
|
|
||||||
new = '#: '+os.path.join(dirpath, file)[2:]
|
|
||||||
msgs = msgs.replace(old, new)
|
|
||||||
if os.path.exists(potfile):
|
|
||||||
# Strip the header
|
|
||||||
msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
|
|
||||||
else:
|
|
||||||
msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8')
|
|
||||||
if msgs:
|
|
||||||
open(potfile, 'ab').write(msgs)
|
|
||||||
os.unlink(os.path.join(dirpath, thefile))
|
|
||||||
elif domain == 'django' and (file.endswith('.py') or file.endswith('.html')):
|
|
||||||
thefile = file
|
|
||||||
if file.endswith('.html'):
|
|
||||||
src = open(os.path.join(dirpath, file), "rb").read()
|
|
||||||
thefile = '%s.py' % file
|
|
||||||
open(os.path.join(dirpath, thefile), "wb").write(templatize(src))
|
|
||||||
if verbose:
|
|
||||||
sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
|
|
||||||
cmd = 'xgettext -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --keyword=ugettext_noop --keyword=ugettext_lazy --keyword=ungettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % (
|
|
||||||
domain, os.path.join(dirpath, thefile))
|
|
||||||
(stdin, stdout, stderr) = os.popen3(cmd, 't')
|
|
||||||
msgs = stdout.read()
|
|
||||||
errors = stderr.read()
|
|
||||||
if errors:
|
|
||||||
print "errors happened while running xgettext on %s" % file
|
|
||||||
print errors
|
|
||||||
sys.exit(8)
|
|
||||||
if thefile != file:
|
|
||||||
old = '#: '+os.path.join(dirpath, thefile)[2:]
|
|
||||||
new = '#: '+os.path.join(dirpath, file)[2:]
|
|
||||||
msgs = msgs.replace(old, new)
|
|
||||||
if os.path.exists(potfile):
|
|
||||||
# Strip the header
|
|
||||||
msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
|
|
||||||
else:
|
|
||||||
msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8')
|
|
||||||
if msgs:
|
|
||||||
open(potfile, 'ab').write(msgs)
|
|
||||||
if thefile != file:
|
|
||||||
os.unlink(os.path.join(dirpath, thefile))
|
|
||||||
|
|
||||||
if os.path.exists(potfile):
|
|
||||||
(stdin, stdout, stderr) = os.popen3('msguniq --to-code=utf-8 "%s"' % potfile, 'b')
|
|
||||||
msgs = stdout.read()
|
|
||||||
errors = stderr.read()
|
|
||||||
if errors:
|
|
||||||
print "errors happened while running msguniq"
|
|
||||||
print errors
|
|
||||||
sys.exit(8)
|
|
||||||
open(potfile, 'w').write(msgs)
|
|
||||||
if os.path.exists(pofile):
|
|
||||||
(stdin, stdout, stderr) = os.popen3('msgmerge -q "%s" "%s"' % (pofile, potfile), 'b')
|
|
||||||
msgs = stdout.read()
|
|
||||||
errors = stderr.read()
|
|
||||||
if errors:
|
|
||||||
print "errors happened while running msgmerge"
|
|
||||||
print errors
|
|
||||||
sys.exit(8)
|
|
||||||
open(pofile, 'wb').write(msgs)
|
|
||||||
os.unlink(potfile)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
make_messages()
|
import sys
|
||||||
|
name = sys.argv[0]
|
||||||
|
args = ' '.join(sys.argv[1:])
|
||||||
|
print >> sys.stderr, "%s has been moved into django-admin.py" % name
|
||||||
|
print >> sys.stderr, 'Please run "django-admin.py makemessages %s" instead.'% args
|
||||||
|
print >> sys.stderr
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
@ -71,6 +71,13 @@ class LazySettings(object):
|
|||||||
setattr(holder, name, value)
|
setattr(holder, name, value)
|
||||||
self._target = holder
|
self._target = holder
|
||||||
|
|
||||||
|
def configured(self):
|
||||||
|
"""
|
||||||
|
Returns True if the settings have already been configured.
|
||||||
|
"""
|
||||||
|
return bool(self._target)
|
||||||
|
configured = property(configured)
|
||||||
|
|
||||||
class Settings(object):
|
class Settings(object):
|
||||||
def __init__(self, settings_module):
|
def __init__(self, settings_module):
|
||||||
# update this dict from global settings (but only for ALL_CAPS settings)
|
# update this dict from global settings (but only for ALL_CAPS settings)
|
||||||
|
Binary file not shown.
@ -6,8 +6,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Django vSVN\n"
|
"Project-Id-Version: Django vSVN\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2008-06-02 18:40+0200\n"
|
"POT-Creation-Date: 2008-07-12 20:30+0200\n"
|
||||||
"PO-Revision-Date: 2008-05-18 19:13+0200\n"
|
"PO-Revision-Date: 2008-07-12 20:45+0200\n"
|
||||||
"Last-Translator: Nicola Larosa <nico@tekNico.net>\n"
|
"Last-Translator: Nicola Larosa <nico@tekNico.net>\n"
|
||||||
"Language-Team: Italiano\n"
|
"Language-Team: Italiano\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -16,191 +16,199 @@ msgstr ""
|
|||||||
"X-Generator: KBabel 1.11.4\n"
|
"X-Generator: KBabel 1.11.4\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: conf/global_settings.py:43
|
#: conf/global_settings.py:44
|
||||||
msgid "Arabic"
|
msgid "Arabic"
|
||||||
msgstr "Arabo"
|
msgstr "Arabo"
|
||||||
|
|
||||||
#: conf/global_settings.py:44
|
#: conf/global_settings.py:45
|
||||||
msgid "Bengali"
|
msgid "Bengali"
|
||||||
msgstr "Bengali"
|
msgstr "Bengali"
|
||||||
|
|
||||||
#: conf/global_settings.py:45
|
#: conf/global_settings.py:46
|
||||||
msgid "Bulgarian"
|
msgid "Bulgarian"
|
||||||
msgstr "Bulgaro"
|
msgstr "Bulgaro"
|
||||||
|
|
||||||
#: conf/global_settings.py:46
|
#: conf/global_settings.py:47
|
||||||
msgid "Catalan"
|
msgid "Catalan"
|
||||||
msgstr "Catalano"
|
msgstr "Catalano"
|
||||||
|
|
||||||
#: conf/global_settings.py:47
|
#: conf/global_settings.py:48
|
||||||
msgid "Czech"
|
msgid "Czech"
|
||||||
msgstr "Ceco"
|
msgstr "Ceco"
|
||||||
|
|
||||||
#: conf/global_settings.py:48
|
#: conf/global_settings.py:49
|
||||||
msgid "Welsh"
|
msgid "Welsh"
|
||||||
msgstr "Gallese"
|
msgstr "Gallese"
|
||||||
|
|
||||||
#: conf/global_settings.py:49
|
#: conf/global_settings.py:50
|
||||||
msgid "Danish"
|
msgid "Danish"
|
||||||
msgstr "Danese"
|
msgstr "Danese"
|
||||||
|
|
||||||
#: conf/global_settings.py:50
|
#: conf/global_settings.py:51
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Tedesco"
|
msgstr "Tedesco"
|
||||||
|
|
||||||
#: conf/global_settings.py:51
|
#: conf/global_settings.py:52
|
||||||
msgid "Greek"
|
msgid "Greek"
|
||||||
msgstr "Greco"
|
msgstr "Greco"
|
||||||
|
|
||||||
#: conf/global_settings.py:52
|
#: conf/global_settings.py:53
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Inglese"
|
msgstr "Inglese"
|
||||||
|
|
||||||
#: conf/global_settings.py:53
|
#: conf/global_settings.py:54
|
||||||
msgid "Spanish"
|
msgid "Spanish"
|
||||||
msgstr "Spagnolo"
|
msgstr "Spagnolo"
|
||||||
|
|
||||||
#: conf/global_settings.py:54
|
#: conf/global_settings.py:55
|
||||||
|
msgid "Estonian"
|
||||||
|
msgstr "Estone"
|
||||||
|
|
||||||
|
#: conf/global_settings.py:56
|
||||||
msgid "Argentinean Spanish"
|
msgid "Argentinean Spanish"
|
||||||
msgstr "Spagnolo argentino"
|
msgstr "Spagnolo argentino"
|
||||||
|
|
||||||
#: conf/global_settings.py:55
|
#: conf/global_settings.py:57
|
||||||
msgid "Basque"
|
msgid "Basque"
|
||||||
msgstr "Basco"
|
msgstr "Basco"
|
||||||
|
|
||||||
#: conf/global_settings.py:56
|
#: conf/global_settings.py:58
|
||||||
msgid "Persian"
|
msgid "Persian"
|
||||||
msgstr "Persiano"
|
msgstr "Persiano"
|
||||||
|
|
||||||
#: conf/global_settings.py:57
|
#: conf/global_settings.py:59
|
||||||
msgid "Finnish"
|
msgid "Finnish"
|
||||||
msgstr "Finlandese"
|
msgstr "Finlandese"
|
||||||
|
|
||||||
#: conf/global_settings.py:58
|
#: conf/global_settings.py:60
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr "Francese"
|
msgstr "Francese"
|
||||||
|
|
||||||
#: conf/global_settings.py:59
|
#: conf/global_settings.py:61
|
||||||
msgid "Irish"
|
msgid "Irish"
|
||||||
msgstr "Irlandese"
|
msgstr "Irlandese"
|
||||||
|
|
||||||
#: conf/global_settings.py:60
|
#: conf/global_settings.py:62
|
||||||
msgid "Galician"
|
msgid "Galician"
|
||||||
msgstr "Galiziano"
|
msgstr "Galiziano"
|
||||||
|
|
||||||
#: conf/global_settings.py:61
|
#: conf/global_settings.py:63
|
||||||
msgid "Hungarian"
|
msgid "Hungarian"
|
||||||
msgstr "Ungherese"
|
msgstr "Ungherese"
|
||||||
|
|
||||||
#: conf/global_settings.py:62
|
#: conf/global_settings.py:64
|
||||||
msgid "Hebrew"
|
msgid "Hebrew"
|
||||||
msgstr "Ebraico"
|
msgstr "Ebraico"
|
||||||
|
|
||||||
#: conf/global_settings.py:63
|
#: conf/global_settings.py:65
|
||||||
msgid "Croatian"
|
msgid "Croatian"
|
||||||
msgstr "Croato"
|
msgstr "Croato"
|
||||||
|
|
||||||
#: conf/global_settings.py:64
|
#: conf/global_settings.py:66
|
||||||
msgid "Icelandic"
|
msgid "Icelandic"
|
||||||
msgstr "Islandese"
|
msgstr "Islandese"
|
||||||
|
|
||||||
#: conf/global_settings.py:65
|
#: conf/global_settings.py:67
|
||||||
msgid "Italian"
|
msgid "Italian"
|
||||||
msgstr "Italiano"
|
msgstr "Italiano"
|
||||||
|
|
||||||
#: conf/global_settings.py:66
|
#: conf/global_settings.py:68
|
||||||
msgid "Japanese"
|
msgid "Japanese"
|
||||||
msgstr "Giapponese"
|
msgstr "Giapponese"
|
||||||
|
|
||||||
#: conf/global_settings.py:67
|
#: conf/global_settings.py:69
|
||||||
msgid "Georgian"
|
msgid "Georgian"
|
||||||
msgstr "Georgiano"
|
msgstr "Georgiano"
|
||||||
|
|
||||||
#: conf/global_settings.py:68
|
#: conf/global_settings.py:70
|
||||||
msgid "Korean"
|
msgid "Korean"
|
||||||
msgstr "Coreano"
|
msgstr "Coreano"
|
||||||
|
|
||||||
#: conf/global_settings.py:69
|
#: conf/global_settings.py:71
|
||||||
msgid "Khmer"
|
msgid "Khmer"
|
||||||
msgstr "Khmer"
|
msgstr "Khmer"
|
||||||
|
|
||||||
#: conf/global_settings.py:70
|
#: conf/global_settings.py:72
|
||||||
msgid "Kannada"
|
msgid "Kannada"
|
||||||
msgstr "Kannada"
|
msgstr "Kannada"
|
||||||
|
|
||||||
#: conf/global_settings.py:71
|
#: conf/global_settings.py:73
|
||||||
msgid "Latvian"
|
msgid "Latvian"
|
||||||
msgstr "Lettone"
|
msgstr "Lettone"
|
||||||
|
|
||||||
#: conf/global_settings.py:72
|
#: conf/global_settings.py:74
|
||||||
|
msgid "Lithuanian"
|
||||||
|
msgstr "Lituano"
|
||||||
|
|
||||||
|
#: conf/global_settings.py:75
|
||||||
msgid "Macedonian"
|
msgid "Macedonian"
|
||||||
msgstr "Macedone"
|
msgstr "Macedone"
|
||||||
|
|
||||||
#: conf/global_settings.py:73
|
#: conf/global_settings.py:76
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr "Olandese"
|
msgstr "Olandese"
|
||||||
|
|
||||||
#: conf/global_settings.py:74
|
#: conf/global_settings.py:77
|
||||||
msgid "Norwegian"
|
msgid "Norwegian"
|
||||||
msgstr "Norvegese"
|
msgstr "Norvegese"
|
||||||
|
|
||||||
#: conf/global_settings.py:75
|
#: conf/global_settings.py:78
|
||||||
msgid "Polish"
|
msgid "Polish"
|
||||||
msgstr "Polacco"
|
msgstr "Polacco"
|
||||||
|
|
||||||
#: conf/global_settings.py:76
|
#: conf/global_settings.py:79
|
||||||
msgid "Portugese"
|
msgid "Portugese"
|
||||||
msgstr "Portoghese"
|
msgstr "Portoghese"
|
||||||
|
|
||||||
#: conf/global_settings.py:77
|
#: conf/global_settings.py:80
|
||||||
msgid "Brazilian Portuguese"
|
msgid "Brazilian Portuguese"
|
||||||
msgstr "Brasiliano Portoghese"
|
msgstr "Brasiliano Portoghese"
|
||||||
|
|
||||||
#: conf/global_settings.py:78
|
#: conf/global_settings.py:81
|
||||||
msgid "Romanian"
|
msgid "Romanian"
|
||||||
msgstr "Rumeno"
|
msgstr "Rumeno"
|
||||||
|
|
||||||
#: conf/global_settings.py:79
|
#: conf/global_settings.py:82
|
||||||
msgid "Russian"
|
msgid "Russian"
|
||||||
msgstr "Russo"
|
msgstr "Russo"
|
||||||
|
|
||||||
#: conf/global_settings.py:80
|
#: conf/global_settings.py:83
|
||||||
msgid "Slovak"
|
msgid "Slovak"
|
||||||
msgstr "Slovacco"
|
msgstr "Slovacco"
|
||||||
|
|
||||||
#: conf/global_settings.py:81
|
#: conf/global_settings.py:84
|
||||||
msgid "Slovenian"
|
msgid "Slovenian"
|
||||||
msgstr "Sloveno"
|
msgstr "Sloveno"
|
||||||
|
|
||||||
#: conf/global_settings.py:82
|
#: conf/global_settings.py:85
|
||||||
msgid "Serbian"
|
msgid "Serbian"
|
||||||
msgstr "Serbo"
|
msgstr "Serbo"
|
||||||
|
|
||||||
#: conf/global_settings.py:83
|
#: conf/global_settings.py:86
|
||||||
msgid "Swedish"
|
msgid "Swedish"
|
||||||
msgstr "Svedese"
|
msgstr "Svedese"
|
||||||
|
|
||||||
#: conf/global_settings.py:84
|
#: conf/global_settings.py:87
|
||||||
msgid "Tamil"
|
msgid "Tamil"
|
||||||
msgstr "Tamil"
|
msgstr "Tamil"
|
||||||
|
|
||||||
#: conf/global_settings.py:85
|
#: conf/global_settings.py:88
|
||||||
msgid "Telugu"
|
msgid "Telugu"
|
||||||
msgstr "Telugu"
|
msgstr "Telugu"
|
||||||
|
|
||||||
#: conf/global_settings.py:86
|
#: conf/global_settings.py:89
|
||||||
msgid "Turkish"
|
msgid "Turkish"
|
||||||
msgstr "Turco"
|
msgstr "Turco"
|
||||||
|
|
||||||
#: conf/global_settings.py:87
|
#: conf/global_settings.py:90
|
||||||
msgid "Ukrainian"
|
msgid "Ukrainian"
|
||||||
msgstr "Ucraino"
|
msgstr "Ucraino"
|
||||||
|
|
||||||
#: conf/global_settings.py:88
|
#: conf/global_settings.py:91
|
||||||
msgid "Simplified Chinese"
|
msgid "Simplified Chinese"
|
||||||
msgstr "Cinese semplificato"
|
msgstr "Cinese semplificato"
|
||||||
|
|
||||||
#: conf/global_settings.py:89
|
#: conf/global_settings.py:92
|
||||||
msgid "Traditional Chinese"
|
msgid "Traditional Chinese"
|
||||||
msgstr "Cinese tradizionale"
|
msgstr "Cinese tradizionale"
|
||||||
|
|
||||||
@ -1133,15 +1141,15 @@ msgstr "permessi"
|
|||||||
msgid "group"
|
msgid "group"
|
||||||
msgstr "gruppo"
|
msgstr "gruppo"
|
||||||
|
|
||||||
#: contrib/auth/models.py:98 contrib/auth/models.py:141
|
#: contrib/auth/models.py:98 contrib/auth/models.py:148
|
||||||
msgid "groups"
|
msgid "groups"
|
||||||
msgstr "gruppi"
|
msgstr "gruppi"
|
||||||
|
|
||||||
#: contrib/auth/models.py:131
|
#: contrib/auth/models.py:138
|
||||||
msgid "username"
|
msgid "username"
|
||||||
msgstr "nome utente"
|
msgstr "nome utente"
|
||||||
|
|
||||||
#: contrib/auth/models.py:131
|
#: contrib/auth/models.py:138
|
||||||
msgid ""
|
msgid ""
|
||||||
"Required. 30 characters or fewer. Alphanumeric characters only (letters, "
|
"Required. 30 characters or fewer. Alphanumeric characters only (letters, "
|
||||||
"digits and underscores)."
|
"digits and underscores)."
|
||||||
@ -1149,23 +1157,23 @@ msgstr ""
|
|||||||
"Obbligatorio. 30 caratteri o meno. Solo caratteri alfanumerici (lettere, "
|
"Obbligatorio. 30 caratteri o meno. Solo caratteri alfanumerici (lettere, "
|
||||||
"cifre e sottolineati)."
|
"cifre e sottolineati)."
|
||||||
|
|
||||||
#: contrib/auth/models.py:132
|
#: contrib/auth/models.py:139
|
||||||
msgid "first name"
|
msgid "first name"
|
||||||
msgstr "nome"
|
msgstr "nome"
|
||||||
|
|
||||||
#: contrib/auth/models.py:133
|
#: contrib/auth/models.py:140
|
||||||
msgid "last name"
|
msgid "last name"
|
||||||
msgstr "cognome"
|
msgstr "cognome"
|
||||||
|
|
||||||
#: contrib/auth/models.py:134
|
#: contrib/auth/models.py:141
|
||||||
msgid "e-mail address"
|
msgid "e-mail address"
|
||||||
msgstr "indirizzo e-mail"
|
msgstr "indirizzo e-mail"
|
||||||
|
|
||||||
#: contrib/auth/models.py:135
|
#: contrib/auth/models.py:142
|
||||||
msgid "password"
|
msgid "password"
|
||||||
msgstr "password"
|
msgstr "password"
|
||||||
|
|
||||||
#: contrib/auth/models.py:135
|
#: contrib/auth/models.py:142
|
||||||
msgid ""
|
msgid ""
|
||||||
"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
|
"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
|
||||||
"password form</a>."
|
"password form</a>."
|
||||||
@ -1173,19 +1181,19 @@ msgstr ""
|
|||||||
"Usare '[algo]$[salt]$[hexdigest]' oppure la <a href=\"password/\">maschera "
|
"Usare '[algo]$[salt]$[hexdigest]' oppure la <a href=\"password/\">maschera "
|
||||||
"di cambio password</a>."
|
"di cambio password</a>."
|
||||||
|
|
||||||
#: contrib/auth/models.py:136
|
#: contrib/auth/models.py:143
|
||||||
msgid "staff status"
|
msgid "staff status"
|
||||||
msgstr "privilegi di staff"
|
msgstr "privilegi di staff"
|
||||||
|
|
||||||
#: contrib/auth/models.py:136
|
#: contrib/auth/models.py:143
|
||||||
msgid "Designates whether the user can log into this admin site."
|
msgid "Designates whether the user can log into this admin site."
|
||||||
msgstr "Indica se l'utente può accedere a questo sito di amministrazione."
|
msgstr "Indica se l'utente può accedere a questo sito di amministrazione."
|
||||||
|
|
||||||
#: contrib/auth/models.py:137
|
#: contrib/auth/models.py:144
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr "attivo"
|
msgstr "attivo"
|
||||||
|
|
||||||
#: contrib/auth/models.py:137
|
#: contrib/auth/models.py:144
|
||||||
msgid ""
|
msgid ""
|
||||||
"Designates whether this user should be treated as active. Unselect this "
|
"Designates whether this user should be treated as active. Unselect this "
|
||||||
"instead of deleting accounts."
|
"instead of deleting accounts."
|
||||||
@ -1193,11 +1201,11 @@ msgstr ""
|
|||||||
"Indica se l'utente debba essere considerato attivo. Deselezionare "
|
"Indica se l'utente debba essere considerato attivo. Deselezionare "
|
||||||
"qui, piuttosto che cancellare gli account."
|
"qui, piuttosto che cancellare gli account."
|
||||||
|
|
||||||
#: contrib/auth/models.py:138
|
#: contrib/auth/models.py:145
|
||||||
msgid "superuser status"
|
msgid "superuser status"
|
||||||
msgstr "privilegi di superutente"
|
msgstr "privilegi di superutente"
|
||||||
|
|
||||||
#: contrib/auth/models.py:138
|
#: contrib/auth/models.py:145
|
||||||
msgid ""
|
msgid ""
|
||||||
"Designates that this user has all permissions without explicitly assigning "
|
"Designates that this user has all permissions without explicitly assigning "
|
||||||
"them."
|
"them."
|
||||||
@ -1205,15 +1213,15 @@ msgstr ""
|
|||||||
"Indica che l'utente ha tutti i privilegi, senza che siano stati assegnati "
|
"Indica che l'utente ha tutti i privilegi, senza che siano stati assegnati "
|
||||||
"esplicitamente."
|
"esplicitamente."
|
||||||
|
|
||||||
#: contrib/auth/models.py:139
|
#: contrib/auth/models.py:146
|
||||||
msgid "last login"
|
msgid "last login"
|
||||||
msgstr "ultimo accesso"
|
msgstr "ultimo accesso"
|
||||||
|
|
||||||
#: contrib/auth/models.py:140
|
#: contrib/auth/models.py:147
|
||||||
msgid "date joined"
|
msgid "date joined"
|
||||||
msgstr "iscritto in data"
|
msgstr "iscritto in data"
|
||||||
|
|
||||||
#: contrib/auth/models.py:142
|
#: contrib/auth/models.py:149
|
||||||
msgid ""
|
msgid ""
|
||||||
"In addition to the permissions manually assigned, this user will also get "
|
"In addition to the permissions manually assigned, this user will also get "
|
||||||
"all permissions granted to each group he/she is in."
|
"all permissions granted to each group he/she is in."
|
||||||
@ -1221,39 +1229,39 @@ msgstr ""
|
|||||||
"In aggiunta ai privilegi assegnati manualmente, l'utente riceverà anche "
|
"In aggiunta ai privilegi assegnati manualmente, l'utente riceverà anche "
|
||||||
"tutti i privilegi assegnati ad ogni gruppo cui appartiene."
|
"tutti i privilegi assegnati ad ogni gruppo cui appartiene."
|
||||||
|
|
||||||
#: contrib/auth/models.py:143
|
#: contrib/auth/models.py:150
|
||||||
msgid "user permissions"
|
msgid "user permissions"
|
||||||
msgstr "privilegi utente"
|
msgstr "privilegi utente"
|
||||||
|
|
||||||
#: contrib/auth/models.py:147
|
#: contrib/auth/models.py:154
|
||||||
msgid "user"
|
msgid "user"
|
||||||
msgstr "utente"
|
msgstr "utente"
|
||||||
|
|
||||||
#: contrib/auth/models.py:148
|
#: contrib/auth/models.py:155
|
||||||
msgid "users"
|
msgid "users"
|
||||||
msgstr "utenti"
|
msgstr "utenti"
|
||||||
|
|
||||||
#: contrib/auth/models.py:154
|
#: contrib/auth/models.py:160
|
||||||
msgid "Personal info"
|
msgid "Personal info"
|
||||||
msgstr "Informazioni personali"
|
msgstr "Informazioni personali"
|
||||||
|
|
||||||
#: contrib/auth/models.py:155
|
#: contrib/auth/models.py:161
|
||||||
msgid "Permissions"
|
msgid "Permissions"
|
||||||
msgstr "Permessi"
|
msgstr "Permessi"
|
||||||
|
|
||||||
#: contrib/auth/models.py:156
|
#: contrib/auth/models.py:162
|
||||||
msgid "Important dates"
|
msgid "Important dates"
|
||||||
msgstr "Date importanti"
|
msgstr "Date importanti"
|
||||||
|
|
||||||
#: contrib/auth/models.py:157
|
#: contrib/auth/models.py:163
|
||||||
msgid "Groups"
|
msgid "Groups"
|
||||||
msgstr "Gruppi"
|
msgstr "Gruppi"
|
||||||
|
|
||||||
#: contrib/auth/models.py:316
|
#: contrib/auth/models.py:323
|
||||||
msgid "message"
|
msgid "message"
|
||||||
msgstr "messaggio"
|
msgstr "messaggio"
|
||||||
|
|
||||||
#: contrib/auth/views.py:47
|
#: contrib/auth/views.py:49
|
||||||
msgid "Logged out"
|
msgid "Logged out"
|
||||||
msgstr "Accesso annullato"
|
msgstr "Accesso annullato"
|
||||||
|
|
||||||
@ -3523,23 +3531,23 @@ msgstr "redirezione"
|
|||||||
msgid "redirects"
|
msgid "redirects"
|
||||||
msgstr "redirezioni"
|
msgstr "redirezioni"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:41
|
#: contrib/sessions/models.py:45
|
||||||
msgid "session key"
|
msgid "session key"
|
||||||
msgstr "chiave di sessione"
|
msgstr "chiave di sessione"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:42
|
#: contrib/sessions/models.py:47
|
||||||
msgid "session data"
|
msgid "session data"
|
||||||
msgstr "dati di sessione"
|
msgstr "dati di sessione"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:43
|
#: contrib/sessions/models.py:48
|
||||||
msgid "expire date"
|
msgid "expire date"
|
||||||
msgstr "data di scadenza"
|
msgstr "data di scadenza"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:48
|
#: contrib/sessions/models.py:53
|
||||||
msgid "session"
|
msgid "session"
|
||||||
msgstr "sessione"
|
msgstr "sessione"
|
||||||
|
|
||||||
#: contrib/sessions/models.py:49
|
#: contrib/sessions/models.py:54
|
||||||
msgid "sessions"
|
msgid "sessions"
|
||||||
msgstr "sessioni"
|
msgstr "sessioni"
|
||||||
|
|
||||||
@ -3607,7 +3615,7 @@ msgstr "Sono ammessi soltanto caratteri numerici."
|
|||||||
msgid "This value can't be comprised solely of digits."
|
msgid "This value can't be comprised solely of digits."
|
||||||
msgstr "Questo valore non può essere composto solo da cifre."
|
msgstr "Questo valore non può essere composto solo da cifre."
|
||||||
|
|
||||||
#: core/validators.py:128 newforms/fields.py:152
|
#: core/validators.py:128 newforms/fields.py:157
|
||||||
msgid "Enter a whole number."
|
msgid "Enter a whole number."
|
||||||
msgstr "Inserire un numero intero."
|
msgstr "Inserire un numero intero."
|
||||||
|
|
||||||
@ -3624,7 +3632,7 @@ msgstr "L'anno deve essere 1900 o successivo."
|
|||||||
msgid "Invalid date: %s"
|
msgid "Invalid date: %s"
|
||||||
msgstr "Data non valida: %s"
|
msgstr "Data non valida: %s"
|
||||||
|
|
||||||
#: core/validators.py:156 db/models/fields/__init__.py:548
|
#: core/validators.py:156 db/models/fields/__init__.py:554
|
||||||
msgid "Enter a valid date in YYYY-MM-DD format."
|
msgid "Enter a valid date in YYYY-MM-DD format."
|
||||||
msgstr "Inserire una data valida in formato AAAA-MM-GG."
|
msgstr "Inserire una data valida in formato AAAA-MM-GG."
|
||||||
|
|
||||||
@ -3632,20 +3640,19 @@ msgstr "Inserire una data valida in formato AAAA-MM-GG."
|
|||||||
msgid "Enter a valid time in HH:MM format."
|
msgid "Enter a valid time in HH:MM format."
|
||||||
msgstr "Inserire un ora valida in formato OO:MM."
|
msgstr "Inserire un ora valida in formato OO:MM."
|
||||||
|
|
||||||
#: core/validators.py:165 db/models/fields/__init__.py:625
|
#: core/validators.py:165 db/models/fields/__init__.py:631
|
||||||
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
|
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
|
||||||
msgstr "Inserire una data/ora valida in formato AAAA-MM-GG OO:MM."
|
msgstr "Inserire una data/ora valida in formato AAAA-MM-GG OO:MM."
|
||||||
|
|
||||||
#: core/validators.py:170 newforms/fields.py:403
|
#: core/validators.py:170 newforms/fields.py:408
|
||||||
msgid "Enter a valid e-mail address."
|
msgid "Enter a valid e-mail address."
|
||||||
msgstr "Inserire un indirizzo e-mail valido."
|
msgstr "Inserire un indirizzo e-mail valido."
|
||||||
|
|
||||||
#: core/validators.py:182 core/validators.py:474 newforms/fields.py:433
|
#: core/validators.py:182 core/validators.py:474 newforms/fields.py:426
|
||||||
#: oldforms/__init__.py:687
|
|
||||||
msgid "No file was submitted. Check the encoding type on the form."
|
msgid "No file was submitted. Check the encoding type on the form."
|
||||||
msgstr "Non è stato inviato alcun file. Verificare il tipo di codifica della form."
|
msgstr "Non è stato inviato alcun file. Verificare il tipo di codifica della form."
|
||||||
|
|
||||||
#: core/validators.py:193 newforms/fields.py:459
|
#: core/validators.py:193 newforms/fields.py:468
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||||
"corrupted image."
|
"corrupted image."
|
||||||
@ -3876,38 +3883,38 @@ msgstr ""
|
|||||||
msgid "%(object)s with this %(type)s already exists for the given %(field)s."
|
msgid "%(object)s with this %(type)s already exists for the given %(field)s."
|
||||||
msgstr "Un %(object)s·con questo·%(type)s·esiste già per questo·%(field)s."
|
msgstr "Un %(object)s·con questo·%(type)s·esiste già per questo·%(field)s."
|
||||||
|
|
||||||
#: db/models/fields/__init__.py:54
|
#: db/models/fields/__init__.py:52
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(optname)s with this %(fieldname)s already exists."
|
msgid "%(optname)s with this %(fieldname)s already exists."
|
||||||
msgstr "Un %(optname)s·con questo·%(fieldname)s·esiste già."
|
msgstr "Un %(optname)s·con questo·%(fieldname)s·esiste già."
|
||||||
|
|
||||||
#: db/models/fields/__init__.py:179 db/models/fields/__init__.py:348
|
#: db/models/fields/__init__.py:182 db/models/fields/__init__.py:354
|
||||||
#: db/models/fields/__init__.py:780 db/models/fields/__init__.py:791
|
#: db/models/fields/__init__.py:788 db/models/fields/__init__.py:799
|
||||||
#: newforms/fields.py:46 oldforms/__init__.py:374
|
#: newforms/fields.py:51 oldforms/__init__.py:374
|
||||||
msgid "This field is required."
|
msgid "This field is required."
|
||||||
msgstr "Questo campo è obbligatorio."
|
msgstr "Questo campo è obbligatorio."
|
||||||
|
|
||||||
#: db/models/fields/__init__.py:448
|
#: db/models/fields/__init__.py:454
|
||||||
msgid "This value must be an integer."
|
msgid "This value must be an integer."
|
||||||
msgstr "Questo valore deve essere un intero."
|
msgstr "Questo valore deve essere un intero."
|
||||||
|
|
||||||
#: db/models/fields/__init__.py:487
|
#: db/models/fields/__init__.py:493
|
||||||
msgid "This value must be either True or False."
|
msgid "This value must be either True or False."
|
||||||
msgstr "Questo valore deve essere True o False."
|
msgstr "Questo valore deve essere True o False."
|
||||||
|
|
||||||
#: db/models/fields/__init__.py:511
|
#: db/models/fields/__init__.py:517
|
||||||
msgid "This field cannot be null."
|
msgid "This field cannot be null."
|
||||||
msgstr "Questo campo non può essere nullo."
|
msgstr "Questo campo non può essere nullo."
|
||||||
|
|
||||||
#: db/models/fields/__init__.py:689
|
#: db/models/fields/__init__.py:695
|
||||||
msgid "This value must be a decimal number."
|
msgid "This value must be a decimal number."
|
||||||
msgstr "Questo valore deve essere un numero decimale."
|
msgstr "Questo valore deve essere un numero decimale."
|
||||||
|
|
||||||
#: db/models/fields/__init__.py:800
|
#: db/models/fields/__init__.py:808
|
||||||
msgid "Enter a valid filename."
|
msgid "Enter a valid filename."
|
||||||
msgstr "Inserire un nome di file valido."
|
msgstr "Inserire un nome di file valido."
|
||||||
|
|
||||||
#: db/models/fields/__init__.py:981
|
#: db/models/fields/__init__.py:999
|
||||||
msgid "This value must be either None, True or False."
|
msgid "This value must be either None, True or False."
|
||||||
msgstr "Questo valore deve essere None, True o False."
|
msgstr "Questo valore deve essere None, True o False."
|
||||||
|
|
||||||
@ -3916,118 +3923,118 @@ msgstr "Questo valore deve essere None, True o False."
|
|||||||
msgid "Please enter a valid %s."
|
msgid "Please enter a valid %s."
|
||||||
msgstr "Inserire un %s valido."
|
msgstr "Inserire un %s valido."
|
||||||
|
|
||||||
#: db/models/fields/related.py:721
|
#: db/models/fields/related.py:756
|
||||||
msgid "Separate multiple IDs with commas."
|
msgid "Separate multiple IDs with commas."
|
||||||
msgstr "Separare gli ID multipli con virgole."
|
msgstr "Separare gli ID multipli con virgole."
|
||||||
|
|
||||||
#: db/models/fields/related.py:723
|
#: db/models/fields/related.py:758
|
||||||
msgid "Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
msgid "Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Tenere premuto \"Control\", o \"Command\" su Mac, per selezionarne più di "
|
"Tenere premuto \"Control\", o \"Command\" su Mac, per selezionarne più di "
|
||||||
"uno."
|
"uno."
|
||||||
|
|
||||||
#: db/models/fields/related.py:770
|
#: db/models/fields/related.py:805
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
|
msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
|
||||||
msgid_plural "Please enter valid %(self)s IDs. The values %(value)r are invalid."
|
msgid_plural "Please enter valid %(self)s IDs. The values %(value)r are invalid."
|
||||||
msgstr[0] "Inserire un ID valido per %(self)s. Il valore %(value)r non è valido."
|
msgstr[0] "Inserire un ID valido per %(self)s. Il valore %(value)r non è valido."
|
||||||
msgstr[1] "Inserire ID validi per %(self)s. I valori %(value)r non sono validi."
|
msgstr[1] "Inserire ID validi per %(self)s. I valori %(value)r non sono validi."
|
||||||
|
|
||||||
#: newforms/fields.py:47
|
#: newforms/fields.py:52
|
||||||
msgid "Enter a valid value."
|
msgid "Enter a valid value."
|
||||||
msgstr "Inserire un valore valido."
|
msgstr "Inserire un valore valido."
|
||||||
|
|
||||||
#: newforms/fields.py:124
|
#: newforms/fields.py:129
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
|
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Assicurarsi che questo valore non contenga più di %(max)d caratteri (ne ha %"
|
"Assicurarsi che questo valore non contenga più di %(max)d caratteri (ne ha %"
|
||||||
"(length)d)."
|
"(length)d)."
|
||||||
|
|
||||||
#: newforms/fields.py:125
|
#: newforms/fields.py:130
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
|
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Assicurarsi che questo valore contenga almeno %(min)d caratteri (ne ha %"
|
"Assicurarsi che questo valore contenga almeno %(min)d caratteri (ne ha %"
|
||||||
"(length)d)."
|
"(length)d)."
|
||||||
|
|
||||||
#: newforms/fields.py:153 newforms/fields.py:182 newforms/fields.py:211
|
#: newforms/fields.py:158 newforms/fields.py:187 newforms/fields.py:216
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure this value is less than or equal to %s."
|
msgid "Ensure this value is less than or equal to %s."
|
||||||
msgstr "Assicurarsi che questo valore sia minore o uguale a %s."
|
msgstr "Assicurarsi che questo valore sia minore o uguale a %s."
|
||||||
|
|
||||||
#: newforms/fields.py:154 newforms/fields.py:183 newforms/fields.py:212
|
#: newforms/fields.py:159 newforms/fields.py:188 newforms/fields.py:217
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure this value is greater than or equal to %s."
|
msgid "Ensure this value is greater than or equal to %s."
|
||||||
msgstr "Assicurarsi che questo valore sia maggiore o uguale a %s."
|
msgstr "Assicurarsi che questo valore sia maggiore o uguale a %s."
|
||||||
|
|
||||||
#: newforms/fields.py:181 newforms/fields.py:210
|
#: newforms/fields.py:186 newforms/fields.py:215
|
||||||
msgid "Enter a number."
|
msgid "Enter a number."
|
||||||
msgstr "Inserire un numero."
|
msgstr "Inserire un numero."
|
||||||
|
|
||||||
#: newforms/fields.py:213
|
#: newforms/fields.py:218
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure that there are no more than %s digits in total."
|
msgid "Ensure that there are no more than %s digits in total."
|
||||||
msgstr "Assicurarsi che non vi siano più di %s cifre in totale."
|
msgstr "Assicurarsi che non vi siano più di %s cifre in totale."
|
||||||
|
|
||||||
#: newforms/fields.py:214
|
#: newforms/fields.py:219
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure that there are no more than %s decimal places."
|
msgid "Ensure that there are no more than %s decimal places."
|
||||||
msgstr "Assicurarsi che non vi siano più di %s cifre decimali."
|
msgstr "Assicurarsi che non vi siano più di %s cifre decimali."
|
||||||
|
|
||||||
#: newforms/fields.py:215
|
#: newforms/fields.py:220
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Ensure that there are no more than %s digits before the decimal point."
|
msgid "Ensure that there are no more than %s digits before the decimal point."
|
||||||
msgstr "Assicurarsi che non vi siano più di %s cifre prima della virgola."
|
msgstr "Assicurarsi che non vi siano più di %s cifre prima della virgola."
|
||||||
|
|
||||||
#: newforms/fields.py:263 newforms/fields.py:751
|
#: newforms/fields.py:268 newforms/fields.py:779
|
||||||
msgid "Enter a valid date."
|
msgid "Enter a valid date."
|
||||||
msgstr "Inserire una data valida."
|
msgstr "Inserire una data valida."
|
||||||
|
|
||||||
#: newforms/fields.py:296 newforms/fields.py:752
|
#: newforms/fields.py:301 newforms/fields.py:780
|
||||||
msgid "Enter a valid time."
|
msgid "Enter a valid time."
|
||||||
msgstr "Inserire un ora valida."
|
msgstr "Inserire un ora valida."
|
||||||
|
|
||||||
#: newforms/fields.py:335
|
#: newforms/fields.py:340
|
||||||
msgid "Enter a valid date/time."
|
msgid "Enter a valid date/time."
|
||||||
msgstr "Inserire una coppia data/ora valida."
|
msgstr "Inserire una coppia data/ora valida."
|
||||||
|
|
||||||
#: newforms/fields.py:434
|
#: newforms/fields.py:427
|
||||||
msgid "No file was submitted."
|
msgid "No file was submitted."
|
||||||
msgstr "Nessun file è stato inviato."
|
msgstr "Nessun file è stato inviato."
|
||||||
|
|
||||||
#: newforms/fields.py:435 oldforms/__init__.py:689
|
#: newforms/fields.py:428 oldforms/__init__.py:693
|
||||||
msgid "The submitted file is empty."
|
msgid "The submitted file is empty."
|
||||||
msgstr "Il file inviato è vuoto."
|
msgstr "Il file inviato è vuoto."
|
||||||
|
|
||||||
#: newforms/fields.py:497
|
#: newforms/fields.py:522
|
||||||
msgid "Enter a valid URL."
|
msgid "Enter a valid URL."
|
||||||
msgstr "Inserire una URL valida."
|
msgstr "Inserire una URL valida."
|
||||||
|
|
||||||
#: newforms/fields.py:498
|
#: newforms/fields.py:523
|
||||||
msgid "This URL appears to be a broken link."
|
msgid "This URL appears to be a broken link."
|
||||||
msgstr "Questa URL non sembra funzionare."
|
msgstr "Questa URL non sembra funzionare."
|
||||||
|
|
||||||
#: newforms/fields.py:560 newforms/models.py:299
|
#: newforms/fields.py:588 newforms/models.py:306
|
||||||
msgid "Select a valid choice. That choice is not one of the available choices."
|
msgid "Select a valid choice. That choice is not one of the available choices."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Scegliere un'opzione valida. La scelta effettuata non compare tra quelle "
|
"Scegliere un'opzione valida. La scelta effettuata non compare tra quelle "
|
||||||
"disponibili."
|
"disponibili."
|
||||||
|
|
||||||
#: newforms/fields.py:599
|
#: newforms/fields.py:627
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Select a valid choice. %(value)s is not one of the available choices."
|
msgid "Select a valid choice. %(value)s is not one of the available choices."
|
||||||
msgstr "Scegliere un'opzione valida. '%(value)s non compare tra quelle disponibili."
|
msgstr "Scegliere un'opzione valida. '%(value)s non compare tra quelle disponibili."
|
||||||
|
|
||||||
#: newforms/fields.py:600 newforms/fields.py:662 newforms/models.py:371
|
#: newforms/fields.py:628 newforms/fields.py:690 newforms/models.py:373
|
||||||
msgid "Enter a list of values."
|
msgid "Enter a list of values."
|
||||||
msgstr "Inserire una lista di valori."
|
msgstr "Inserire una lista di valori."
|
||||||
|
|
||||||
#: newforms/fields.py:780
|
#: newforms/fields.py:808
|
||||||
msgid "Enter a valid IPv4 address."
|
msgid "Enter a valid IPv4 address."
|
||||||
msgstr "Inserire un indirizzo IPv4 valido."
|
msgstr "Inserire un indirizzo IPv4 valido."
|
||||||
|
|
||||||
#: newforms/models.py:372
|
#: newforms/models.py:374
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Select a valid choice. %s is not one of the available choices."
|
msgid "Select a valid choice. %s is not one of the available choices."
|
||||||
msgstr "Scegliere un'opzione valida. '%s non compare tra quelle disponibili."
|
msgstr "Scegliere un'opzione valida. '%s non compare tra quelle disponibili."
|
||||||
@ -4048,15 +4055,15 @@ msgstr "Non sono ammessi a capo manuali qui."
|
|||||||
msgid "Select a valid choice; '%(data)s' is not in %(choices)s."
|
msgid "Select a valid choice; '%(data)s' is not in %(choices)s."
|
||||||
msgstr "Scegliere un'opzione valida; '%(data)s' non presente in %(choices)s."
|
msgstr "Scegliere un'opzione valida; '%(data)s' non presente in %(choices)s."
|
||||||
|
|
||||||
#: oldforms/__init__.py:745
|
#: oldforms/__init__.py:754
|
||||||
msgid "Enter a whole number between -32,768 and 32,767."
|
msgid "Enter a whole number between -32,768 and 32,767."
|
||||||
msgstr "Inserire un numero intero compreso tra -32.768 e 32.767 ."
|
msgstr "Inserire un numero intero compreso tra -32.768 e 32.767 ."
|
||||||
|
|
||||||
#: oldforms/__init__.py:755
|
#: oldforms/__init__.py:764
|
||||||
msgid "Enter a positive number."
|
msgid "Enter a positive number."
|
||||||
msgstr "Inserire un numero positivo."
|
msgstr "Inserire un numero positivo."
|
||||||
|
|
||||||
#: oldforms/__init__.py:765
|
#: oldforms/__init__.py:774
|
||||||
msgid "Enter a whole number between 0 and 32,767."
|
msgid "Enter a whole number between 0 and 32,767."
|
||||||
msgstr "Inserire un numero intero compreso tra 0 e 32.767 ."
|
msgstr "Inserire un numero intero compreso tra 0 e 32.767 ."
|
||||||
|
|
||||||
@ -4290,7 +4297,7 @@ msgstr "Nov."
|
|||||||
msgid "Dec."
|
msgid "Dec."
|
||||||
msgstr "Dic."
|
msgstr "Dic."
|
||||||
|
|
||||||
#: utils/text.py:127
|
#: utils/text.py:128
|
||||||
msgid "or"
|
msgid "or"
|
||||||
msgstr "o"
|
msgstr "o"
|
||||||
|
|
||||||
@ -4344,23 +4351,23 @@ msgstr "%(number)d %(type)s"
|
|||||||
msgid ", %(number)d %(type)s"
|
msgid ", %(number)d %(type)s"
|
||||||
msgstr ", %(number)d %(type)s"
|
msgstr ", %(number)d %(type)s"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:403
|
#: utils/translation/trans_real.py:412
|
||||||
msgid "DATE_FORMAT"
|
msgid "DATE_FORMAT"
|
||||||
msgstr "j F Y"
|
msgstr "j F Y"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:404
|
#: utils/translation/trans_real.py:413
|
||||||
msgid "DATETIME_FORMAT"
|
msgid "DATETIME_FORMAT"
|
||||||
msgstr "j F Y, H:i"
|
msgstr "j F Y, H:i"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:405
|
#: utils/translation/trans_real.py:414
|
||||||
msgid "TIME_FORMAT"
|
msgid "TIME_FORMAT"
|
||||||
msgstr "H:i"
|
msgstr "H:i"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:421
|
#: utils/translation/trans_real.py:430
|
||||||
msgid "YEAR_MONTH_FORMAT"
|
msgid "YEAR_MONTH_FORMAT"
|
||||||
msgstr "Y F"
|
msgstr "Y F"
|
||||||
|
|
||||||
#: utils/translation/trans_real.py:422
|
#: utils/translation/trans_real.py:431
|
||||||
msgid "MONTH_DAY_FORMAT"
|
msgid "MONTH_DAY_FORMAT"
|
||||||
msgstr "F j"
|
msgstr "F j"
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from django.contrib.admin.views.decorators import staff_member_required
|
|||||||
from django.views.decorators.cache import never_cache
|
from django.views.decorators.cache import never_cache
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
|
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
|
||||||
from django.core.paginator import QuerySetPaginator, InvalidPage
|
from django.core.paginator import Paginator, InvalidPage
|
||||||
from django.shortcuts import get_object_or_404, render_to_response
|
from django.shortcuts import get_object_or_404, render_to_response
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
@ -611,7 +611,7 @@ class ChangeList(object):
|
|||||||
return mark_safe('?' + '&'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20'))
|
return mark_safe('?' + '&'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20'))
|
||||||
|
|
||||||
def get_results(self, request):
|
def get_results(self, request):
|
||||||
paginator = QuerySetPaginator(self.query_set, self.lookup_opts.admin.list_per_page)
|
paginator = Paginator(self.query_set, self.lookup_opts.admin.list_per_page)
|
||||||
|
|
||||||
# Get the number of objects, with admin filters applied.
|
# Get the number of objects, with admin filters applied.
|
||||||
try:
|
try:
|
||||||
|
@ -38,14 +38,11 @@ def debug(request):
|
|||||||
return context_extras
|
return context_extras
|
||||||
|
|
||||||
def i18n(request):
|
def i18n(request):
|
||||||
|
from django.utils import translation
|
||||||
|
|
||||||
context_extras = {}
|
context_extras = {}
|
||||||
context_extras['LANGUAGES'] = settings.LANGUAGES
|
context_extras['LANGUAGES'] = settings.LANGUAGES
|
||||||
if hasattr(request, 'LANGUAGE_CODE'):
|
context_extras['LANGUAGE_CODE'] = translation.get_language()
|
||||||
context_extras['LANGUAGE_CODE'] = request.LANGUAGE_CODE
|
|
||||||
else:
|
|
||||||
context_extras['LANGUAGE_CODE'] = settings.LANGUAGE_CODE
|
|
||||||
|
|
||||||
from django.utils import translation
|
|
||||||
context_extras['LANGUAGE_BIDI'] = translation.get_language_bidi()
|
context_extras['LANGUAGE_BIDI'] = translation.get_language_bidi()
|
||||||
|
|
||||||
return context_extras
|
return context_extras
|
||||||
|
@ -3,12 +3,40 @@ Classes representing uploaded files.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
|
import warnings
|
||||||
try:
|
try:
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile')
|
from django.conf import settings
|
||||||
|
|
||||||
|
__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile', 'SimpleUploadedFile')
|
||||||
|
|
||||||
|
# Because we fooled around with it a bunch, UploadedFile has a bunch
|
||||||
|
# of deprecated properties. This little shortcut helps define 'em
|
||||||
|
# without too much code duplication.
|
||||||
|
def deprecated_property(old, new, readonly=False):
|
||||||
|
def issue_warning():
|
||||||
|
warnings.warn(
|
||||||
|
message = "UploadedFile.%s is deprecated; use UploadedFile.%s instead." % (old, new),
|
||||||
|
category = DeprecationWarning,
|
||||||
|
stacklevel = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
def getter(self):
|
||||||
|
issue_warning()
|
||||||
|
return getattr(self, new)
|
||||||
|
|
||||||
|
def setter(self, value):
|
||||||
|
issue_warning()
|
||||||
|
setattr(self, new, value)
|
||||||
|
|
||||||
|
if readonly:
|
||||||
|
return property(getter)
|
||||||
|
else:
|
||||||
|
return property(getter, setter)
|
||||||
|
|
||||||
class UploadedFile(object):
|
class UploadedFile(object):
|
||||||
"""
|
"""
|
||||||
@ -20,16 +48,19 @@ class UploadedFile(object):
|
|||||||
"""
|
"""
|
||||||
DEFAULT_CHUNK_SIZE = 64 * 2**10
|
DEFAULT_CHUNK_SIZE = 64 * 2**10
|
||||||
|
|
||||||
def __init__(self, file_name=None, content_type=None, file_size=None, charset=None):
|
def __init__(self, name=None, content_type=None, size=None, charset=None):
|
||||||
self.file_name = file_name
|
self.name = name
|
||||||
self.file_size = file_size
|
self.size = size
|
||||||
self.content_type = content_type
|
self.content_type = content_type
|
||||||
self.charset = charset
|
self.charset = charset
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s: %s (%s)>" % (self.__class__.__name__, self.file_name, self.content_type)
|
return "<%s: %s (%s)>" % (self.__class__.__name__, self.name, self.content_type)
|
||||||
|
|
||||||
def _set_file_name(self, name):
|
def _get_name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def _set_name(self, name):
|
||||||
# Sanitize the file name so that it can't be dangerous.
|
# Sanitize the file name so that it can't be dangerous.
|
||||||
if name is not None:
|
if name is not None:
|
||||||
# Just use the basename of the file -- anything else is dangerous.
|
# Just use the basename of the file -- anything else is dangerous.
|
||||||
@ -40,14 +71,11 @@ class UploadedFile(object):
|
|||||||
name, ext = os.path.splitext(name)
|
name, ext = os.path.splitext(name)
|
||||||
name = name[:255 - len(ext)] + ext
|
name = name[:255 - len(ext)] + ext
|
||||||
|
|
||||||
self._file_name = name
|
self._name = name
|
||||||
|
|
||||||
def _get_file_name(self):
|
name = property(_get_name, _set_name)
|
||||||
return self._file_name
|
|
||||||
|
|
||||||
file_name = property(_get_file_name, _set_file_name)
|
def chunks(self, chunk_size=None):
|
||||||
|
|
||||||
def chunk(self, chunk_size=None):
|
|
||||||
"""
|
"""
|
||||||
Read the file and yield chucks of ``chunk_size`` bytes (defaults to
|
Read the file and yield chucks of ``chunk_size`` bytes (defaults to
|
||||||
``UploadedFile.DEFAULT_CHUNK_SIZE``).
|
``UploadedFile.DEFAULT_CHUNK_SIZE``).
|
||||||
@ -58,12 +86,27 @@ class UploadedFile(object):
|
|||||||
if hasattr(self, 'seek'):
|
if hasattr(self, 'seek'):
|
||||||
self.seek(0)
|
self.seek(0)
|
||||||
# Assume the pointer is at zero...
|
# Assume the pointer is at zero...
|
||||||
counter = self.file_size
|
counter = self.size
|
||||||
|
|
||||||
while counter > 0:
|
while counter > 0:
|
||||||
yield self.read(chunk_size)
|
yield self.read(chunk_size)
|
||||||
counter -= chunk_size
|
counter -= chunk_size
|
||||||
|
|
||||||
|
# Deprecated properties
|
||||||
|
filename = deprecated_property(old="filename", new="name")
|
||||||
|
file_name = deprecated_property(old="file_name", new="name")
|
||||||
|
file_size = deprecated_property(old="file_size", new="size")
|
||||||
|
chunk = deprecated_property(old="chunk", new="chunks", readonly=True)
|
||||||
|
|
||||||
|
def _get_data(self):
|
||||||
|
warnings.warn(
|
||||||
|
message = "UploadedFile.data is deprecated; use UploadedFile.read() instead.",
|
||||||
|
category = DeprecationWarning,
|
||||||
|
stacklevel = 2
|
||||||
|
)
|
||||||
|
return self.read()
|
||||||
|
data = property(_get_data)
|
||||||
|
|
||||||
def multiple_chunks(self, chunk_size=None):
|
def multiple_chunks(self, chunk_size=None):
|
||||||
"""
|
"""
|
||||||
Returns ``True`` if you can expect multiple chunks.
|
Returns ``True`` if you can expect multiple chunks.
|
||||||
@ -74,9 +117,9 @@ class UploadedFile(object):
|
|||||||
"""
|
"""
|
||||||
if not chunk_size:
|
if not chunk_size:
|
||||||
chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE
|
chunk_size = UploadedFile.DEFAULT_CHUNK_SIZE
|
||||||
return self.file_size < chunk_size
|
return self.size > chunk_size
|
||||||
|
|
||||||
# Abstract methods; subclasses *must* default read() and probably should
|
# Abstract methods; subclasses *must* define read() and probably should
|
||||||
# define open/close.
|
# define open/close.
|
||||||
def read(self, num_bytes=None):
|
def read(self, num_bytes=None):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@ -87,23 +130,49 @@ class UploadedFile(object):
|
|||||||
def close(self):
|
def close(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def xreadlines(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def readlines(self):
|
||||||
|
return list(self.xreadlines())
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
# Iterate over this file-like object by newlines
|
||||||
|
buffer_ = None
|
||||||
|
for chunk in self.chunks():
|
||||||
|
chunk_buffer = StringIO(chunk)
|
||||||
|
|
||||||
|
for line in chunk_buffer:
|
||||||
|
if buffer_:
|
||||||
|
line = buffer_ + line
|
||||||
|
buffer_ = None
|
||||||
|
|
||||||
|
# If this is the end of a line, yield
|
||||||
|
# otherwise, wait for the next round
|
||||||
|
if line[-1] in ('\n', '\r'):
|
||||||
|
yield line
|
||||||
|
else:
|
||||||
|
buffer_ = line
|
||||||
|
|
||||||
|
if buffer_ is not None:
|
||||||
|
yield buffer_
|
||||||
|
|
||||||
# Backwards-compatible support for uploaded-files-as-dictionaries.
|
# Backwards-compatible support for uploaded-files-as-dictionaries.
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
import warnings
|
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
message = "The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.",
|
message = "The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.",
|
||||||
category = DeprecationWarning,
|
category = DeprecationWarning,
|
||||||
stacklevel = 2
|
stacklevel = 2
|
||||||
)
|
)
|
||||||
backwards_translate = {
|
backwards_translate = {
|
||||||
'filename': 'file_name',
|
'filename': 'name',
|
||||||
'content-type': 'content_type',
|
'content-type': 'content_type',
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == 'content':
|
if key == 'content':
|
||||||
return self.read()
|
return self.read()
|
||||||
elif key == 'filename':
|
elif key == 'filename':
|
||||||
return self.file_name
|
return self.name
|
||||||
elif key == 'content-type':
|
elif key == 'content-type':
|
||||||
return self.content_type
|
return self.content_type
|
||||||
else:
|
else:
|
||||||
@ -113,34 +182,36 @@ class TemporaryUploadedFile(UploadedFile):
|
|||||||
"""
|
"""
|
||||||
A file uploaded to a temporary location (i.e. stream-to-disk).
|
A file uploaded to a temporary location (i.e. stream-to-disk).
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, name, content_type, size, charset):
|
||||||
def __init__(self, file, file_name, content_type, file_size, charset):
|
super(TemporaryUploadedFile, self).__init__(name, content_type, size, charset)
|
||||||
super(TemporaryUploadedFile, self).__init__(file_name, content_type, file_size, charset)
|
if settings.FILE_UPLOAD_TEMP_DIR:
|
||||||
self.file = file
|
self._file = tempfile.NamedTemporaryFile(suffix='.upload', dir=settings.FILE_UPLOAD_TEMP_DIR)
|
||||||
self.path = file.name
|
else:
|
||||||
self.file.seek(0)
|
self._file = tempfile.NamedTemporaryFile(suffix='.upload')
|
||||||
|
|
||||||
def temporary_file_path(self):
|
def temporary_file_path(self):
|
||||||
"""
|
"""
|
||||||
Returns the full path of this file.
|
Returns the full path of this file.
|
||||||
"""
|
"""
|
||||||
return self.path
|
return self._file.name
|
||||||
|
|
||||||
def read(self, *args, **kwargs):
|
# Most methods on this object get proxied to NamedTemporaryFile.
|
||||||
return self.file.read(*args, **kwargs)
|
# We can't directly subclass because NamedTemporaryFile is actually a
|
||||||
|
# factory function
|
||||||
def open(self):
|
def read(self, *args): return self._file.read(*args)
|
||||||
self.seek(0)
|
def seek(self, offset): return self._file.seek(offset)
|
||||||
|
def write(self, s): return self._file.write(s)
|
||||||
def seek(self, *args, **kwargs):
|
def close(self): return self._file.close()
|
||||||
self.file.seek(*args, **kwargs)
|
def __iter__(self): return iter(self._file)
|
||||||
|
def readlines(self, size=None): return self._file.readlines(size)
|
||||||
|
def xreadlines(self): return self._file.xreadlines()
|
||||||
|
|
||||||
class InMemoryUploadedFile(UploadedFile):
|
class InMemoryUploadedFile(UploadedFile):
|
||||||
"""
|
"""
|
||||||
A file uploaded into memory (i.e. stream-to-memory).
|
A file uploaded into memory (i.e. stream-to-memory).
|
||||||
"""
|
"""
|
||||||
def __init__(self, file, field_name, file_name, content_type, file_size, charset):
|
def __init__(self, file, field_name, name, content_type, size, charset):
|
||||||
super(InMemoryUploadedFile, self).__init__(file_name, content_type, file_size, charset)
|
super(InMemoryUploadedFile, self).__init__(name, content_type, size, charset)
|
||||||
self.file = file
|
self.file = file
|
||||||
self.field_name = field_name
|
self.field_name = field_name
|
||||||
self.file.seek(0)
|
self.file.seek(0)
|
||||||
@ -154,7 +225,7 @@ class InMemoryUploadedFile(UploadedFile):
|
|||||||
def read(self, *args, **kwargs):
|
def read(self, *args, **kwargs):
|
||||||
return self.file.read(*args, **kwargs)
|
return self.file.read(*args, **kwargs)
|
||||||
|
|
||||||
def chunk(self, chunk_size=None):
|
def chunks(self, chunk_size=None):
|
||||||
self.file.seek(0)
|
self.file.seek(0)
|
||||||
yield self.read()
|
yield self.read()
|
||||||
|
|
||||||
@ -168,9 +239,9 @@ class SimpleUploadedFile(InMemoryUploadedFile):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, name, content, content_type='text/plain'):
|
def __init__(self, name, content, content_type='text/plain'):
|
||||||
self.file = StringIO(content or '')
|
self.file = StringIO(content or '')
|
||||||
self.file_name = name
|
self.name = name
|
||||||
self.field_name = None
|
self.field_name = None
|
||||||
self.file_size = len(content or '')
|
self.size = len(content or '')
|
||||||
self.content_type = content_type
|
self.content_type = content_type
|
||||||
self.charset = None
|
self.charset = None
|
||||||
self.file.seek(0)
|
self.file.seek(0)
|
||||||
|
@ -132,21 +132,15 @@ class TemporaryFileUploadHandler(FileUploadHandler):
|
|||||||
Create the file object to append to as data is coming in.
|
Create the file object to append to as data is coming in.
|
||||||
"""
|
"""
|
||||||
super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs)
|
super(TemporaryFileUploadHandler, self).new_file(file_name, *args, **kwargs)
|
||||||
self.file = TemporaryFile(settings.FILE_UPLOAD_TEMP_DIR)
|
self.file = TemporaryUploadedFile(self.file_name, self.content_type, 0, self.charset)
|
||||||
self.write = self.file.write
|
|
||||||
|
|
||||||
def receive_data_chunk(self, raw_data, start):
|
def receive_data_chunk(self, raw_data, start):
|
||||||
self.write(raw_data)
|
self.file.write(raw_data)
|
||||||
|
|
||||||
def file_complete(self, file_size):
|
def file_complete(self, file_size):
|
||||||
self.file.seek(0)
|
self.file.seek(0)
|
||||||
return TemporaryUploadedFile(
|
self.file.size = file_size
|
||||||
file = self.file,
|
return self.file
|
||||||
file_name = self.file_name,
|
|
||||||
content_type = self.content_type,
|
|
||||||
file_size = file_size,
|
|
||||||
charset = self.charset
|
|
||||||
)
|
|
||||||
|
|
||||||
class MemoryFileUploadHandler(FileUploadHandler):
|
class MemoryFileUploadHandler(FileUploadHandler):
|
||||||
"""
|
"""
|
||||||
@ -189,37 +183,12 @@ class MemoryFileUploadHandler(FileUploadHandler):
|
|||||||
return InMemoryUploadedFile(
|
return InMemoryUploadedFile(
|
||||||
file = self.file,
|
file = self.file,
|
||||||
field_name = self.field_name,
|
field_name = self.field_name,
|
||||||
file_name = self.file_name,
|
name = self.file_name,
|
||||||
content_type = self.content_type,
|
content_type = self.content_type,
|
||||||
file_size = file_size,
|
size = file_size,
|
||||||
charset = self.charset
|
charset = self.charset
|
||||||
)
|
)
|
||||||
|
|
||||||
class TemporaryFile(object):
|
|
||||||
"""
|
|
||||||
A temporary file that tries to delete itself when garbage collected.
|
|
||||||
"""
|
|
||||||
def __init__(self, dir):
|
|
||||||
if not dir:
|
|
||||||
dir = tempfile.gettempdir()
|
|
||||||
try:
|
|
||||||
(fd, name) = tempfile.mkstemp(suffix='.upload', dir=dir)
|
|
||||||
self.file = os.fdopen(fd, 'w+b')
|
|
||||||
except (OSError, IOError):
|
|
||||||
raise OSError("Could not create temporary file for uploading, have you set settings.FILE_UPLOAD_TEMP_DIR correctly?")
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
a = getattr(self.__dict__['file'], name)
|
|
||||||
if type(a) != type(0):
|
|
||||||
setattr(self, name, a)
|
|
||||||
return a
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
try:
|
|
||||||
os.unlink(self.name)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def load_handler(path, *args, **kwargs):
|
def load_handler(path, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -6,8 +6,12 @@ from django.dispatch import dispatcher
|
|||||||
|
|
||||||
class BaseHandler(object):
|
class BaseHandler(object):
|
||||||
# Changes that are always applied to a response (in this order).
|
# Changes that are always applied to a response (in this order).
|
||||||
response_fixes = [http.fix_location_header,
|
response_fixes = [
|
||||||
http.conditional_content_removal]
|
http.fix_location_header,
|
||||||
|
http.conditional_content_removal,
|
||||||
|
http.fix_IE_for_attach,
|
||||||
|
http.fix_IE_for_vary,
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
|
self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
|
||||||
|
@ -205,10 +205,12 @@ class EmailMessage(object):
|
|||||||
conversions.
|
conversions.
|
||||||
"""
|
"""
|
||||||
if to:
|
if to:
|
||||||
|
assert not isinstance(to, basestring), '"to" argument must be a list or tuple'
|
||||||
self.to = list(to)
|
self.to = list(to)
|
||||||
else:
|
else:
|
||||||
self.to = []
|
self.to = []
|
||||||
if bcc:
|
if bcc:
|
||||||
|
assert not isinstance(bcc, basestring), '"bcc" argument must be a list or tuple'
|
||||||
self.bcc = list(bcc)
|
self.bcc = list(bcc)
|
||||||
else:
|
else:
|
||||||
self.bcc = []
|
self.bcc = []
|
||||||
|
@ -135,6 +135,35 @@ class LaxOptionParser(OptionParser):
|
|||||||
def error(self, msg):
|
def error(self, msg):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _process_args(self, largs, rargs, values):
|
||||||
|
"""
|
||||||
|
Overrides OptionParser._process_args to exclusively handle default
|
||||||
|
options and ignore args and other options.
|
||||||
|
|
||||||
|
This overrides the behavior of the super class, which stop parsing
|
||||||
|
at the first unrecognized option.
|
||||||
|
"""
|
||||||
|
while rargs:
|
||||||
|
arg = rargs[0]
|
||||||
|
try:
|
||||||
|
if arg[0:2] == "--" and len(arg) > 2:
|
||||||
|
# process a single long option (possibly with value(s))
|
||||||
|
# the superclass code pops the arg off rargs
|
||||||
|
self._process_long_opt(rargs, values)
|
||||||
|
elif arg[:1] == "-" and len(arg) > 1:
|
||||||
|
# process a cluster of short options (possibly with
|
||||||
|
# value(s) for the last one only)
|
||||||
|
# the superclass code pops the arg off rargs
|
||||||
|
self._process_short_opts(rargs, values)
|
||||||
|
else:
|
||||||
|
# it's either a non-default option or an arg
|
||||||
|
# either way, add it to the args list so we can keep
|
||||||
|
# dealing with options
|
||||||
|
del rargs[0]
|
||||||
|
raise error
|
||||||
|
except:
|
||||||
|
largs.append(arg)
|
||||||
|
|
||||||
class ManagementUtility(object):
|
class ManagementUtility(object):
|
||||||
"""
|
"""
|
||||||
Encapsulates the logic of the django-admin.py and manage.py utilities.
|
Encapsulates the logic of the django-admin.py and manage.py utilities.
|
||||||
|
@ -6,6 +6,11 @@ import django
|
|||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.management.color import color_style
|
from django.core.management.color import color_style
|
||||||
|
|
||||||
|
try:
|
||||||
|
set
|
||||||
|
except NameError:
|
||||||
|
from sets import Set as set # For Python 2.3
|
||||||
|
|
||||||
class CommandError(Exception):
|
class CommandError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
11
django/core/management/commands/cleanup.py
Normal file
11
django/core/management/commands/cleanup.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import datetime
|
||||||
|
from django.core.management.base import NoArgsCommand
|
||||||
|
|
||||||
|
class Command(NoArgsCommand):
|
||||||
|
help = "Can be run as a cronjob or directly to clean out old data from the database (only expired sessions at the moment)."
|
||||||
|
|
||||||
|
def handle_noargs(self, **options):
|
||||||
|
from django.db import transaction
|
||||||
|
from django.contrib.sessions.models import Session
|
||||||
|
Session.objects.filter(expire_date__lt=datetime.datetime.now()).delete()
|
||||||
|
transaction.commit_unless_managed()
|
58
django/core/management/commands/compilemessages.py
Normal file
58
django/core/management/commands/compilemessages.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from optparse import make_option
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.core.management.color import no_style
|
||||||
|
|
||||||
|
try:
|
||||||
|
set
|
||||||
|
except NameError:
|
||||||
|
from sets import Set as set # For Python 2.3
|
||||||
|
|
||||||
|
def compile_messages(locale=None):
|
||||||
|
basedirs = (os.path.join('conf', 'locale'), 'locale')
|
||||||
|
if os.environ.get('DJANGO_SETTINGS_MODULE'):
|
||||||
|
from django.conf import settings
|
||||||
|
basedirs += settings.LOCALE_PATHS
|
||||||
|
|
||||||
|
# Gather existing directories.
|
||||||
|
basedirs = set(map(os.path.abspath, filter(os.path.isdir, basedirs)))
|
||||||
|
|
||||||
|
if not basedirs:
|
||||||
|
raise CommandError("This script should be run from the Django SVN tree or your project or app tree, or with the settings module specified.")
|
||||||
|
|
||||||
|
for basedir in basedirs:
|
||||||
|
if locale:
|
||||||
|
basedir = os.path.join(basedir, locale, 'LC_MESSAGES')
|
||||||
|
for dirpath, dirnames, filenames in os.walk(basedir):
|
||||||
|
for f in filenames:
|
||||||
|
if f.endswith('.po'):
|
||||||
|
sys.stderr.write('processing file %s in %s\n' % (f, dirpath))
|
||||||
|
pf = os.path.splitext(os.path.join(dirpath, f))[0]
|
||||||
|
# Store the names of the .mo and .po files in an environment
|
||||||
|
# variable, rather than doing a string replacement into the
|
||||||
|
# command, so that we can take advantage of shell quoting, to
|
||||||
|
# quote any malicious characters/escaping.
|
||||||
|
# See http://cyberelk.net/tim/articles/cmdline/ar01s02.html
|
||||||
|
os.environ['djangocompilemo'] = pf + '.mo'
|
||||||
|
os.environ['djangocompilepo'] = pf + '.po'
|
||||||
|
if sys.platform == 'win32': # Different shell-variable syntax
|
||||||
|
cmd = 'msgfmt --check-format -o "%djangocompilemo%" "%djangocompilepo%"'
|
||||||
|
else:
|
||||||
|
cmd = 'msgfmt --check-format -o "$djangocompilemo" "$djangocompilepo"'
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
option_list = BaseCommand.option_list + (
|
||||||
|
make_option('--locale', '-l', dest='locale',
|
||||||
|
help='The locale to process. Default is to process all.'),
|
||||||
|
)
|
||||||
|
help = 'Compiles .po files to .mo files for use with builtin gettext support.'
|
||||||
|
|
||||||
|
requires_model_validation = False
|
||||||
|
can_import_settings = False
|
||||||
|
|
||||||
|
def handle(self, **options):
|
||||||
|
locale = options.get('locale')
|
||||||
|
compile_messages(locale)
|
162
django/core/management/commands/makemessages.py
Normal file
162
django/core/management/commands/makemessages.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import re
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from itertools import dropwhile
|
||||||
|
from optparse import make_option
|
||||||
|
from django.core.management.base import CommandError, BaseCommand
|
||||||
|
|
||||||
|
pythonize_re = re.compile(r'\n\s*//')
|
||||||
|
|
||||||
|
def make_messages(locale=None, domain='django', verbosity='1', all=False):
|
||||||
|
"""
|
||||||
|
Uses the locale directory from the Django SVN tree or an application/
|
||||||
|
project to process all
|
||||||
|
"""
|
||||||
|
# Need to ensure that the i18n framework is enabled
|
||||||
|
from django.conf import settings
|
||||||
|
if settings.configured:
|
||||||
|
settings.USE_I18N = True
|
||||||
|
else:
|
||||||
|
settings.configure(USE_I18N = True)
|
||||||
|
|
||||||
|
from django.utils.translation import templatize
|
||||||
|
|
||||||
|
if os.path.isdir(os.path.join('conf', 'locale')):
|
||||||
|
localedir = os.path.abspath(os.path.join('conf', 'locale'))
|
||||||
|
elif os.path.isdir('locale'):
|
||||||
|
localedir = os.path.abspath('locale')
|
||||||
|
else:
|
||||||
|
raise CommandError("This script should be run from the Django SVN tree or your project or app tree. If you did indeed run it from the SVN checkout or your project or application, maybe you are just missing the conf/locale (in the django tree) or locale (for project and application) directory? It is not created automatically, you have to create it by hand if you want to enable i18n for your project or application.")
|
||||||
|
|
||||||
|
if domain not in ('django', 'djangojs'):
|
||||||
|
raise CommandError("currently makemessages only supports domains 'django' and 'djangojs'")
|
||||||
|
|
||||||
|
if (locale is None and not all) or domain is None:
|
||||||
|
# backwards compatible error message
|
||||||
|
if not sys.argv[0].endswith("make-messages.py"):
|
||||||
|
message = "Type '%s help %s' for usage.\n" % (os.path.basename(sys.argv[0]), sys.argv[1])
|
||||||
|
else:
|
||||||
|
message = "usage: make-messages.py -l <language>\n or: make-messages.py -a\n"
|
||||||
|
raise CommandError(message)
|
||||||
|
|
||||||
|
languages = []
|
||||||
|
if locale is not None:
|
||||||
|
languages.append(locale)
|
||||||
|
elif all:
|
||||||
|
languages = [el for el in os.listdir(localedir) if not el.startswith('.')]
|
||||||
|
|
||||||
|
for locale in languages:
|
||||||
|
if verbosity > 0:
|
||||||
|
print "processing language", locale
|
||||||
|
basedir = os.path.join(localedir, locale, 'LC_MESSAGES')
|
||||||
|
if not os.path.isdir(basedir):
|
||||||
|
os.makedirs(basedir)
|
||||||
|
|
||||||
|
pofile = os.path.join(basedir, '%s.po' % domain)
|
||||||
|
potfile = os.path.join(basedir, '%s.pot' % domain)
|
||||||
|
|
||||||
|
if os.path.exists(potfile):
|
||||||
|
os.unlink(potfile)
|
||||||
|
|
||||||
|
all_files = []
|
||||||
|
for (dirpath, dirnames, filenames) in os.walk("."):
|
||||||
|
all_files.extend([(dirpath, f) for f in filenames])
|
||||||
|
all_files.sort()
|
||||||
|
for dirpath, file in all_files:
|
||||||
|
if domain == 'djangojs' and file.endswith('.js'):
|
||||||
|
if verbosity > 1:
|
||||||
|
sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
|
||||||
|
src = open(os.path.join(dirpath, file), "rb").read()
|
||||||
|
src = pythonize_re.sub('\n#', src)
|
||||||
|
open(os.path.join(dirpath, '%s.py' % file), "wb").write(src)
|
||||||
|
thefile = '%s.py' % file
|
||||||
|
cmd = 'xgettext -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % (domain, os.path.join(dirpath, thefile))
|
||||||
|
(stdin, stdout, stderr) = os.popen3(cmd, 't')
|
||||||
|
msgs = stdout.read()
|
||||||
|
errors = stderr.read()
|
||||||
|
if errors:
|
||||||
|
raise CommandError("errors happened while running xgettext on %s\n%s" % (file, errors))
|
||||||
|
old = '#: '+os.path.join(dirpath, thefile)[2:]
|
||||||
|
new = '#: '+os.path.join(dirpath, file)[2:]
|
||||||
|
msgs = msgs.replace(old, new)
|
||||||
|
if os.path.exists(potfile):
|
||||||
|
# Strip the header
|
||||||
|
msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
|
||||||
|
else:
|
||||||
|
msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8')
|
||||||
|
if msgs:
|
||||||
|
open(potfile, 'ab').write(msgs)
|
||||||
|
os.unlink(os.path.join(dirpath, thefile))
|
||||||
|
elif domain == 'django' and (file.endswith('.py') or file.endswith('.html')):
|
||||||
|
thefile = file
|
||||||
|
if file.endswith('.html'):
|
||||||
|
src = open(os.path.join(dirpath, file), "rb").read()
|
||||||
|
thefile = '%s.py' % file
|
||||||
|
open(os.path.join(dirpath, thefile), "wb").write(templatize(src))
|
||||||
|
if verbosity > 1:
|
||||||
|
sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
|
||||||
|
cmd = 'xgettext -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --keyword=ugettext_noop --keyword=ugettext_lazy --keyword=ungettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % (
|
||||||
|
domain, os.path.join(dirpath, thefile))
|
||||||
|
(stdin, stdout, stderr) = os.popen3(cmd, 't')
|
||||||
|
msgs = stdout.read()
|
||||||
|
errors = stderr.read()
|
||||||
|
if errors:
|
||||||
|
raise CommandError("errors happened while running xgettext on %s\n%s" % (file, errors))
|
||||||
|
if thefile != file:
|
||||||
|
old = '#: '+os.path.join(dirpath, thefile)[2:]
|
||||||
|
new = '#: '+os.path.join(dirpath, file)[2:]
|
||||||
|
msgs = msgs.replace(old, new)
|
||||||
|
if os.path.exists(potfile):
|
||||||
|
# Strip the header
|
||||||
|
msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
|
||||||
|
else:
|
||||||
|
msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8')
|
||||||
|
if msgs:
|
||||||
|
open(potfile, 'ab').write(msgs)
|
||||||
|
if thefile != file:
|
||||||
|
os.unlink(os.path.join(dirpath, thefile))
|
||||||
|
|
||||||
|
if os.path.exists(potfile):
|
||||||
|
(stdin, stdout, stderr) = os.popen3('msguniq --to-code=utf-8 "%s"' % potfile, 'b')
|
||||||
|
msgs = stdout.read()
|
||||||
|
errors = stderr.read()
|
||||||
|
if errors:
|
||||||
|
raise CommandError("errors happened while running msguniq\n%s" % errors)
|
||||||
|
open(potfile, 'w').write(msgs)
|
||||||
|
if os.path.exists(pofile):
|
||||||
|
(stdin, stdout, stderr) = os.popen3('msgmerge -q "%s" "%s"' % (pofile, potfile), 'b')
|
||||||
|
msgs = stdout.read()
|
||||||
|
errors = stderr.read()
|
||||||
|
if errors:
|
||||||
|
raise CommandError("errors happened while running msgmerge\n%s" % errors)
|
||||||
|
open(pofile, 'wb').write(msgs)
|
||||||
|
os.unlink(potfile)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
option_list = BaseCommand.option_list + (
|
||||||
|
make_option('--locale', '-l', default=None, dest='locale',
|
||||||
|
help='Creates or updates the message files only for the given locale (e.g. pt_BR).'),
|
||||||
|
make_option('--domain', '-d', default='django', dest='domain',
|
||||||
|
help='The domain of the message files (default: "django").'),
|
||||||
|
make_option('--verbosity', '-v', action='store', dest='verbosity',
|
||||||
|
default='1', type='choice', choices=['0', '1', '2'],
|
||||||
|
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
|
||||||
|
make_option('--all', '-a', action='store_true', dest='all',
|
||||||
|
default=False, help='Reexamines all source code and templates for new translation strings and updates all message files for all available languages.'),
|
||||||
|
)
|
||||||
|
help = "Runs over the entire source tree of the current directory and pulls out all strings marked for translation. It creates (or updates) a message file in the conf/locale (in the django tree) or locale (for project and application) directory."
|
||||||
|
|
||||||
|
requires_model_validation = False
|
||||||
|
can_import_settings = False
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
if len(args) != 0:
|
||||||
|
raise CommandError("Command doesn't accept any arguments")
|
||||||
|
|
||||||
|
locale = options.get('locale')
|
||||||
|
domain = options.get('domain')
|
||||||
|
verbosity = int(options.get('verbosity'))
|
||||||
|
process_all = options.get('all')
|
||||||
|
|
||||||
|
make_messages(locale, domain, verbosity, process_all)
|
@ -41,10 +41,11 @@ class Command(NoArgsCommand):
|
|||||||
# but raises an ImportError for some reason. The only way we
|
# but raises an ImportError for some reason. The only way we
|
||||||
# can do this is to check the text of the exception. Note that
|
# can do this is to check the text of the exception. Note that
|
||||||
# we're a bit broad in how we check the text, because different
|
# we're a bit broad in how we check the text, because different
|
||||||
# Python implementations may not use the same text. CPython
|
# Python implementations may not use the same text.
|
||||||
# uses the text "No module named management".
|
# CPython uses the text "No module named management"
|
||||||
|
# PyPy uses "No module named myproject.myapp.management"
|
||||||
msg = exc.args[0]
|
msg = exc.args[0]
|
||||||
if not msg.startswith('No module named management') or 'management' not in msg:
|
if not msg.startswith('No module named') or 'management' not in msg:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
@ -106,6 +107,9 @@ class Command(NoArgsCommand):
|
|||||||
# to do at this point.
|
# to do at this point.
|
||||||
emit_post_sync_signal(created_models, verbosity, interactive)
|
emit_post_sync_signal(created_models, verbosity, interactive)
|
||||||
|
|
||||||
|
# The connection may have been closed by a syncdb handler.
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
# Install custom SQL for the app (but only if this
|
# Install custom SQL for the app (but only if this
|
||||||
# is a model we've just created)
|
# is a model we've just created)
|
||||||
for app in models.get_apps():
|
for app in models.get_apps():
|
||||||
@ -144,7 +148,7 @@ class Command(NoArgsCommand):
|
|||||||
for sql in index_sql:
|
for sql in index_sql:
|
||||||
cursor.execute(sql)
|
cursor.execute(sql)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
sys.stderr.write("Failed to install index for %s.%s model: %s" % \
|
sys.stderr.write("Failed to install index for %s.%s model: %s\n" % \
|
||||||
(app_name, model._meta.object_name, e))
|
(app_name, model._meta.object_name, e))
|
||||||
transaction.rollback_unless_managed()
|
transaction.rollback_unless_managed()
|
||||||
else:
|
else:
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
class InvalidPage(Exception):
|
class InvalidPage(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class PageNotAnInteger(InvalidPage):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class EmptyPage(InvalidPage):
|
||||||
|
pass
|
||||||
|
|
||||||
class Paginator(object):
|
class Paginator(object):
|
||||||
def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
|
def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
|
||||||
self.object_list = object_list
|
self.object_list = object_list
|
||||||
@ -14,14 +20,14 @@ class Paginator(object):
|
|||||||
try:
|
try:
|
||||||
number = int(number)
|
number = int(number)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise InvalidPage('That page number is not an integer')
|
raise PageNotAnInteger('That page number is not an integer')
|
||||||
if number < 1:
|
if number < 1:
|
||||||
raise InvalidPage('That page number is less than 1')
|
raise EmptyPage('That page number is less than 1')
|
||||||
if number > self.num_pages:
|
if number > self.num_pages:
|
||||||
if number == 1 and self.allow_empty_first_page:
|
if number == 1 and self.allow_empty_first_page:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise InvalidPage('That page contains no results')
|
raise EmptyPage('That page contains no results')
|
||||||
return number
|
return number
|
||||||
|
|
||||||
def page(self, number):
|
def page(self, number):
|
||||||
@ -36,6 +42,10 @@ class Paginator(object):
|
|||||||
def _get_count(self):
|
def _get_count(self):
|
||||||
"Returns the total number of objects, across all pages."
|
"Returns the total number of objects, across all pages."
|
||||||
if self._count is None:
|
if self._count is None:
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
if isinstance(self.object_list, QuerySet):
|
||||||
|
self._count = self.object_list.count()
|
||||||
|
else:
|
||||||
self._count = len(self.object_list)
|
self._count = len(self.object_list)
|
||||||
return self._count
|
return self._count
|
||||||
count = property(_get_count)
|
count = property(_get_count)
|
||||||
@ -61,15 +71,7 @@ class Paginator(object):
|
|||||||
return range(1, self.num_pages + 1)
|
return range(1, self.num_pages + 1)
|
||||||
page_range = property(_get_page_range)
|
page_range = property(_get_page_range)
|
||||||
|
|
||||||
class QuerySetPaginator(Paginator):
|
QuerySetPaginator = Paginator # For backwards-compatibility.
|
||||||
"""
|
|
||||||
Like Paginator, but works on QuerySets.
|
|
||||||
"""
|
|
||||||
def _get_count(self):
|
|
||||||
if self._count is None:
|
|
||||||
self._count = self.object_list.count()
|
|
||||||
return self._count
|
|
||||||
count = property(_get_count)
|
|
||||||
|
|
||||||
class Page(object):
|
class Page(object):
|
||||||
def __init__(self, object_list, number, paginator):
|
def __init__(self, object_list, number, paginator):
|
||||||
@ -133,14 +135,14 @@ class ObjectPaginator(Paginator):
|
|||||||
try:
|
try:
|
||||||
page_number = int(page_number) + 1
|
page_number = int(page_number) + 1
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise InvalidPage
|
raise PageNotAnInteger
|
||||||
return self.validate_number(page_number)
|
return self.validate_number(page_number)
|
||||||
|
|
||||||
def get_page(self, page_number):
|
def get_page(self, page_number):
|
||||||
try:
|
try:
|
||||||
page_number = int(page_number) + 1
|
page_number = int(page_number) + 1
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise InvalidPage
|
raise PageNotAnInteger
|
||||||
return self.page(page_number).object_list
|
return self.page(page_number).object_list
|
||||||
|
|
||||||
def has_next_page(self, page_number):
|
def has_next_page(self, page_number):
|
||||||
|
@ -177,7 +177,7 @@ def isValidImage(field_data, all_data):
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
try:
|
try:
|
||||||
content = field_data['content']
|
content = field_data.read()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
raise ValidationError, _("No file was submitted. Check the encoding type on the form.")
|
raise ValidationError, _("No file was submitted. Check the encoding type on the form.")
|
||||||
try:
|
try:
|
||||||
@ -469,7 +469,7 @@ class HasAllowableSize(object):
|
|||||||
|
|
||||||
def __call__(self, field_data, all_data):
|
def __call__(self, field_data, all_data):
|
||||||
try:
|
try:
|
||||||
content = field_data['content']
|
content = field_data.read()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
raise ValidationError, ugettext_lazy("No file was submitted. Check the encoding type on the form.")
|
raise ValidationError, ugettext_lazy("No file was submitted. Check the encoding type on the form.")
|
||||||
if self.min_size is not None and len(content) < self.min_size:
|
if self.min_size is not None and len(content) < self.min_size:
|
||||||
|
@ -161,16 +161,6 @@ class BaseDatabaseOperations(object):
|
|||||||
"""
|
"""
|
||||||
return cursor.lastrowid
|
return cursor.lastrowid
|
||||||
|
|
||||||
def limit_offset_sql(self, limit, offset=None):
|
|
||||||
"""
|
|
||||||
Returns a LIMIT/OFFSET SQL clause, given a limit and optional offset.
|
|
||||||
"""
|
|
||||||
# 'LIMIT 40 OFFSET 20'
|
|
||||||
sql = "LIMIT %s" % limit
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += " OFFSET %s" % offset
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def lookup_cast(self, lookup_type):
|
def lookup_cast(self, lookup_type):
|
||||||
"""
|
"""
|
||||||
Returns the string to use in a query when performing lookups
|
Returns the string to use in a query when performing lookups
|
||||||
|
@ -89,13 +89,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||||||
def fulltext_search_sql(self, field_name):
|
def fulltext_search_sql(self, field_name):
|
||||||
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
||||||
|
|
||||||
def limit_offset_sql(self, limit, offset=None):
|
|
||||||
# 'LIMIT 20,40'
|
|
||||||
sql = "LIMIT "
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += "%s," % offset
|
|
||||||
return sql + str(limit)
|
|
||||||
|
|
||||||
def no_limit_value(self):
|
def no_limit_value(self):
|
||||||
# 2**64 - 1, as recommended by the MySQL documentation
|
# 2**64 - 1, as recommended by the MySQL documentation
|
||||||
return 18446744073709551615L
|
return 18446744073709551615L
|
||||||
|
@ -93,13 +93,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||||||
def fulltext_search_sql(self, field_name):
|
def fulltext_search_sql(self, field_name):
|
||||||
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
||||||
|
|
||||||
def limit_offset_sql(self, limit, offset=None):
|
|
||||||
# 'LIMIT 20,40'
|
|
||||||
sql = "LIMIT "
|
|
||||||
if offset and offset != 0:
|
|
||||||
sql += "%s," % offset
|
|
||||||
return sql + str(limit)
|
|
||||||
|
|
||||||
def no_limit_value(self):
|
def no_limit_value(self):
|
||||||
# 2**64 - 1, as recommended by the MySQL documentation
|
# 2**64 - 1, as recommended by the MySQL documentation
|
||||||
return 18446744073709551615L
|
return 18446744073709551615L
|
||||||
|
@ -87,11 +87,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||||||
cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name)
|
cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name)
|
||||||
return cursor.fetchone()[0]
|
return cursor.fetchone()[0]
|
||||||
|
|
||||||
def limit_offset_sql(self, limit, offset=None):
|
|
||||||
# Limits and offset are too complicated to be handled here.
|
|
||||||
# Instead, they are handled in django/db/backends/oracle/query.py.
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def lookup_cast(self, lookup_type):
|
def lookup_cast(self, lookup_type):
|
||||||
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
|
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
|
||||||
return "UPPER(%s)"
|
return "UPPER(%s)"
|
||||||
|
@ -3,6 +3,10 @@ import types
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from itertools import izip
|
from itertools import izip
|
||||||
|
try:
|
||||||
|
set
|
||||||
|
except NameError:
|
||||||
|
from sets import Set as set # Python 2.3 fallback.
|
||||||
|
|
||||||
import django.db.models.manipulators # Imported to register signal handler.
|
import django.db.models.manipulators # Imported to register signal handler.
|
||||||
import django.db.models.manager # Ditto.
|
import django.db.models.manager # Ditto.
|
||||||
@ -23,27 +27,21 @@ from django.core.files.move import file_move_safe
|
|||||||
from django.core.files import locks
|
from django.core.files import locks
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
try:
|
|
||||||
set
|
|
||||||
except NameError:
|
|
||||||
from sets import Set as set # Python 2.3 fallback
|
|
||||||
|
|
||||||
class ModelBase(type):
|
class ModelBase(type):
|
||||||
"Metaclass for all models"
|
"""
|
||||||
|
Metaclass for all models.
|
||||||
|
"""
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
# If this isn't a subclass of Model, don't do anything special.
|
super_new = super(ModelBase, cls).__new__
|
||||||
try:
|
parents = [b for b in bases if isinstance(b, ModelBase)]
|
||||||
parents = [b for b in bases if issubclass(b, Model)]
|
|
||||||
except NameError:
|
|
||||||
# 'Model' isn't defined yet, meaning we're looking at Django's own
|
|
||||||
# Model class, defined below.
|
|
||||||
parents = []
|
|
||||||
if not parents:
|
if not parents:
|
||||||
return super(ModelBase, cls).__new__(cls, name, bases, attrs)
|
# If this isn't a subclass of Model, don't do anything special.
|
||||||
|
return super_new(cls, name, bases, attrs)
|
||||||
|
|
||||||
# Create the class.
|
# Create the class.
|
||||||
module = attrs.pop('__module__')
|
module = attrs.pop('__module__')
|
||||||
new_class = type.__new__(cls, name, bases, {'__module__': module})
|
new_class = super_new(cls, name, bases, {'__module__': module})
|
||||||
attr_meta = attrs.pop('Meta', None)
|
attr_meta = attrs.pop('Meta', None)
|
||||||
abstract = getattr(attr_meta, 'abstract', False)
|
abstract = getattr(attr_meta, 'abstract', False)
|
||||||
if not attr_meta:
|
if not attr_meta:
|
||||||
@ -148,7 +146,9 @@ class ModelBase(type):
|
|||||||
setattr(cls, name, value)
|
setattr(cls, name, value)
|
||||||
|
|
||||||
def _prepare(cls):
|
def _prepare(cls):
|
||||||
# Creates some methods once self._meta has been populated.
|
"""
|
||||||
|
Creates some methods once self._meta has been populated.
|
||||||
|
"""
|
||||||
opts = cls._meta
|
opts = cls._meta
|
||||||
opts._prepare(cls)
|
opts._prepare(cls)
|
||||||
|
|
||||||
@ -167,6 +167,7 @@ class ModelBase(type):
|
|||||||
|
|
||||||
dispatcher.send(signal=signals.class_prepared, sender=cls)
|
dispatcher.send(signal=signals.class_prepared, sender=cls)
|
||||||
|
|
||||||
|
|
||||||
class Model(object):
|
class Model(object):
|
||||||
__metaclass__ = ModelBase
|
__metaclass__ = ModelBase
|
||||||
|
|
||||||
@ -271,7 +272,7 @@ class Model(object):
|
|||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""
|
"""
|
||||||
Save the current instance. Override this in a subclass if you want to
|
Saves the current instance. Override this in a subclass if you want to
|
||||||
control the saving process.
|
control the saving process.
|
||||||
"""
|
"""
|
||||||
self.save_base()
|
self.save_base()
|
||||||
@ -375,10 +376,12 @@ class Model(object):
|
|||||||
|
|
||||||
def _collect_sub_objects(self, seen_objs, parent=None, nullable=False):
|
def _collect_sub_objects(self, seen_objs, parent=None, nullable=False):
|
||||||
"""
|
"""
|
||||||
Recursively populates seen_objs with all objects related to this object.
|
Recursively populates seen_objs with all objects related to this
|
||||||
|
object.
|
||||||
|
|
||||||
When done, seen_objs.items() will be in the format:
|
When done, seen_objs.items() will be in the format:
|
||||||
[(model_class, {pk_val: obj, pk_val: obj, ...}),
|
[(model_class, {pk_val: obj, pk_val: obj, ...}),
|
||||||
(model_class, {pk_val: obj, pk_val: obj, ...}),...]
|
(model_class, {pk_val: obj, pk_val: obj, ...}), ...]
|
||||||
"""
|
"""
|
||||||
pk_val = self._get_pk_val()
|
pk_val = self._get_pk_val()
|
||||||
if seen_objs.add(self.__class__, pk_val, self, parent, nullable):
|
if seen_objs.add(self.__class__, pk_val, self, parent, nullable):
|
||||||
@ -415,11 +418,11 @@ class Model(object):
|
|||||||
def delete(self):
|
def delete(self):
|
||||||
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
|
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
|
||||||
|
|
||||||
# Find all the objects than need to be deleted
|
# Find all the objects than need to be deleted.
|
||||||
seen_objs = CollectedObjects()
|
seen_objs = CollectedObjects()
|
||||||
self._collect_sub_objects(seen_objs)
|
self._collect_sub_objects(seen_objs)
|
||||||
|
|
||||||
# Actually delete the objects
|
# Actually delete the objects.
|
||||||
delete_objects(seen_objs)
|
delete_objects(seen_objs)
|
||||||
|
|
||||||
delete.alters_data = True
|
delete.alters_data = True
|
||||||
@ -458,12 +461,12 @@ class Model(object):
|
|||||||
return getattr(self, cachename)
|
return getattr(self, cachename)
|
||||||
|
|
||||||
def _get_FIELD_filename(self, field):
|
def _get_FIELD_filename(self, field):
|
||||||
if getattr(self, field.attname): # value is not blank
|
if getattr(self, field.attname): # Value is not blank.
|
||||||
return os.path.normpath(os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname)))
|
return os.path.normpath(os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname)))
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def _get_FIELD_url(self, field):
|
def _get_FIELD_url(self, field):
|
||||||
if getattr(self, field.attname): # value is not blank
|
if getattr(self, field.attname): # Value is not blank.
|
||||||
import urlparse
|
import urlparse
|
||||||
return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/')
|
return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/')
|
||||||
return ''
|
return ''
|
||||||
@ -478,16 +481,14 @@ class Model(object):
|
|||||||
except OSError: # Directory probably already exists.
|
except OSError: # Directory probably already exists.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#
|
|
||||||
# Check for old-style usage (files-as-dictionaries). Warn here first
|
# Check for old-style usage (files-as-dictionaries). Warn here first
|
||||||
# since there are multiple locations where we need to support both new
|
# since there are multiple locations where we need to support both new
|
||||||
# and old usage.
|
# and old usage.
|
||||||
#
|
|
||||||
if isinstance(raw_field, dict):
|
if isinstance(raw_field, dict):
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
message = "Representing uploaded files as dictionaries is"\
|
message = "Representing uploaded files as dictionaries is"\
|
||||||
" deprected. Use django.core.files.SimpleUploadedFile"\
|
" deprecated. Use django.core.files.SimpleUploadedFile"\
|
||||||
" instead.",
|
" instead.",
|
||||||
category = DeprecationWarning,
|
category = DeprecationWarning,
|
||||||
stacklevel = 2
|
stacklevel = 2
|
||||||
@ -512,41 +513,35 @@ class Model(object):
|
|||||||
|
|
||||||
filename = field.get_filename(filename)
|
filename = field.get_filename(filename)
|
||||||
|
|
||||||
#
|
# If the filename already exists, keep adding an underscore to the name
|
||||||
# If the filename already exists, keep adding an underscore to the name of
|
# of the file until the filename doesn't exist.
|
||||||
# the file until the filename doesn't exist.
|
|
||||||
#
|
|
||||||
while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):
|
while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)):
|
||||||
try:
|
try:
|
||||||
dot_index = filename.rindex('.')
|
dot_index = filename.rindex('.')
|
||||||
except ValueError: # filename has no dot
|
except ValueError: # filename has no dot.
|
||||||
filename += '_'
|
filename += '_'
|
||||||
else:
|
else:
|
||||||
filename = filename[:dot_index] + '_' + filename[dot_index:]
|
filename = filename[:dot_index] + '_' + filename[dot_index:]
|
||||||
#
|
|
||||||
# Save the file name on the object and write the file to disk
|
|
||||||
#
|
|
||||||
|
|
||||||
|
# Save the file name on the object and write the file to disk.
|
||||||
setattr(self, field.attname, filename)
|
setattr(self, field.attname, filename)
|
||||||
|
|
||||||
full_filename = self._get_FIELD_filename(field)
|
full_filename = self._get_FIELD_filename(field)
|
||||||
|
|
||||||
if hasattr(raw_field, 'temporary_file_path'):
|
if hasattr(raw_field, 'temporary_file_path'):
|
||||||
# This file has a file path that we can move.
|
# This file has a file path that we can move.
|
||||||
raw_field.close()
|
raw_field.close()
|
||||||
file_move_safe(raw_field.temporary_file_path(), full_filename)
|
file_move_safe(raw_field.temporary_file_path(), full_filename)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# This is a normal uploadedfile that we can stream.
|
# This is a normal uploadedfile that we can stream.
|
||||||
fp = open(full_filename, 'wb')
|
fp = open(full_filename, 'wb')
|
||||||
locks.lock(fp, locks.LOCK_EX)
|
locks.lock(fp, locks.LOCK_EX)
|
||||||
for chunk in raw_field.chunk():
|
for chunk in raw_field.chunks():
|
||||||
fp.write(chunk)
|
fp.write(chunk)
|
||||||
locks.unlock(fp)
|
locks.unlock(fp)
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
# Save the width and/or height, if applicable.
|
# Save the width and/or height, if applicable.
|
||||||
if isinstance(field, ImageField) and (field.width_field or field.height_field):
|
if isinstance(field, ImageField) and \
|
||||||
|
(field.width_field or field.height_field):
|
||||||
from django.utils.images import get_image_dimensions
|
from django.utils.images import get_image_dimensions
|
||||||
width, height = get_image_dimensions(full_filename)
|
width, height = get_image_dimensions(full_filename)
|
||||||
if field.width_field:
|
if field.width_field:
|
||||||
@ -554,7 +549,7 @@ class Model(object):
|
|||||||
if field.height_field:
|
if field.height_field:
|
||||||
setattr(self, field.height_field, height)
|
setattr(self, field.height_field, height)
|
||||||
|
|
||||||
# Save the object because it has changed unless save is False
|
# Save the object because it has changed, unless save is False.
|
||||||
if save:
|
if save:
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@ -574,6 +569,7 @@ class Model(object):
|
|||||||
setattr(self, cachename, get_image_dimensions(filename))
|
setattr(self, cachename, get_image_dimensions(filename))
|
||||||
return getattr(self, cachename)
|
return getattr(self, cachename)
|
||||||
|
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
|
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
|
||||||
############################################
|
############################################
|
||||||
@ -589,6 +585,7 @@ def method_set_order(ordered_obj, self, id_list):
|
|||||||
ordered_obj.objects.filter(**{'pk': j, order_name: rel_val}).update(_order=i)
|
ordered_obj.objects.filter(**{'pk': j, order_name: rel_val}).update(_order=i)
|
||||||
transaction.commit_unless_managed()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
|
|
||||||
def method_get_order(ordered_obj, self):
|
def method_get_order(ordered_obj, self):
|
||||||
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
|
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
|
||||||
order_name = ordered_obj._meta.order_with_respect_to.name
|
order_name = ordered_obj._meta.order_with_respect_to.name
|
||||||
@ -596,6 +593,7 @@ def method_get_order(ordered_obj, self):
|
|||||||
return [r[pk_name] for r in
|
return [r[pk_name] for r in
|
||||||
ordered_obj.objects.filter(**{order_name: rel_val}).values(pk_name)]
|
ordered_obj.objects.filter(**{order_name: rel_val}).values(pk_name)]
|
||||||
|
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
# HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) #
|
# HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) #
|
||||||
##############################################
|
##############################################
|
||||||
@ -603,6 +601,7 @@ def method_get_order(ordered_obj, self):
|
|||||||
def get_absolute_url(opts, func, self, *args, **kwargs):
|
def get_absolute_url(opts, func, self, *args, **kwargs):
|
||||||
return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self, *args, **kwargs)
|
return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
########
|
########
|
||||||
# MISC #
|
# MISC #
|
||||||
########
|
########
|
||||||
@ -614,8 +613,6 @@ if sys.version_info < (2, 5):
|
|||||||
# Prior to Python 2.5, Exception was an old-style class
|
# Prior to Python 2.5, Exception was an old-style class
|
||||||
def subclass_exception(name, parent, unused):
|
def subclass_exception(name, parent, unused):
|
||||||
return types.ClassType(name, (parent,), {})
|
return types.ClassType(name, (parent,), {})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
def subclass_exception(name, parent, module):
|
def subclass_exception(name, parent, module):
|
||||||
return type(name, (parent,), {'__module__': module})
|
return type(name, (parent,), {'__module__': module})
|
||||||
|
|
||||||
|
@ -330,7 +330,7 @@ class Field(object):
|
|||||||
params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))
|
params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))
|
||||||
if self.unique_for_year:
|
if self.unique_for_year:
|
||||||
params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))
|
params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))
|
||||||
if self.unique or (self.primary_key and not rel):
|
if self.unique and not rel:
|
||||||
params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
|
params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
|
||||||
|
|
||||||
# Only add is_required=True if the field cannot be blank. Primary keys
|
# Only add is_required=True if the field cannot be blank. Primary keys
|
||||||
@ -558,7 +558,7 @@ class DateField(Field):
|
|||||||
raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.')
|
raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.')
|
||||||
|
|
||||||
def get_db_prep_lookup(self, lookup_type, value):
|
def get_db_prep_lookup(self, lookup_type, value):
|
||||||
if lookup_type == 'range':
|
if lookup_type in ('range', 'in'):
|
||||||
value = [smart_unicode(v) for v in value]
|
value = [smart_unicode(v) for v in value]
|
||||||
elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'):
|
elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'):
|
||||||
value = value.strftime('%Y-%m-%d')
|
value = value.strftime('%Y-%m-%d')
|
||||||
@ -645,7 +645,7 @@ class DateTimeField(DateField):
|
|||||||
return Field.get_db_prep_save(self, value)
|
return Field.get_db_prep_save(self, value)
|
||||||
|
|
||||||
def get_db_prep_lookup(self, lookup_type, value):
|
def get_db_prep_lookup(self, lookup_type, value):
|
||||||
if lookup_type == 'range':
|
if lookup_type in ('range', 'in'):
|
||||||
value = [smart_unicode(v) for v in value]
|
value = [smart_unicode(v) for v in value]
|
||||||
else:
|
else:
|
||||||
value = smart_unicode(value)
|
value = smart_unicode(value)
|
||||||
@ -724,7 +724,7 @@ class DecimalField(Field):
|
|||||||
return super(DecimalField, self).get_db_prep_save(value)
|
return super(DecimalField, self).get_db_prep_save(value)
|
||||||
|
|
||||||
def get_db_prep_lookup(self, lookup_type, value):
|
def get_db_prep_lookup(self, lookup_type, value):
|
||||||
if lookup_type == 'range':
|
if lookup_type in ('range', 'in'):
|
||||||
value = [self._format(v) for v in value]
|
value = [self._format(v) for v in value]
|
||||||
else:
|
else:
|
||||||
value = self._format(value)
|
value = self._format(value)
|
||||||
@ -770,8 +770,11 @@ class FileField(Field):
|
|||||||
def get_db_prep_save(self, value):
|
def get_db_prep_save(self, value):
|
||||||
"Returns field's value prepared for saving into a database."
|
"Returns field's value prepared for saving into a database."
|
||||||
# Need to convert UploadedFile objects provided via a form to unicode for database insertion
|
# Need to convert UploadedFile objects provided via a form to unicode for database insertion
|
||||||
if value is None:
|
if hasattr(value, 'name'):
|
||||||
|
return value.name
|
||||||
|
elif value is None:
|
||||||
return None
|
return None
|
||||||
|
else:
|
||||||
return unicode(value)
|
return unicode(value)
|
||||||
|
|
||||||
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
|
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
|
||||||
@ -836,20 +839,23 @@ class FileField(Field):
|
|||||||
def save_file(self, new_data, new_object, original_object, change, rel, save=True):
|
def save_file(self, new_data, new_object, original_object, change, rel, save=True):
|
||||||
upload_field_name = self.get_manipulator_field_names('')[0]
|
upload_field_name = self.get_manipulator_field_names('')[0]
|
||||||
if new_data.get(upload_field_name, False):
|
if new_data.get(upload_field_name, False):
|
||||||
func = getattr(new_object, 'save_%s_file' % self.name)
|
|
||||||
if rel:
|
if rel:
|
||||||
file = new_data[upload_field_name][0]
|
file = new_data[upload_field_name][0]
|
||||||
else:
|
else:
|
||||||
file = new_data[upload_field_name]
|
file = new_data[upload_field_name]
|
||||||
|
|
||||||
|
if not file:
|
||||||
|
return
|
||||||
|
|
||||||
# Backwards-compatible support for files-as-dictionaries.
|
# Backwards-compatible support for files-as-dictionaries.
|
||||||
# We don't need to raise a warning because Model._save_FIELD_file will
|
# We don't need to raise a warning because Model._save_FIELD_file will
|
||||||
# do so for us.
|
# do so for us.
|
||||||
try:
|
try:
|
||||||
file_name = file.file_name
|
file_name = file.name
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
file_name = file['filename']
|
file_name = file['filename']
|
||||||
|
|
||||||
|
func = getattr(new_object, 'save_%s_file' % self.name)
|
||||||
func(file_name, file, save)
|
func(file_name, file, save)
|
||||||
|
|
||||||
def get_directory_name(self):
|
def get_directory_name(self):
|
||||||
@ -861,9 +867,9 @@ class FileField(Field):
|
|||||||
return os.path.normpath(f)
|
return os.path.normpath(f)
|
||||||
|
|
||||||
def save_form_data(self, instance, data):
|
def save_form_data(self, instance, data):
|
||||||
from django.newforms.fields import UploadedFile
|
from django.core.files.uploadedfile import UploadedFile
|
||||||
if data and isinstance(data, UploadedFile):
|
if data and isinstance(data, UploadedFile):
|
||||||
getattr(instance, "save_%s_file" % self.name)(data.filename, data.data, save=False)
|
getattr(instance, "save_%s_file" % self.name)(data.name, data, save=False)
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
defaults = {'form_class': forms.FileField}
|
defaults = {'form_class': forms.FileField}
|
||||||
@ -1098,7 +1104,7 @@ class TimeField(Field):
|
|||||||
return smart_unicode(value)
|
return smart_unicode(value)
|
||||||
else:
|
else:
|
||||||
prep = smart_unicode
|
prep = smart_unicode
|
||||||
if lookup_type == 'range':
|
if lookup_type in ('range', 'in'):
|
||||||
value = [prep(v) for v in value]
|
value = [prep(v) for v in value]
|
||||||
else:
|
else:
|
||||||
value = prep(value)
|
value = prep(value)
|
||||||
|
@ -1,31 +1,41 @@
|
|||||||
import warnings
|
import warnings
|
||||||
|
try:
|
||||||
|
set
|
||||||
|
except NameError:
|
||||||
|
from sets import Set as set # Python 2.3 fallback
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import connection, transaction, IntegrityError
|
from django.db import connection, transaction, IntegrityError
|
||||||
from django.db.models.fields import DateField, FieldDoesNotExist
|
from django.db.models.fields import DateField
|
||||||
from django.db.models.query_utils import Q, select_related_descend
|
from django.db.models.query_utils import Q, select_related_descend
|
||||||
from django.db.models import signals, sql
|
from django.db.models import signals, sql
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
|
|
||||||
|
|
||||||
# Used to control how many objects are worked with at once in some cases (e.g.
|
# Used to control how many objects are worked with at once in some cases (e.g.
|
||||||
# when deleting objects).
|
# when deleting objects).
|
||||||
CHUNK_SIZE = 100
|
CHUNK_SIZE = 100
|
||||||
ITER_CHUNK_SIZE = CHUNK_SIZE
|
ITER_CHUNK_SIZE = CHUNK_SIZE
|
||||||
|
|
||||||
# Pull into this namespace for backwards compatibility
|
# Pull into this namespace for backwards compatibility.
|
||||||
EmptyResultSet = sql.EmptyResultSet
|
EmptyResultSet = sql.EmptyResultSet
|
||||||
|
|
||||||
|
|
||||||
class CyclicDependency(Exception):
|
class CyclicDependency(Exception):
|
||||||
|
"""
|
||||||
|
An error when dealing with a collection of objects that have a cyclic
|
||||||
|
dependency, i.e. when deleting multiple objects.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CollectedObjects(object):
|
class CollectedObjects(object):
|
||||||
"""
|
"""
|
||||||
A container that stores keys and lists of values along with
|
A container that stores keys and lists of values along with remembering the
|
||||||
remembering the parent objects for all the keys.
|
parent objects for all the keys.
|
||||||
|
|
||||||
This is used for the database object deletion routines so that we
|
This is used for the database object deletion routines so that we can
|
||||||
can calculate the 'leaf' objects which should be deleted first.
|
calculate the 'leaf' objects which should be deleted first.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -34,26 +44,27 @@ class CollectedObjects(object):
|
|||||||
|
|
||||||
def add(self, model, pk, obj, parent_model, nullable=False):
|
def add(self, model, pk, obj, parent_model, nullable=False):
|
||||||
"""
|
"""
|
||||||
Adds an item.
|
Adds an item to the container.
|
||||||
model is the class of the object being added,
|
|
||||||
pk is the primary key, obj is the object itself,
|
|
||||||
parent_model is the model of the parent object
|
|
||||||
that this object was reached through, nullable should
|
|
||||||
be True if this relation is nullable.
|
|
||||||
|
|
||||||
If the item already existed in the structure,
|
Arguments:
|
||||||
returns true, otherwise false.
|
* model - the class of the object being added.
|
||||||
|
* pk - the primary key.
|
||||||
|
* obj - the object itself.
|
||||||
|
* parent_model - the model of the parent object that this object was
|
||||||
|
reached through.
|
||||||
|
* nullable - should be True if this relation is nullable.
|
||||||
|
|
||||||
|
Returns True if the item already existed in the structure and
|
||||||
|
False otherwise.
|
||||||
"""
|
"""
|
||||||
d = self.data.setdefault(model, SortedDict())
|
d = self.data.setdefault(model, SortedDict())
|
||||||
retval = pk in d
|
retval = pk in d
|
||||||
d[pk] = obj
|
d[pk] = obj
|
||||||
# Nullable relationships can be ignored -- they
|
# Nullable relationships can be ignored -- they are nulled out before
|
||||||
# are nulled out before deleting, and therefore
|
# deleting, and therefore do not affect the order in which objects
|
||||||
# do not affect the order in which objects have
|
# have to be deleted.
|
||||||
# to be deleted.
|
|
||||||
if parent_model is not None and not nullable:
|
if parent_model is not None and not nullable:
|
||||||
self.children.setdefault(parent_model, []).append(model)
|
self.children.setdefault(parent_model, []).append(model)
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
@ -77,8 +88,8 @@ class CollectedObjects(object):
|
|||||||
|
|
||||||
def ordered_keys(self):
|
def ordered_keys(self):
|
||||||
"""
|
"""
|
||||||
Returns the models in the order that they should be
|
Returns the models in the order that they should be dealt with (i.e.
|
||||||
dealth with i.e. models with no dependencies first.
|
models with no dependencies first).
|
||||||
"""
|
"""
|
||||||
dealt_with = SortedDict()
|
dealt_with = SortedDict()
|
||||||
# Start with items that have no children
|
# Start with items that have no children
|
||||||
@ -91,19 +102,22 @@ class CollectedObjects(object):
|
|||||||
dealt_with[model] = None
|
dealt_with[model] = None
|
||||||
found = True
|
found = True
|
||||||
if not found:
|
if not found:
|
||||||
raise CyclicDependency("There is a cyclic dependency of items to be processed.")
|
raise CyclicDependency(
|
||||||
|
"There is a cyclic dependency of items to be processed.")
|
||||||
|
|
||||||
return dealt_with.keys()
|
return dealt_with.keys()
|
||||||
|
|
||||||
def unordered_keys(self):
|
def unordered_keys(self):
|
||||||
"""
|
"""
|
||||||
Fallback for the case where is a cyclic dependency but we
|
Fallback for the case where is a cyclic dependency but we don't care.
|
||||||
don't care.
|
|
||||||
"""
|
"""
|
||||||
return self.data.keys()
|
return self.data.keys()
|
||||||
|
|
||||||
|
|
||||||
class QuerySet(object):
|
class QuerySet(object):
|
||||||
"Represents a lazy database lookup for a set of objects"
|
"""
|
||||||
|
Represents a lazy database lookup for a set of objects.
|
||||||
|
"""
|
||||||
def __init__(self, model=None, query=None):
|
def __init__(self, model=None, query=None):
|
||||||
self.model = model
|
self.model = model
|
||||||
self.query = query or sql.Query(self.model, connection)
|
self.query = query or sql.Query(self.model, connection)
|
||||||
@ -116,7 +130,7 @@ class QuerySet(object):
|
|||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
"""
|
"""
|
||||||
Allows the Queryset to be pickled.
|
Allows the QuerySet to be pickled.
|
||||||
"""
|
"""
|
||||||
# Force the cache to be fully populated.
|
# Force the cache to be fully populated.
|
||||||
len(self)
|
len(self)
|
||||||
@ -131,7 +145,7 @@ class QuerySet(object):
|
|||||||
def __len__(self):
|
def __len__(self):
|
||||||
# Since __len__ is called quite frequently (for example, as part of
|
# Since __len__ is called quite frequently (for example, as part of
|
||||||
# list(qs), we make some effort here to be as efficient as possible
|
# list(qs), we make some effort here to be as efficient as possible
|
||||||
# whilst not messing up any existing iterators against the queryset.
|
# whilst not messing up any existing iterators against the QuerySet.
|
||||||
if self._result_cache is None:
|
if self._result_cache is None:
|
||||||
if self._iter:
|
if self._iter:
|
||||||
self._result_cache = list(self._iter)
|
self._result_cache = list(self._iter)
|
||||||
@ -173,7 +187,9 @@ class QuerySet(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def __getitem__(self, k):
|
def __getitem__(self, k):
|
||||||
"Retrieve an item or slice from the set of results."
|
"""
|
||||||
|
Retrieves an item or slice from the set of results.
|
||||||
|
"""
|
||||||
if not isinstance(k, (slice, int, long)):
|
if not isinstance(k, (slice, int, long)):
|
||||||
raise TypeError
|
raise TypeError
|
||||||
assert ((not isinstance(k, slice) and (k >= 0))
|
assert ((not isinstance(k, slice) and (k >= 0))
|
||||||
@ -264,7 +280,7 @@ class QuerySet(object):
|
|||||||
Performs a SELECT COUNT() and returns the number of records as an
|
Performs a SELECT COUNT() and returns the number of records as an
|
||||||
integer.
|
integer.
|
||||||
|
|
||||||
If the queryset is already cached (i.e. self._result_cache is set) this
|
If the QuerySet is already cached (i.e. self._result_cache is set) this
|
||||||
simply returns the length of the cached results set to avoid multiple
|
simply returns the length of the cached results set to avoid multiple
|
||||||
SELECT COUNT(*) calls.
|
SELECT COUNT(*) calls.
|
||||||
"""
|
"""
|
||||||
@ -290,7 +306,7 @@ class QuerySet(object):
|
|||||||
|
|
||||||
def create(self, **kwargs):
|
def create(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Create a new object with the given kwargs, saving it to the database
|
Creates a new object with the given kwargs, saving it to the database
|
||||||
and returning the created object.
|
and returning the created object.
|
||||||
"""
|
"""
|
||||||
obj = self.model(**kwargs)
|
obj = self.model(**kwargs)
|
||||||
@ -425,8 +441,8 @@ class QuerySet(object):
|
|||||||
|
|
||||||
def dates(self, field_name, kind, order='ASC'):
|
def dates(self, field_name, kind, order='ASC'):
|
||||||
"""
|
"""
|
||||||
Returns a list of datetime objects representing all available dates
|
Returns a list of datetime objects representing all available dates for
|
||||||
for the given field_name, scoped to 'kind'.
|
the given field_name, scoped to 'kind'.
|
||||||
"""
|
"""
|
||||||
assert kind in ("month", "year", "day"), \
|
assert kind in ("month", "year", "day"), \
|
||||||
"'kind' must be one of 'year', 'month' or 'day'."
|
"'kind' must be one of 'year', 'month' or 'day'."
|
||||||
@ -441,7 +457,7 @@ class QuerySet(object):
|
|||||||
|
|
||||||
def none(self):
|
def none(self):
|
||||||
"""
|
"""
|
||||||
Returns an empty queryset.
|
Returns an empty QuerySet.
|
||||||
"""
|
"""
|
||||||
return self._clone(klass=EmptyQuerySet)
|
return self._clone(klass=EmptyQuerySet)
|
||||||
|
|
||||||
@ -485,6 +501,7 @@ class QuerySet(object):
|
|||||||
def complex_filter(self, filter_obj):
|
def complex_filter(self, filter_obj):
|
||||||
"""
|
"""
|
||||||
Returns a new QuerySet instance with filter_obj added to the filters.
|
Returns a new QuerySet instance with filter_obj added to the filters.
|
||||||
|
|
||||||
filter_obj can be a Q object (or anything with an add_to_query()
|
filter_obj can be a Q object (or anything with an add_to_query()
|
||||||
method) or a dictionary of keyword lookup arguments.
|
method) or a dictionary of keyword lookup arguments.
|
||||||
|
|
||||||
@ -500,8 +517,9 @@ class QuerySet(object):
|
|||||||
|
|
||||||
def select_related(self, *fields, **kwargs):
|
def select_related(self, *fields, **kwargs):
|
||||||
"""
|
"""
|
||||||
Returns a new QuerySet instance that will select related objects. If
|
Returns a new QuerySet instance that will select related objects.
|
||||||
fields are specified, they must be ForeignKey fields and only those
|
|
||||||
|
If fields are specified, they must be ForeignKey fields and only those
|
||||||
related objects are included in the selection.
|
related objects are included in the selection.
|
||||||
"""
|
"""
|
||||||
depth = kwargs.pop('depth', 0)
|
depth = kwargs.pop('depth', 0)
|
||||||
@ -521,13 +539,15 @@ class QuerySet(object):
|
|||||||
|
|
||||||
def dup_select_related(self, other):
|
def dup_select_related(self, other):
|
||||||
"""
|
"""
|
||||||
Copies the related selection status from the queryset 'other' to the
|
Copies the related selection status from the QuerySet 'other' to the
|
||||||
current queryset.
|
current QuerySet.
|
||||||
"""
|
"""
|
||||||
self.query.select_related = other.query.select_related
|
self.query.select_related = other.query.select_related
|
||||||
|
|
||||||
def order_by(self, *field_names):
|
def order_by(self, *field_names):
|
||||||
"""Returns a new QuerySet instance with the ordering changed."""
|
"""
|
||||||
|
Returns a new QuerySet instance with the ordering changed.
|
||||||
|
"""
|
||||||
assert self.query.can_filter(), \
|
assert self.query.can_filter(), \
|
||||||
"Cannot reorder a query once a slice has been taken."
|
"Cannot reorder a query once a slice has been taken."
|
||||||
obj = self._clone()
|
obj = self._clone()
|
||||||
@ -546,7 +566,7 @@ class QuerySet(object):
|
|||||||
def extra(self, select=None, where=None, params=None, tables=None,
|
def extra(self, select=None, where=None, params=None, tables=None,
|
||||||
order_by=None, select_params=None):
|
order_by=None, select_params=None):
|
||||||
"""
|
"""
|
||||||
Add extra SQL fragments to the query.
|
Adds extra SQL fragments to the query.
|
||||||
"""
|
"""
|
||||||
assert self.query.can_filter(), \
|
assert self.query.can_filter(), \
|
||||||
"Cannot change a query once a slice has been taken"
|
"Cannot change a query once a slice has been taken"
|
||||||
@ -556,7 +576,7 @@ class QuerySet(object):
|
|||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
"""
|
"""
|
||||||
Reverses the ordering of the queryset.
|
Reverses the ordering of the QuerySet.
|
||||||
"""
|
"""
|
||||||
clone = self._clone()
|
clone = self._clone()
|
||||||
clone.query.standard_ordering = not clone.query.standard_ordering
|
clone.query.standard_ordering = not clone.query.standard_ordering
|
||||||
@ -589,12 +609,13 @@ class QuerySet(object):
|
|||||||
|
|
||||||
def _merge_sanity_check(self, other):
|
def _merge_sanity_check(self, other):
|
||||||
"""
|
"""
|
||||||
Checks that we are merging two comparable queryset classes. By default
|
Checks that we are merging two comparable QuerySet classes. By default
|
||||||
this does nothing, but see the ValuesQuerySet for an example of where
|
this does nothing, but see the ValuesQuerySet for an example of where
|
||||||
it's useful.
|
it's useful.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ValuesQuerySet(QuerySet):
|
class ValuesQuerySet(QuerySet):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(ValuesQuerySet, self).__init__(*args, **kwargs)
|
super(ValuesQuerySet, self).__init__(*args, **kwargs)
|
||||||
@ -617,7 +638,7 @@ class ValuesQuerySet(QuerySet):
|
|||||||
Constructs the field_names list that the values query will be
|
Constructs the field_names list that the values query will be
|
||||||
retrieving.
|
retrieving.
|
||||||
|
|
||||||
Called by the _clone() method after initialising the rest of the
|
Called by the _clone() method after initializing the rest of the
|
||||||
instance.
|
instance.
|
||||||
"""
|
"""
|
||||||
self.extra_names = []
|
self.extra_names = []
|
||||||
@ -658,6 +679,7 @@ class ValuesQuerySet(QuerySet):
|
|||||||
raise TypeError("Merging '%s' classes must involve the same values in each case."
|
raise TypeError("Merging '%s' classes must involve the same values in each case."
|
||||||
% self.__class__.__name__)
|
% self.__class__.__name__)
|
||||||
|
|
||||||
|
|
||||||
class ValuesListQuerySet(ValuesQuerySet):
|
class ValuesListQuerySet(ValuesQuerySet):
|
||||||
def iterator(self):
|
def iterator(self):
|
||||||
self.query.trim_extra_select(self.extra_names)
|
self.query.trim_extra_select(self.extra_names)
|
||||||
@ -681,6 +703,7 @@ class ValuesListQuerySet(ValuesQuerySet):
|
|||||||
clone.flat = self.flat
|
clone.flat = self.flat
|
||||||
return clone
|
return clone
|
||||||
|
|
||||||
|
|
||||||
class DateQuerySet(QuerySet):
|
class DateQuerySet(QuerySet):
|
||||||
def iterator(self):
|
def iterator(self):
|
||||||
return self.query.results_iter()
|
return self.query.results_iter()
|
||||||
@ -689,7 +712,7 @@ class DateQuerySet(QuerySet):
|
|||||||
"""
|
"""
|
||||||
Sets up any special features of the query attribute.
|
Sets up any special features of the query attribute.
|
||||||
|
|
||||||
Called by the _clone() method after initialising the rest of the
|
Called by the _clone() method after initializing the rest of the
|
||||||
instance.
|
instance.
|
||||||
"""
|
"""
|
||||||
self.query = self.query.clone(klass=sql.DateQuery, setup=True)
|
self.query = self.query.clone(klass=sql.DateQuery, setup=True)
|
||||||
@ -706,6 +729,7 @@ class DateQuerySet(QuerySet):
|
|||||||
c._setup_query()
|
c._setup_query()
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
|
||||||
class EmptyQuerySet(QuerySet):
|
class EmptyQuerySet(QuerySet):
|
||||||
def __init__(self, model=None, query=None):
|
def __init__(self, model=None, query=None):
|
||||||
super(EmptyQuerySet, self).__init__(model, query)
|
super(EmptyQuerySet, self).__init__(model, query)
|
||||||
@ -733,6 +757,7 @@ class EmptyQuerySet(QuerySet):
|
|||||||
# (it raises StopIteration immediately).
|
# (it raises StopIteration immediately).
|
||||||
yield iter([]).next()
|
yield iter([]).next()
|
||||||
|
|
||||||
|
|
||||||
# QOperator, QNot, QAnd and QOr are temporarily retained for backwards
|
# QOperator, QNot, QAnd and QOr are temporarily retained for backwards
|
||||||
# compatibility. All the old functionality is now part of the 'Q' class.
|
# compatibility. All the old functionality is now part of the 'Q' class.
|
||||||
class QOperator(Q):
|
class QOperator(Q):
|
||||||
@ -743,10 +768,12 @@ class QOperator(Q):
|
|||||||
|
|
||||||
QOr = QAnd = QOperator
|
QOr = QAnd = QOperator
|
||||||
|
|
||||||
|
|
||||||
def QNot(q):
|
def QNot(q):
|
||||||
warnings.warn('Use ~q instead of QNot(q)', DeprecationWarning, stacklevel=2)
|
warnings.warn('Use ~q instead of QNot(q)', DeprecationWarning, stacklevel=2)
|
||||||
return ~q
|
return ~q
|
||||||
|
|
||||||
|
|
||||||
def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
|
def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
|
||||||
requested=None):
|
requested=None):
|
||||||
"""
|
"""
|
||||||
@ -774,6 +801,7 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
|
|||||||
setattr(obj, f.get_cache_name(), rel_obj)
|
setattr(obj, f.get_cache_name(), rel_obj)
|
||||||
return obj, index_end
|
return obj, index_end
|
||||||
|
|
||||||
|
|
||||||
def delete_objects(seen_objs):
|
def delete_objects(seen_objs):
|
||||||
"""
|
"""
|
||||||
Iterate through a list of seen classes, and remove any instances that are
|
Iterate through a list of seen classes, and remove any instances that are
|
||||||
@ -782,10 +810,10 @@ def delete_objects(seen_objs):
|
|||||||
try:
|
try:
|
||||||
ordered_classes = seen_objs.keys()
|
ordered_classes = seen_objs.keys()
|
||||||
except CyclicDependency:
|
except CyclicDependency:
|
||||||
# if there is a cyclic dependency, we cannot in general delete
|
# If there is a cyclic dependency, we cannot in general delete the
|
||||||
# the objects. However, if an appropriate transaction is set
|
# objects. However, if an appropriate transaction is set up, or if the
|
||||||
# up, or if the database is lax enough, it will succeed.
|
# database is lax enough, it will succeed. So for now, we go ahead and
|
||||||
# So for now, we go ahead and try anway.
|
# try anyway.
|
||||||
ordered_classes = seen_objs.unordered_keys()
|
ordered_classes = seen_objs.unordered_keys()
|
||||||
|
|
||||||
obj_pairs = {}
|
obj_pairs = {}
|
||||||
@ -794,7 +822,7 @@ def delete_objects(seen_objs):
|
|||||||
items.sort()
|
items.sort()
|
||||||
obj_pairs[cls] = items
|
obj_pairs[cls] = items
|
||||||
|
|
||||||
# Pre notify all instances to be deleted
|
# Pre-notify all instances to be deleted.
|
||||||
for pk_val, instance in items:
|
for pk_val, instance in items:
|
||||||
dispatcher.send(signal=signals.pre_delete, sender=cls,
|
dispatcher.send(signal=signals.pre_delete, sender=cls,
|
||||||
instance=instance)
|
instance=instance)
|
||||||
@ -808,7 +836,7 @@ def delete_objects(seen_objs):
|
|||||||
if field.rel and field.null and field.rel.to in seen_objs:
|
if field.rel and field.null and field.rel.to in seen_objs:
|
||||||
update_query.clear_related(field, pk_list)
|
update_query.clear_related(field, pk_list)
|
||||||
|
|
||||||
# Now delete the actual data
|
# Now delete the actual data.
|
||||||
for cls in ordered_classes:
|
for cls in ordered_classes:
|
||||||
items = obj_pairs[cls]
|
items = obj_pairs[cls]
|
||||||
items.reverse()
|
items.reverse()
|
||||||
@ -831,6 +859,7 @@ def delete_objects(seen_objs):
|
|||||||
|
|
||||||
transaction.commit_unless_managed()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
|
|
||||||
def insert_query(model, values, return_id=False, raw_values=False):
|
def insert_query(model, values, return_id=False, raw_values=False):
|
||||||
"""
|
"""
|
||||||
Inserts a new record for the given model. This provides an interface to
|
Inserts a new record for the given model. This provides an interface to
|
||||||
@ -840,4 +869,3 @@ def insert_query(model, values, return_id=False, raw_values=False):
|
|||||||
query = sql.InsertQuery(model, connection)
|
query = sql.InsertQuery(model, connection)
|
||||||
query.insert_values(values, raw_values)
|
query.insert_values(values, raw_values)
|
||||||
return query.execute_sql(return_id)
|
return query.execute_sql(return_id)
|
||||||
|
|
||||||
|
@ -283,12 +283,11 @@ class Query(object):
|
|||||||
if ordering:
|
if ordering:
|
||||||
result.append('ORDER BY %s' % ', '.join(ordering))
|
result.append('ORDER BY %s' % ', '.join(ordering))
|
||||||
|
|
||||||
# FIXME: Pull this out to make life easier for Oracle et al.
|
|
||||||
if with_limits:
|
if with_limits:
|
||||||
if self.high_mark:
|
if self.high_mark is not None:
|
||||||
result.append('LIMIT %d' % (self.high_mark - self.low_mark))
|
result.append('LIMIT %d' % (self.high_mark - self.low_mark))
|
||||||
if self.low_mark:
|
if self.low_mark:
|
||||||
if not self.high_mark:
|
if self.high_mark is None:
|
||||||
val = self.connection.ops.no_limit_value()
|
val = self.connection.ops.no_limit_value()
|
||||||
if val:
|
if val:
|
||||||
result.append('LIMIT %d' % val)
|
result.append('LIMIT %d' % val)
|
||||||
@ -1381,12 +1380,12 @@ class Query(object):
|
|||||||
constraints. So low is added to the current low value and both will be
|
constraints. So low is added to the current low value and both will be
|
||||||
clamped to any existing high value.
|
clamped to any existing high value.
|
||||||
"""
|
"""
|
||||||
if high:
|
if high is not None:
|
||||||
if self.high_mark:
|
if self.high_mark:
|
||||||
self.high_mark = min(self.high_mark, self.low_mark + high)
|
self.high_mark = min(self.high_mark, self.low_mark + high)
|
||||||
else:
|
else:
|
||||||
self.high_mark = self.low_mark + high
|
self.high_mark = self.low_mark + high
|
||||||
if low:
|
if low is not None:
|
||||||
if self.high_mark:
|
if self.high_mark:
|
||||||
self.low_mark = min(self.high_mark, self.low_mark + low)
|
self.low_mark = min(self.high_mark, self.low_mark + low)
|
||||||
else:
|
else:
|
||||||
|
@ -136,6 +136,7 @@ class MultiPartParser(object):
|
|||||||
# since we cannot be sure a file is complete until
|
# since we cannot be sure a file is complete until
|
||||||
# we hit the next boundary/part of the multipart content.
|
# we hit the next boundary/part of the multipart content.
|
||||||
self.handle_file_complete(old_field_name, counters)
|
self.handle_file_complete(old_field_name, counters)
|
||||||
|
old_field_name = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
disposition = meta_data['content-disposition'][1]
|
disposition = meta_data['content-disposition'][1]
|
||||||
@ -269,24 +270,9 @@ class LazyStream(object):
|
|||||||
self._empty = False
|
self._empty = False
|
||||||
self._leftover = ''
|
self._leftover = ''
|
||||||
self.length = length
|
self.length = length
|
||||||
self._position = 0
|
self.position = 0
|
||||||
self._remaining = length
|
self._remaining = length
|
||||||
|
self._unget_history = []
|
||||||
# These fields are to do sanity checking to make sure we don't
|
|
||||||
# have infinite loops getting/ungetting from the stream. The
|
|
||||||
# purpose overall is to raise an exception if we perform lots
|
|
||||||
# of stream get/unget gymnastics without getting
|
|
||||||
# anywhere. Naturally this is not sound, but most probably
|
|
||||||
# would indicate a bug if the exception is raised.
|
|
||||||
|
|
||||||
# largest position tell us how far this lazystream has ever
|
|
||||||
# been advanced
|
|
||||||
self._largest_position = 0
|
|
||||||
|
|
||||||
# "modifications since" will start at zero and increment every
|
|
||||||
# time the position is modified but a new largest position is
|
|
||||||
# not achieved.
|
|
||||||
self._modifications_since = 0
|
|
||||||
|
|
||||||
def tell(self):
|
def tell(self):
|
||||||
return self.position
|
return self.position
|
||||||
@ -328,6 +314,7 @@ class LazyStream(object):
|
|||||||
self._leftover = ''
|
self._leftover = ''
|
||||||
else:
|
else:
|
||||||
output = self._producer.next()
|
output = self._producer.next()
|
||||||
|
self._unget_history = []
|
||||||
self.position += len(output)
|
self.position += len(output)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@ -350,26 +337,31 @@ class LazyStream(object):
|
|||||||
Future calls to read() will return those bytes first. The
|
Future calls to read() will return those bytes first. The
|
||||||
stream position and thus tell() will be rewound.
|
stream position and thus tell() will be rewound.
|
||||||
"""
|
"""
|
||||||
|
if not bytes:
|
||||||
|
return
|
||||||
|
self._update_unget_history(len(bytes))
|
||||||
self.position -= len(bytes)
|
self.position -= len(bytes)
|
||||||
self._leftover = ''.join([bytes, self._leftover])
|
self._leftover = ''.join([bytes, self._leftover])
|
||||||
|
|
||||||
def _set_position(self, value):
|
def _update_unget_history(self, num_bytes):
|
||||||
if value > self._largest_position:
|
"""
|
||||||
self._modifications_since = 0
|
Updates the unget history as a sanity check to see if we've pushed
|
||||||
self._largest_position = value
|
back the same number of bytes in one chunk. If we keep ungetting the
|
||||||
else:
|
same number of bytes many times (here, 50), we're mostly likely in an
|
||||||
self._modifications_since += 1
|
infinite loop of some sort. This is usually caused by a
|
||||||
if self._modifications_since > 500:
|
maliciously-malformed MIME request.
|
||||||
|
"""
|
||||||
|
self._unget_history = [num_bytes] + self._unget_history[:49]
|
||||||
|
number_equal = len([current_number for current_number in self._unget_history
|
||||||
|
if current_number == num_bytes])
|
||||||
|
|
||||||
|
if number_equal > 40:
|
||||||
raise SuspiciousOperation(
|
raise SuspiciousOperation(
|
||||||
"The multipart parser got stuck, which shouldn't happen with"
|
"The multipart parser got stuck, which shouldn't happen with"
|
||||||
" normal uploaded files. Check for malicious upload activity;"
|
" normal uploaded files. Check for malicious upload activity;"
|
||||||
" if there is none, report this to the Django developers."
|
" if there is none, report this to the Django developers."
|
||||||
)
|
)
|
||||||
|
|
||||||
self._position = value
|
|
||||||
|
|
||||||
position = property(lambda self: self._position, _set_position)
|
|
||||||
|
|
||||||
class ChunkIter(object):
|
class ChunkIter(object):
|
||||||
"""
|
"""
|
||||||
An iterable that will yield chunks of data. Given a file-like object as the
|
An iterable that will yield chunks of data. Given a file-like object as the
|
||||||
|
@ -31,3 +31,54 @@ def conditional_content_removal(request, response):
|
|||||||
if request.method == 'HEAD':
|
if request.method == 'HEAD':
|
||||||
response.content = ''
|
response.content = ''
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def fix_IE_for_attach(request, response):
|
||||||
|
"""
|
||||||
|
This function will prevent Django from serving a Content-Disposition header
|
||||||
|
while expecting the browser to cache it (only when the browser is IE). This
|
||||||
|
leads to IE not allowing the client to download.
|
||||||
|
"""
|
||||||
|
if 'MSIE' not in request.META.get('HTTP_USER_AGENT', '').upper():
|
||||||
|
return response
|
||||||
|
|
||||||
|
offending_headers = ('no-cache', 'no-store')
|
||||||
|
if response.has_header('Content-Disposition'):
|
||||||
|
try:
|
||||||
|
del response['Pragma']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
if response.has_header('Cache-Control'):
|
||||||
|
cache_control_values = [value.strip() for value in
|
||||||
|
response['Cache-Control'].split(',')
|
||||||
|
if value.strip().lower() not in offending_headers]
|
||||||
|
|
||||||
|
if not len(cache_control_values):
|
||||||
|
del response['Cache-Control']
|
||||||
|
else:
|
||||||
|
response['Cache-Control'] = ', '.join(cache_control_values)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def fix_IE_for_vary(request, response):
|
||||||
|
"""
|
||||||
|
This function will fix the bug reported at
|
||||||
|
http://support.microsoft.com/kb/824847/en-us?spid=8722&sid=global
|
||||||
|
by clearing the Vary header whenever the mime-type is not safe
|
||||||
|
enough for Internet Explorer to handle. Poor thing.
|
||||||
|
"""
|
||||||
|
if 'MSIE' not in request.META.get('HTTP_USER_AGENT', '').upper():
|
||||||
|
return response
|
||||||
|
|
||||||
|
# These mime-types that are decreed "Vary-safe" for IE:
|
||||||
|
safe_mime_types = ('text/html', 'text/plain', 'text/sgml')
|
||||||
|
|
||||||
|
# The first part of the Content-Type field will be the MIME type,
|
||||||
|
# everything after ';', such as character-set, can be ignored.
|
||||||
|
if response['Content-Type'].split(';')[0] not in safe_mime_types:
|
||||||
|
try:
|
||||||
|
del response['Vary']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str
|
|||||||
|
|
||||||
from util import ErrorList, ValidationError
|
from util import ErrorList, ValidationError
|
||||||
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput
|
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput
|
||||||
|
from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Field', 'CharField', 'IntegerField',
|
'Field', 'CharField', 'IntegerField',
|
||||||
@ -419,18 +419,6 @@ except ImportError:
|
|||||||
# It's OK if Django settings aren't configured.
|
# It's OK if Django settings aren't configured.
|
||||||
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
|
||||||
|
|
||||||
class UploadedFile(StrAndUnicode):
|
|
||||||
"A wrapper for files uploaded in a FileField"
|
|
||||||
def __init__(self, filename, data):
|
|
||||||
self.filename = filename
|
|
||||||
self.data = data
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
"""
|
|
||||||
The unicode representation is the filename, so that the pre-database-insertion
|
|
||||||
logic can use UploadedFile objects
|
|
||||||
"""
|
|
||||||
return self.filename
|
|
||||||
|
|
||||||
class FileField(Field):
|
class FileField(Field):
|
||||||
widget = FileInput
|
widget = FileInput
|
||||||
@ -460,15 +448,12 @@ class FileField(Field):
|
|||||||
category = DeprecationWarning,
|
category = DeprecationWarning,
|
||||||
stacklevel = 2
|
stacklevel = 2
|
||||||
)
|
)
|
||||||
|
data = UploadedFile(data['filename'], data['content'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
file_name = data.file_name
|
file_name = data.name
|
||||||
file_size = data.file_size
|
file_size = data.size
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
try:
|
|
||||||
file_name = data.get('filename')
|
|
||||||
file_size = bool(data['content'])
|
|
||||||
except (AttributeError, KeyError):
|
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'])
|
||||||
|
|
||||||
if not file_name:
|
if not file_name:
|
||||||
@ -476,7 +461,7 @@ class FileField(Field):
|
|||||||
if not file_size:
|
if not file_size:
|
||||||
raise ValidationError(self.error_messages['empty'])
|
raise ValidationError(self.error_messages['empty'])
|
||||||
|
|
||||||
return UploadedFile(file_name, data)
|
return data
|
||||||
|
|
||||||
class ImageField(FileField):
|
class ImageField(FileField):
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
@ -522,6 +507,8 @@ class ImageField(FileField):
|
|||||||
trial_image.verify()
|
trial_image.verify()
|
||||||
except Exception: # Python Imaging Library doesn't recognize it as an image
|
except Exception: # Python Imaging Library doesn't recognize it as an image
|
||||||
raise ValidationError(self.error_messages['invalid_image'])
|
raise ValidationError(self.error_messages['invalid_image'])
|
||||||
|
if hasattr(f, 'seek') and callable(f.seek):
|
||||||
|
f.seek(0)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
url_re = re.compile(
|
url_re = re.compile(
|
||||||
|
@ -56,7 +56,8 @@ class DeclarativeFieldsMetaclass(type):
|
|||||||
"""
|
"""
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
attrs['base_fields'] = get_declared_fields(bases, attrs)
|
attrs['base_fields'] = get_declared_fields(bases, attrs)
|
||||||
return type.__new__(cls, name, bases, attrs)
|
return super(DeclarativeFieldsMetaclass,
|
||||||
|
cls).__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
class BaseForm(StrAndUnicode):
|
class BaseForm(StrAndUnicode):
|
||||||
# This is the main implementation of all the Form logic. Note that this
|
# This is the main implementation of all the Form logic. Note that this
|
||||||
|
@ -213,18 +213,19 @@ class ModelFormOptions(object):
|
|||||||
|
|
||||||
|
|
||||||
class ModelFormMetaclass(type):
|
class ModelFormMetaclass(type):
|
||||||
def __new__(cls, name, bases, attrs,
|
def __new__(cls, name, bases, attrs):
|
||||||
formfield_callback=lambda f: f.formfield()):
|
formfield_callback = attrs.pop('formfield_callback',
|
||||||
|
lambda f: f.formfield())
|
||||||
try:
|
try:
|
||||||
parents = [b for b in bases if issubclass(b, ModelForm)]
|
parents = [b for b in bases if issubclass(b, ModelForm)]
|
||||||
except NameError:
|
except NameError:
|
||||||
# We are defining ModelForm itself.
|
# We are defining ModelForm itself.
|
||||||
parents = None
|
parents = None
|
||||||
if not parents:
|
new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases,
|
||||||
return super(ModelFormMetaclass, cls).__new__(cls, name, bases,
|
|
||||||
attrs)
|
attrs)
|
||||||
|
if not parents:
|
||||||
|
return new_class
|
||||||
|
|
||||||
new_class = type.__new__(cls, name, bases, attrs)
|
|
||||||
declared_fields = get_declared_fields(bases, attrs, False)
|
declared_fields = get_declared_fields(bases, attrs, False)
|
||||||
opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
|
opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
|
||||||
if opts.model:
|
if opts.model:
|
||||||
|
@ -686,7 +686,7 @@ class FileUploadField(FormField):
|
|||||||
if upload_errors:
|
if upload_errors:
|
||||||
raise validators.CriticalValidationError, upload_errors
|
raise validators.CriticalValidationError, upload_errors
|
||||||
try:
|
try:
|
||||||
file_size = new_data.file_size
|
file_size = new_data.size
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
file_size = len(new_data['content'])
|
file_size = len(new_data['content'])
|
||||||
if not file_size:
|
if not file_size:
|
||||||
|
@ -90,19 +90,21 @@ def encode_multipart(boundary, data):
|
|||||||
"""
|
"""
|
||||||
lines = []
|
lines = []
|
||||||
to_str = lambda s: smart_str(s, settings.DEFAULT_CHARSET)
|
to_str = lambda s: smart_str(s, settings.DEFAULT_CHARSET)
|
||||||
|
|
||||||
|
# Not by any means perfect, but good enough for our purposes.
|
||||||
|
is_file = lambda thing: hasattr(thing, "read") and callable(thing.read)
|
||||||
|
|
||||||
|
# Each bit of the multipart form data could be either a form value or a
|
||||||
|
# file, or a *list* of form values and/or files. Remember that HTTP field
|
||||||
|
# names can be duplicated!
|
||||||
for (key, value) in data.items():
|
for (key, value) in data.items():
|
||||||
if isinstance(value, file):
|
if is_file(value):
|
||||||
lines.extend([
|
lines.extend(encode_file(boundary, key, value))
|
||||||
'--' + boundary,
|
elif not isinstance(value, basestring) and is_iterable(value):
|
||||||
'Content-Disposition: form-data; name="%s"; filename="%s"' \
|
|
||||||
% (to_str(key), to_str(os.path.basename(value.name))),
|
|
||||||
'Content-Type: application/octet-stream',
|
|
||||||
'',
|
|
||||||
value.read()
|
|
||||||
])
|
|
||||||
else:
|
|
||||||
if not isinstance(value, basestring) and is_iterable(value):
|
|
||||||
for item in value:
|
for item in value:
|
||||||
|
if is_file(item):
|
||||||
|
lines.extend(encode_file(boundary, key, item))
|
||||||
|
else:
|
||||||
lines.extend([
|
lines.extend([
|
||||||
'--' + boundary,
|
'--' + boundary,
|
||||||
'Content-Disposition: form-data; name="%s"' % to_str(key),
|
'Content-Disposition: form-data; name="%s"' % to_str(key),
|
||||||
@ -123,6 +125,17 @@ def encode_multipart(boundary, data):
|
|||||||
])
|
])
|
||||||
return '\r\n'.join(lines)
|
return '\r\n'.join(lines)
|
||||||
|
|
||||||
|
def encode_file(boundary, key, file):
|
||||||
|
to_str = lambda s: smart_str(s, settings.DEFAULT_CHARSET)
|
||||||
|
return [
|
||||||
|
'--' + boundary,
|
||||||
|
'Content-Disposition: form-data; name="%s"; filename="%s"' \
|
||||||
|
% (to_str(key), to_str(os.path.basename(file.name))),
|
||||||
|
'Content-Type: application/octet-stream',
|
||||||
|
'',
|
||||||
|
file.read()
|
||||||
|
]
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
"""
|
"""
|
||||||
A class that can act as a client for testing purposes.
|
A class that can act as a client for testing purposes.
|
||||||
|
@ -67,3 +67,8 @@ def is_iterable(x):
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def sorted(in_value):
|
||||||
|
"A naive implementation of sorted"
|
||||||
|
out_value = in_value[:]
|
||||||
|
out_value.sort()
|
||||||
|
return out_value
|
||||||
|
@ -161,6 +161,15 @@ def translation(language):
|
|||||||
|
|
||||||
res = _translation(globalpath)
|
res = _translation(globalpath)
|
||||||
|
|
||||||
|
# We want to ensure that, for example, "en-gb" and "en-us" don't share
|
||||||
|
# the same translation object (thus, merging en-us with a local update
|
||||||
|
# doesn't affect en-gb), even though they will both use the core "en"
|
||||||
|
# translation. So we have to subvert Python's internal gettext caching.
|
||||||
|
base_lang = lambda x: x.split('-', 1)[0]
|
||||||
|
if base_lang(lang) in [base_lang(trans) for trans in _translations]:
|
||||||
|
res._info = res._info.copy()
|
||||||
|
res._catalog = res._catalog.copy()
|
||||||
|
|
||||||
def _merge(path):
|
def _merge(path):
|
||||||
t = _translation(path)
|
t = _translation(path)
|
||||||
if t is not None:
|
if t is not None:
|
||||||
|
@ -450,11 +450,11 @@ TECHNICAL_500_TEMPLATE = """
|
|||||||
{% if frame.context_line %}
|
{% if frame.context_line %}
|
||||||
<div class="context" id="c{{ frame.id }}">
|
<div class="context" id="c{{ frame.id }}">
|
||||||
{% if frame.pre_context %}
|
{% if frame.pre_context %}
|
||||||
<ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}{% if line %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endif %}{% endfor %}</ol>
|
<ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">{% for line in frame.pre_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line|escape }} <span>...</span></li></ol>
|
<ol start="{{ frame.lineno }}" class="context-line"><li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ frame.context_line|escape }} <span>...</span></li></ol>
|
||||||
{% if frame.post_context %}
|
{% if frame.post_context %}
|
||||||
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}{% if line %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endif %}{% endfor %}</ol>
|
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">{% for line in frame.post_context %}<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')">{{ line|escape }}</li>{% endfor %}</ol>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from django.template import loader, RequestContext
|
from django.template import loader, RequestContext
|
||||||
from django.http import Http404, HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
from django.core.xheaders import populate_xheaders
|
from django.core.xheaders import populate_xheaders
|
||||||
from django.core.paginator import QuerySetPaginator, InvalidPage
|
from django.core.paginator import Paginator, InvalidPage
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
def object_list(request, queryset, paginate_by=None, page=None,
|
def object_list(request, queryset, paginate_by=None, page=None,
|
||||||
@ -45,7 +45,7 @@ def object_list(request, queryset, paginate_by=None, page=None,
|
|||||||
if extra_context is None: extra_context = {}
|
if extra_context is None: extra_context = {}
|
||||||
queryset = queryset._clone()
|
queryset = queryset._clone()
|
||||||
if paginate_by:
|
if paginate_by:
|
||||||
paginator = QuerySetPaginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
|
paginator = Paginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
|
||||||
if not page:
|
if not page:
|
||||||
page = request.GET.get('page', 1)
|
page = request.GET.get('page', 1)
|
||||||
try:
|
try:
|
||||||
|
@ -392,7 +392,7 @@ translated, here's what to do:
|
|||||||
* Create translations using the methods described in the
|
* Create translations using the methods described in the
|
||||||
`i18n documentation`_.
|
`i18n documentation`_.
|
||||||
* Create a diff of the ``.po`` file against the current Subversion trunk.
|
* Create a diff of the ``.po`` file against the current Subversion trunk.
|
||||||
* Make sure that `` bin/compile-messages.py -l <lang>`` runs without
|
* Make sure that `` django-admin.py compilemessages -l <lang>`` runs without
|
||||||
producing any warnings.
|
producing any warnings.
|
||||||
* Attach the patch to a ticket in Django's ticket system.
|
* Attach the patch to a ticket in Django's ticket system.
|
||||||
|
|
||||||
@ -738,6 +738,11 @@ If you're using another backend:
|
|||||||
deleted when the tests are finished. This means your user account needs
|
deleted when the tests are finished. This means your user account needs
|
||||||
permission to execute ``CREATE DATABASE``.
|
permission to execute ``CREATE DATABASE``.
|
||||||
|
|
||||||
|
You will also need to ensure that your database uses UTF-8 as the default
|
||||||
|
character set. If your database server doesn't use UTF-8 as a default charset,
|
||||||
|
you will need to include a value for ``TEST_DATABASE_CHARSET`` in your settings
|
||||||
|
file.
|
||||||
|
|
||||||
If you want to run the full suite of tests, you'll need to install a number of
|
If you want to run the full suite of tests, you'll need to install a number of
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
||||||
|
@ -2212,6 +2212,18 @@ updated is that it can only access one database table, the model's main
|
|||||||
table. So don't try to filter based on related fields or anything like that;
|
table. So don't try to filter based on related fields or anything like that;
|
||||||
it won't work.
|
it won't work.
|
||||||
|
|
||||||
|
Be aware that the ``update()`` method is converted directly to an SQL
|
||||||
|
statement. It is a bulk operation for direct updates. It doesn't run any
|
||||||
|
``save()`` methods on your models, or emit the ``pre_save`` or ``post_save``
|
||||||
|
signals (which are a consequence of calling ``save()``). If you want to save
|
||||||
|
every item in a ``QuerySet`` and make sure that the ``save()`` method is
|
||||||
|
called on each instance, you don't need any special function to handle that.
|
||||||
|
Just loop over them and call ``save()``:
|
||||||
|
|
||||||
|
for item in my_queryset:
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
|
||||||
Extra instance methods
|
Extra instance methods
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
@ -85,6 +85,32 @@ your admin's index page. See `Tutorial 2`_ for more information.
|
|||||||
|
|
||||||
.. _Tutorial 2: ../tutorial02/
|
.. _Tutorial 2: ../tutorial02/
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
-------
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Can be run as a cronjob or directly to clean out old data from the database
|
||||||
|
(only expired sessions at the moment).
|
||||||
|
|
||||||
|
compilemessages
|
||||||
|
---------------
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Compiles .po files created with ``makemessages`` to .mo files for use with
|
||||||
|
the builtin gettext support. See the `i18n documentation`_ for details.
|
||||||
|
|
||||||
|
--locale
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Use the ``--locale`` or ``-l`` option to specify the locale to process.
|
||||||
|
If not provided all locales are processed.
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
django-admin.py compilemessages --locale=br_PT
|
||||||
|
|
||||||
createcachetable <tablename>
|
createcachetable <tablename>
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
@ -362,6 +388,62 @@ Example usage::
|
|||||||
|
|
||||||
django-admin.py loaddata --verbosity=2
|
django-admin.py loaddata --verbosity=2
|
||||||
|
|
||||||
|
makemessages
|
||||||
|
------------
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Runs over the entire source tree of the current directory and pulls out all
|
||||||
|
strings marked for translation. It creates (or updates) a message file in the
|
||||||
|
conf/locale (in the django tree) or locale (for project and application)
|
||||||
|
directory. After making changes to the messages files you need to compile them
|
||||||
|
with ``compilemessages`` for use with the builtin gettext support. See the
|
||||||
|
`i18n documentation`_ for details.
|
||||||
|
|
||||||
|
.. _i18n documentation: ../i18n/#how-to-create-language-files
|
||||||
|
|
||||||
|
--all
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
Use the ``--all`` or ``-a`` option to update the message files for all
|
||||||
|
available languages.
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
django-admin.py makemessages --all
|
||||||
|
|
||||||
|
--locale
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Use the ``--locale`` or ``-l`` option to specify the locale to process.
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
django-admin.py makemessages --locale=br_PT
|
||||||
|
|
||||||
|
--domain
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Use the ``--domain`` or ``-d`` option to change the domain of the messages files.
|
||||||
|
Currently supported:
|
||||||
|
|
||||||
|
* ``django`` for all ``*.py`` and ``*.html`` files (default)
|
||||||
|
* ``djangojs`` for ``*.js`` files
|
||||||
|
|
||||||
|
--verbosity
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
Use ``--verbosity`` or ``-v`` to specify the amount of notification and debug
|
||||||
|
information that ``django-admin.py`` should print to the console.
|
||||||
|
|
||||||
|
* ``0`` means no output.
|
||||||
|
* ``1`` means normal output (default).
|
||||||
|
* ``2`` means verbose output.
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
django-admin.py makemessages --verbosity=2
|
||||||
|
|
||||||
reset <appname appname ...>
|
reset <appname appname ...>
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
@ -816,15 +816,14 @@ specify the page number in the URL in one of two ways:
|
|||||||
These values and lists are 1-based, not 0-based, so the first page would be
|
These values and lists are 1-based, not 0-based, so the first page would be
|
||||||
represented as page ``1``.
|
represented as page ``1``.
|
||||||
|
|
||||||
An example of the use of pagination can be found in the `object pagination`_
|
For more on pagination, read the `pagination documentation`_.
|
||||||
example model.
|
|
||||||
|
|
||||||
.. _`object pagination`: ../models/pagination/
|
.. _`pagination documentation`: ../pagination/
|
||||||
|
|
||||||
**New in Django development version:**
|
**New in Django development version:**
|
||||||
|
|
||||||
As a special case, you are also permitted to use
|
As a special case, you are also permitted to use ``last`` as a value for
|
||||||
``last`` as a value for ``page``::
|
``page``::
|
||||||
|
|
||||||
/objects/?page=last
|
/objects/?page=last
|
||||||
|
|
||||||
|
@ -122,8 +122,8 @@ Translation works on variables. Again, here's an identical example::
|
|||||||
|
|
||||||
(The caveat with using variables or computed values, as in the previous two
|
(The caveat with using variables or computed values, as in the previous two
|
||||||
examples, is that Django's translation-string-detecting utility,
|
examples, is that Django's translation-string-detecting utility,
|
||||||
``make-messages.py``, won't be able to find these strings. More on
|
``django-admin.py makemessages``, won't be able to find these strings. More on
|
||||||
``make-messages`` later.)
|
``makemessages`` later.)
|
||||||
|
|
||||||
The strings you pass to ``_()`` or ``ugettext()`` can take placeholders,
|
The strings you pass to ``_()`` or ``ugettext()`` can take placeholders,
|
||||||
specified with Python's standard named-string interpolation syntax. Example::
|
specified with Python's standard named-string interpolation syntax. Example::
|
||||||
@ -266,10 +266,11 @@ Internally, all block and inline translations use the appropriate
|
|||||||
Each ``RequestContext`` has access to three translation-specific variables:
|
Each ``RequestContext`` has access to three translation-specific variables:
|
||||||
|
|
||||||
* ``LANGUAGES`` is a list of tuples in which the first element is the
|
* ``LANGUAGES`` is a list of tuples in which the first element is the
|
||||||
language code and the second is the language name (in that language).
|
language code and the second is the language name (translated into the
|
||||||
|
currently active locale).
|
||||||
* ``LANGUAGE_CODE`` is the current user's preferred language, as a string.
|
* ``LANGUAGE_CODE`` is the current user's preferred language, as a string.
|
||||||
Example: ``en-us``. (See "How language preference is discovered", below.)
|
Example: ``en-us``. (See "How language preference is discovered", below.)
|
||||||
* ``LANGUAGE_BIDI`` is the current language's direction. If True, it's a
|
* ``LANGUAGE_BIDI`` is the current locale's direction. If True, it's a
|
||||||
right-to-left language, e.g: Hebrew, Arabic. If False it's a
|
right-to-left language, e.g: Hebrew, Arabic. If False it's a
|
||||||
left-to-right language, e.g: English, French, German etc.
|
left-to-right language, e.g: English, French, German etc.
|
||||||
|
|
||||||
@ -392,12 +393,17 @@ file is a plain-text file, representing a single language, that contains all
|
|||||||
available translation strings and how they should be represented in the given
|
available translation strings and how they should be represented in the given
|
||||||
language. Message files have a ``.po`` file extension.
|
language. Message files have a ``.po`` file extension.
|
||||||
|
|
||||||
Django comes with a tool, ``bin/make-messages.py``, that automates the creation
|
Django comes with a tool, ``django-admin.py makemessages``, that automates the
|
||||||
and upkeep of these files.
|
creation and upkeep of these files.
|
||||||
|
|
||||||
|
.. admonition:: A note to Django veterans
|
||||||
|
|
||||||
|
The old tool ``bin/make-messages.py`` has been moved to the command
|
||||||
|
``django-admin.py makemessages`` to provide consistency throughout Django.
|
||||||
|
|
||||||
To create or update a message file, run this command::
|
To create or update a message file, run this command::
|
||||||
|
|
||||||
bin/make-messages.py -l de
|
django-admin.py makemessages -l de
|
||||||
|
|
||||||
...where ``de`` is the language code for the message file you want to create.
|
...where ``de`` is the language code for the message file you want to create.
|
||||||
The language code, in this case, is in locale format. For example, it's
|
The language code, in this case, is in locale format. For example, it's
|
||||||
@ -422,11 +428,11 @@ do the same, but the location of the locale directory is ``locale/LANG/LC_MESSAG
|
|||||||
|
|
||||||
.. admonition:: No gettext?
|
.. admonition:: No gettext?
|
||||||
|
|
||||||
If you don't have the ``gettext`` utilities installed, ``make-messages.py``
|
If you don't have the ``gettext`` utilities installed,
|
||||||
will create empty files. If that's the case, either install the ``gettext``
|
``django-admin.py makemessages`` will create empty files. If that's the
|
||||||
utilities or just copy the English message file
|
case, either install the ``gettext`` utilities or just copy the English
|
||||||
(``conf/locale/en/LC_MESSAGES/django.po``) and use it as a starting point;
|
message file (``conf/locale/en/LC_MESSAGES/django.po``) and use it as a
|
||||||
it's just an empty translation file.
|
starting point; it's just an empty translation file.
|
||||||
|
|
||||||
The format of ``.po`` files is straightforward. Each ``.po`` file contains a
|
The format of ``.po`` files is straightforward. Each ``.po`` file contains a
|
||||||
small bit of metadata, such as the translation maintainer's contact
|
small bit of metadata, such as the translation maintainer's contact
|
||||||
@ -439,8 +445,8 @@ For example, if your Django app contained a translation string for the text
|
|||||||
|
|
||||||
_("Welcome to my site.")
|
_("Welcome to my site.")
|
||||||
|
|
||||||
...then ``make-messages.py`` will have created a ``.po`` file containing the
|
...then ``django-admin.py makemessages`` will have created a ``.po`` file
|
||||||
following snippet -- a message::
|
containing the following snippet -- a message::
|
||||||
|
|
||||||
#: path/to/python/module.py:23
|
#: path/to/python/module.py:23
|
||||||
msgid "Welcome to my site."
|
msgid "Welcome to my site."
|
||||||
@ -475,24 +481,30 @@ otherwise, they'll be tacked together without whitespace!
|
|||||||
To reexamine all source code and templates for new translation strings and
|
To reexamine all source code and templates for new translation strings and
|
||||||
update all message files for **all** languages, run this::
|
update all message files for **all** languages, run this::
|
||||||
|
|
||||||
make-messages.py -a
|
django-admin.py makemessages -a
|
||||||
|
|
||||||
Compiling message files
|
Compiling message files
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
After you create your message file -- and each time you make changes to it --
|
After you create your message file -- and each time you make changes to it --
|
||||||
you'll need to compile it into a more efficient form, for use by ``gettext``.
|
you'll need to compile it into a more efficient form, for use by ``gettext``.
|
||||||
Do this with the ``bin/compile-messages.py`` utility.
|
Do this with the ``django-admin.py compilemessages`` utility.
|
||||||
|
|
||||||
This tool runs over all available ``.po`` files and creates ``.mo`` files,
|
This tool runs over all available ``.po`` files and creates ``.mo`` files,
|
||||||
which are binary files optimized for use by ``gettext``. In the same directory
|
which are binary files optimized for use by ``gettext``. In the same directory
|
||||||
from which you ran ``make-messages.py``, run ``compile-messages.py`` like
|
from which you ran ``django-admin.py makemessages``, run
|
||||||
this::
|
``django-admin.py compilemessages`` like this::
|
||||||
|
|
||||||
bin/compile-messages.py
|
django-admin.py compilemessages
|
||||||
|
|
||||||
That's it. Your translations are ready for use.
|
That's it. Your translations are ready for use.
|
||||||
|
|
||||||
|
.. admonition:: A note to Django veterans
|
||||||
|
|
||||||
|
The old tool ``bin/compile-messages.py`` has been moved to the command
|
||||||
|
``django-admin.py compilemessages`` to provide consistency throughout
|
||||||
|
Django.
|
||||||
|
|
||||||
.. admonition:: A note to translators
|
.. admonition:: A note to translators
|
||||||
|
|
||||||
If you've created a translation in a language Django doesn't yet support,
|
If you've created a translation in a language Django doesn't yet support,
|
||||||
@ -597,9 +609,9 @@ Notes:
|
|||||||
('en', ugettext('English')),
|
('en', ugettext('English')),
|
||||||
)
|
)
|
||||||
|
|
||||||
With this arrangement, ``make-messages.py`` will still find and mark
|
With this arrangement, ``django-admin.py makemessages`` will still find
|
||||||
these strings for translation, but the translation won't happen at
|
and mark these strings for translation, but the translation won't happen
|
||||||
runtime -- so you'll have to remember to wrap the languages in the *real*
|
at runtime -- so you'll have to remember to wrap the languages in the *real*
|
||||||
``ugettext()`` in any code that uses ``LANGUAGES`` at runtime.
|
``ugettext()`` in any code that uses ``LANGUAGES`` at runtime.
|
||||||
|
|
||||||
* The ``LocaleMiddleware`` can only select languages for which there is a
|
* The ``LocaleMiddleware`` can only select languages for which there is a
|
||||||
@ -676,15 +688,16 @@ All message file repositories are structured the same way. They are:
|
|||||||
searched in that order for ``<language>/LC_MESSAGES/django.(po|mo)``
|
searched in that order for ``<language>/LC_MESSAGES/django.(po|mo)``
|
||||||
* ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)``
|
* ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)``
|
||||||
|
|
||||||
To create message files, you use the same ``make-messages.py`` tool as with the
|
To create message files, you use the same ``django-admin.py makemessages``
|
||||||
Django message files. You only need to be in the right place -- in the directory
|
tool as with the Django message files. You only need to be in the right place
|
||||||
where either the ``conf/locale`` (in case of the source tree) or the ``locale/``
|
-- in the directory where either the ``conf/locale`` (in case of the source
|
||||||
(in case of app messages or project messages) directory are located. And you
|
tree) or the ``locale/`` (in case of app messages or project messages)
|
||||||
use the same ``compile-messages.py`` to produce the binary ``django.mo`` files
|
directory are located. And you use the same ``django-admin.py compilemessages``
|
||||||
that are used by ``gettext``.
|
to produce the binary ``django.mo`` files that are used by ``gettext``.
|
||||||
|
|
||||||
You can also run ``compile-message.py --settings=path.to.settings`` to make
|
You can also run ``django-admin.py compilemessages --settings=path.to.settings``
|
||||||
the compiler process all the directories in your ``LOCALE_PATHS`` setting.
|
to make the compiler process all the directories in your ``LOCALE_PATHS``
|
||||||
|
setting.
|
||||||
|
|
||||||
Application message files are a bit complicated to discover -- they need the
|
Application message files are a bit complicated to discover -- they need the
|
||||||
``LocaleMiddleware``. If you don't use the middleware, only the Django message
|
``LocaleMiddleware``. If you don't use the middleware, only the Django message
|
||||||
@ -694,15 +707,15 @@ Finally, you should give some thought to the structure of your translation
|
|||||||
files. If your applications need to be delivered to other users and will
|
files. If your applications need to be delivered to other users and will
|
||||||
be used in other projects, you might want to use app-specific translations.
|
be used in other projects, you might want to use app-specific translations.
|
||||||
But using app-specific translations and project translations could produce
|
But using app-specific translations and project translations could produce
|
||||||
weird problems with ``make-messages``: ``make-messages`` will traverse all
|
weird problems with ``makemessages``: ``makemessages`` will traverse all
|
||||||
directories below the current path and so might put message IDs into the
|
directories below the current path and so might put message IDs into the
|
||||||
project message file that are already in application message files.
|
project message file that are already in application message files.
|
||||||
|
|
||||||
The easiest way out is to store applications that are not part of the project
|
The easiest way out is to store applications that are not part of the project
|
||||||
(and so carry their own translations) outside the project tree. That way,
|
(and so carry their own translations) outside the project tree. That way,
|
||||||
``make-messages`` on the project level will only translate strings that are
|
``django-admin.py makemessages`` on the project level will only translate
|
||||||
connected to your explicit project and not strings that are distributed
|
strings that are connected to your explicit project and not strings that are
|
||||||
independently.
|
distributed independently.
|
||||||
|
|
||||||
The ``set_language`` redirect view
|
The ``set_language`` redirect view
|
||||||
==================================
|
==================================
|
||||||
@ -857,14 +870,14 @@ Creating JavaScript translation catalogs
|
|||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
You create and update the translation catalogs the same way as the other
|
You create and update the translation catalogs the same way as the other
|
||||||
Django translation catalogs -- with the make-messages.py tool. The only
|
Django translation catalogs -- with the django-admin.py makemessages tool. The
|
||||||
difference is you need to provide a ``-d djangojs`` parameter, like this::
|
only difference is you need to provide a ``-d djangojs`` parameter, like this::
|
||||||
|
|
||||||
make-messages.py -d djangojs -l de
|
django-admin.py makemessages -d djangojs -l de
|
||||||
|
|
||||||
This would create or update the translation catalog for JavaScript for German.
|
This would create or update the translation catalog for JavaScript for German.
|
||||||
After updating translation catalogs, just run ``compile-messages.py`` the same
|
After updating translation catalogs, just run ``django-admin.py compilemessages``
|
||||||
way as you do with normal Django translation catalogs.
|
the same way as you do with normal Django translation catalogs.
|
||||||
|
|
||||||
Specialties of Django translation
|
Specialties of Django translation
|
||||||
==================================
|
==================================
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
.TH "compile-messages.py" "1" "August 2007" "Django Project" ""
|
|
||||||
.SH "NAME"
|
|
||||||
compile-messages.py \- Internationalization utility for the Django
|
|
||||||
web framework
|
|
||||||
.SH "SYNOPSIS"
|
|
||||||
.B compile-messages.py \fR[-l <locale>]
|
|
||||||
|
|
||||||
.SH "DESCRIPTION"
|
|
||||||
A Django-customised wrapper around gettext's \fBmsgfmt\fR command. Generates
|
|
||||||
binary message catalogs (.mo files) from textual translation descriptions (.po
|
|
||||||
files).
|
|
||||||
.sp
|
|
||||||
The script should be invoked after running
|
|
||||||
.BI make-messages.py,
|
|
||||||
in the same directory from which
|
|
||||||
.BI make-messages.py
|
|
||||||
was invoked.
|
|
||||||
|
|
||||||
.SH "OPTIONS"
|
|
||||||
.TP
|
|
||||||
.I \-l <locale>
|
|
||||||
Compile the message catalogs for a specific locale. If this option is omitted,
|
|
||||||
all message catalogs are (re-)compiled.
|
|
||||||
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
The man page for
|
|
||||||
.BI msgfmt
|
|
||||||
from the GNU gettext utilities, and the internationalization documentation
|
|
||||||
for Django:
|
|
||||||
.sp
|
|
||||||
.I http://www.djangoproject.com/documentation/i18n/
|
|
||||||
|
|
||||||
.SH "AUTHORS/CREDITS"
|
|
||||||
Originally developed at World Online in Lawrence, Kansas, USA. Refer to the
|
|
||||||
AUTHORS file in the Django distribution for contributors.
|
|
||||||
|
|
||||||
.SH "LICENSE"
|
|
||||||
New BSD license. For the full license text refer to the LICENSE file in the
|
|
||||||
Django distribution.
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
.TH "django-admin.py" "1" "June 2007" "Django Project" ""
|
.TH "django-admin.py" "1" "March 2008" "Django Project" ""
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
django\-admin.py \- Utility script for the Django web framework
|
django\-admin.py \- Utility script for the Django web framework
|
||||||
.SH "SYNOPSIS"
|
.SH "SYNOPSIS"
|
||||||
@ -21,6 +21,12 @@ script found at the top level of each Django project directory.
|
|||||||
.BI "adminindex [" "appname ..." "]"
|
.BI "adminindex [" "appname ..." "]"
|
||||||
Prints the admin\-index template snippet for the given app name(s).
|
Prints the admin\-index template snippet for the given app name(s).
|
||||||
.TP
|
.TP
|
||||||
|
.BI cleanup
|
||||||
|
Cleans out old data from the database (only expired sessions at the moment).
|
||||||
|
.TP
|
||||||
|
.BI "compilemessages [" "\-\-locale=LOCALE" "]"
|
||||||
|
Compiles .po files to .mo files for use with builtin gettext support.
|
||||||
|
.TP
|
||||||
.BI "createcachetable [" "tablename" "]"
|
.BI "createcachetable [" "tablename" "]"
|
||||||
Creates the table needed to use the SQL cache backend
|
Creates the table needed to use the SQL cache backend
|
||||||
.TP
|
.TP
|
||||||
@ -43,6 +49,11 @@ Executes
|
|||||||
.B sqlall
|
.B sqlall
|
||||||
for the given app(s) in the current database.
|
for the given app(s) in the current database.
|
||||||
.TP
|
.TP
|
||||||
|
.BI "makemessages [" "\-\-locale=LOCALE" "] [" "\-\-domain=DOMAIN" "] [" "\-\-all" "]"
|
||||||
|
Runs over the entire source tree of the current directory and pulls out all
|
||||||
|
strings marked for translation. It creates (or updates) a message file in the
|
||||||
|
conf/locale (in the django tree) or locale (for project and application) directory.
|
||||||
|
.TP
|
||||||
.BI "reset [" "appname ..." "]"
|
.BI "reset [" "appname ..." "]"
|
||||||
Executes
|
Executes
|
||||||
.B sqlreset
|
.B sqlreset
|
||||||
@ -136,7 +147,15 @@ Verbosity level: 0=minimal output, 1=normal output, 2=all output.
|
|||||||
.TP
|
.TP
|
||||||
.I \-\-adminmedia=ADMIN_MEDIA_PATH
|
.I \-\-adminmedia=ADMIN_MEDIA_PATH
|
||||||
Specifies the directory from which to serve admin media when using the development server.
|
Specifies the directory from which to serve admin media when using the development server.
|
||||||
|
.TP
|
||||||
|
.I \-l, \-\-locale=LOCALE
|
||||||
|
The locale to process when using makemessages or compilemessages.
|
||||||
|
.TP
|
||||||
|
.I \-d, \-\-domain=DOMAIN
|
||||||
|
The domain of the message files (default: "django") when using makemessages.
|
||||||
|
.TP
|
||||||
|
.I \-a, \-\-all
|
||||||
|
Process all available locales when using makemessages.
|
||||||
.SH "ENVIRONMENT"
|
.SH "ENVIRONMENT"
|
||||||
.TP
|
.TP
|
||||||
.I DJANGO_SETTINGS_MODULE
|
.I DJANGO_SETTINGS_MODULE
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
.TH "make-messages.py" "1" "August 2007" "Django Project" ""
|
|
||||||
.SH "NAME"
|
|
||||||
make-messages.py \- Internationalization utility for the Django
|
|
||||||
web framework
|
|
||||||
.SH "SYNOPSIS"
|
|
||||||
.B make-messages.py\fR [\-a] [\-v] [\-l <locale>] [\-d <domain>]
|
|
||||||
|
|
||||||
.SH "DESCRIPTION"
|
|
||||||
This script creates or updates one or more message files for a Django app,
|
|
||||||
a Django project or the Django framework itself. It should be run from one
|
|
||||||
of three places: the root directory of a Django app; the root directory
|
|
||||||
of a Django project; or the root django directory (the one in your PYTHONPATH,
|
|
||||||
not the root of a Subversion checkout).
|
|
||||||
.sp
|
|
||||||
The script will run over the source tree of an application, project or Django
|
|
||||||
itself (depending on where it is invoked), pulling out all strings marked for
|
|
||||||
translation and creating or updating a standard PO-format message file for the
|
|
||||||
specified language. Refer to Django's internationalization documentation for
|
|
||||||
details of where this file is created.
|
|
||||||
.sp
|
|
||||||
The \fI\-a\fR and \fI\-l\fR options are used to control whether message
|
|
||||||
catalogs are created for all locales, or just a single one.
|
|
||||||
|
|
||||||
.SH "OPTIONS"
|
|
||||||
.TP
|
|
||||||
.I \-a
|
|
||||||
Run make-messages for all locales specified in the Django settings file. Cannot
|
|
||||||
be used in conjuntion with \fI\-l\fR.
|
|
||||||
.TP
|
|
||||||
.I \-d <domain>
|
|
||||||
Specifies the translation domain to use. Valid domains are \fIdjango\fR or
|
|
||||||
\fIdjangojs\fR, depending on whether you wish to generate translation strings
|
|
||||||
for the Python or JavaScript components of your app, your project or the
|
|
||||||
framework itself. The default domain is \fIdjango\fR.
|
|
||||||
.TP
|
|
||||||
.I \-l <locale>
|
|
||||||
Extract messages for a particular locale.
|
|
||||||
.TP
|
|
||||||
.I \-v
|
|
||||||
Run verbosely.
|
|
||||||
|
|
||||||
.SH "ENVIRONMENT"
|
|
||||||
.TP
|
|
||||||
.I DJANGO_SETTINGS_MODULE
|
|
||||||
This environment variable defines the settings module to be read.
|
|
||||||
It should be in Python-import form, e.g. "myproject.settings".
|
|
||||||
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
The Django internationalization documentation:
|
|
||||||
.sp
|
|
||||||
.I http://www.djangoproject.com/documentation/i18n/
|
|
||||||
.sp
|
|
||||||
The PO file format is documented in the GNU gettext documentation.
|
|
||||||
|
|
||||||
.SH "AUTHORS/CREDITS"
|
|
||||||
Originally developed at World Online in Lawrence, Kansas, USA. Refer to the
|
|
||||||
AUTHORS file in the Django distribution for contributors.
|
|
||||||
|
|
||||||
.SH "LICENSE"
|
|
||||||
New BSD license. For the full license text refer to the LICENSE file in the
|
|
||||||
Django distribution.
|
|
||||||
|
|
@ -1331,23 +1331,12 @@ given length.
|
|||||||
* Validates that non-empty file data has been bound to the form.
|
* Validates that non-empty file data has been bound to the form.
|
||||||
* Error message keys: ``required``, ``invalid``, ``missing``, ``empty``
|
* Error message keys: ``required``, ``invalid``, ``missing``, ``empty``
|
||||||
|
|
||||||
An ``UploadedFile`` object has two attributes:
|
To learn more about the ``UploadedFile`` object, see the `file uploads documentation`_.
|
||||||
|
|
||||||
====================== ====================================================
|
When you use a ``FileField`` in a form, you must also remember to
|
||||||
Attribute Description
|
|
||||||
====================== ====================================================
|
|
||||||
``filename`` The name of the file, provided by the uploading
|
|
||||||
client.
|
|
||||||
|
|
||||||
``content`` The array of bytes comprising the file content.
|
|
||||||
====================== ====================================================
|
|
||||||
|
|
||||||
The string representation of an ``UploadedFile`` is the same as the filename
|
|
||||||
attribute.
|
|
||||||
|
|
||||||
When you use a ``FileField`` on a form, you must also remember to
|
|
||||||
`bind the file data to the form`_.
|
`bind the file data to the form`_.
|
||||||
|
|
||||||
|
.. _file uploads documentation: ../upload_handling/
|
||||||
.. _`bind the file data to the form`: `Binding uploaded files to a form`_
|
.. _`bind the file data to the form`: `Binding uploaded files to a form`_
|
||||||
|
|
||||||
``FilePathField``
|
``FilePathField``
|
||||||
@ -1412,7 +1401,7 @@ These control the range of values permitted in the field.
|
|||||||
|
|
||||||
Using an ImageField requires that the `Python Imaging Library`_ is installed.
|
Using an ImageField requires that the `Python Imaging Library`_ is installed.
|
||||||
|
|
||||||
When you use a ``FileField`` on a form, you must also remember to
|
When you use an ``ImageField`` in a form, you must also remember to
|
||||||
`bind the file data to the form`_.
|
`bind the file data to the form`_.
|
||||||
|
|
||||||
.. _Python Imaging Library: http://www.pythonware.com/products/pil/
|
.. _Python Imaging Library: http://www.pythonware.com/products/pil/
|
||||||
@ -1817,15 +1806,21 @@ reuse certain sets of widget attributes over and over again. Rather than
|
|||||||
repeat these attribute definitions every time you need them, Django allows
|
repeat these attribute definitions every time you need them, Django allows
|
||||||
you to capture those definitions as a custom widget.
|
you to capture those definitions as a custom widget.
|
||||||
|
|
||||||
For example, if you find that you are including a lot of comment fields on forms,
|
For example, if you find that you are including a lot of comment fields on
|
||||||
you could capture the idea of a ``TextInput`` with a specific ``size`` attribute
|
forms, you could capture the idea of a ``TextInput`` with a specific
|
||||||
as a custom extension to the ``TextInput`` widget::
|
default ``size`` attribute as a custom extension to the ``TextInput`` widget::
|
||||||
|
|
||||||
class CommentWidget(forms.TextInput):
|
class CommentWidget(forms.TextInput):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs.setdefault('attrs',{}).update({'size': '40'})
|
attrs = kwargs.setdefault('attrs',{})
|
||||||
|
if 'size' not in attrs:
|
||||||
|
attrs['size'] = 40
|
||||||
super(CommentWidget, self).__init__(*args, **kwargs)
|
super(CommentWidget, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
We allow the ``size`` attribute to be overridden by the user, but, by default,
|
||||||
|
this widget will behave as if ``attrs={'size': 40}`` was always passed into the
|
||||||
|
constructor.
|
||||||
|
|
||||||
Then you can use this widget in your forms::
|
Then you can use this widget in your forms::
|
||||||
|
|
||||||
class CommentForm(forms.Form):
|
class CommentForm(forms.Form):
|
||||||
|
@ -59,6 +59,11 @@ page::
|
|||||||
...
|
...
|
||||||
InvalidPage
|
InvalidPage
|
||||||
|
|
||||||
|
Note that you can give ``Paginator`` a list/tuple or a Django ``QuerySet``. The
|
||||||
|
only difference is in implementation; if you pass a ``QuerySet``, the
|
||||||
|
``Paginator`` will call its ``count()`` method instead of using ``len()``,
|
||||||
|
because the former is more efficient.
|
||||||
|
|
||||||
``Paginator`` objects
|
``Paginator`` objects
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
@ -77,6 +82,21 @@ Attributes
|
|||||||
|
|
||||||
``page_range`` -- A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``.
|
``page_range`` -- A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``.
|
||||||
|
|
||||||
|
``InvalidPage`` exceptions
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The ``page()`` method raises ``InvalidPage`` if the requested page is invalid
|
||||||
|
(i.e., not an integer) or contains no objects. Generally, it's enough to trap
|
||||||
|
the ``InvalidPage`` exception, but if you'd like more granularity, you can trap
|
||||||
|
either of the following exceptions:
|
||||||
|
|
||||||
|
``PageNotAnInteger`` -- Raised when ``page()`` is given a value that isn't an integer.
|
||||||
|
|
||||||
|
``EmptyPage`` -- Raised when ``page()`` is given a valid value but no objects exist on that page.
|
||||||
|
|
||||||
|
Both of the exceptions are subclasses of ``InvalidPage``, so you can handle
|
||||||
|
them both with a simple ``except InvalidPage``.
|
||||||
|
|
||||||
``Page`` objects
|
``Page`` objects
|
||||||
================
|
================
|
||||||
|
|
||||||
@ -116,13 +136,6 @@ Attributes
|
|||||||
|
|
||||||
``paginator`` -- The associated ``Paginator`` object.
|
``paginator`` -- The associated ``Paginator`` object.
|
||||||
|
|
||||||
``QuerySetPaginator`` objects
|
|
||||||
=============================
|
|
||||||
|
|
||||||
Use ``QuerySetPaginator`` instead of ``Paginator`` if you're paginating across
|
|
||||||
a ``QuerySet`` from Django's database API. This is slightly more efficient, and
|
|
||||||
there are no API differences between the two classes.
|
|
||||||
|
|
||||||
The legacy ``ObjectPaginator`` class
|
The legacy ``ObjectPaginator`` class
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ table. Django updates this row each time the session data changes. If the user
|
|||||||
logs out manually, Django deletes the row. But if the user does *not* log out,
|
logs out manually, Django deletes the row. But if the user does *not* log out,
|
||||||
the row never gets deleted.
|
the row never gets deleted.
|
||||||
|
|
||||||
Django provides a sample clean-up script in ``django/bin/daily_cleanup.py``.
|
Django provides a sample clean-up script in ``django-admin.py cleanup``.
|
||||||
That script deletes any session in the session table whose ``expire_date`` is
|
That script deletes any session in the session table whose ``expire_date`` is
|
||||||
in the past -- but your application may have different requirements.
|
in the past -- but your application may have different requirements.
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ Default: ``''`` (Empty string)
|
|||||||
|
|
||||||
The database backend to use. The build-in database backends are
|
The database backend to use. The build-in database backends are
|
||||||
``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'mysql_old'``,
|
``'postgresql_psycopg2'``, ``'postgresql'``, ``'mysql'``, ``'mysql_old'``,
|
||||||
``'sqlite3'``, ``'oracle'``, and ``'oracle'``.
|
``'sqlite3'``, and ``'oracle'``.
|
||||||
|
|
||||||
In the Django development version, you can use a database backend that doesn't
|
In the Django development version, you can use a database backend that doesn't
|
||||||
ship with Django by setting ``DATABASE_ENGINE`` to a fully-qualified path (i.e.
|
ship with Django by setting ``DATABASE_ENGINE`` to a fully-qualified path (i.e.
|
||||||
@ -682,10 +682,10 @@ settings file::
|
|||||||
('en', gettext('English')),
|
('en', gettext('English')),
|
||||||
)
|
)
|
||||||
|
|
||||||
With this arrangement, ``make-messages.py`` will still find and mark these
|
With this arrangement, ``django-admin.py makemessages`` will still find and
|
||||||
strings for translation, but the translation won't happen at runtime -- so
|
mark these strings for translation, but the translation won't happen at
|
||||||
you'll have to remember to wrap the languages in the *real* ``gettext()`` in
|
runtime -- so you'll have to remember to wrap the languages in the *real*
|
||||||
any code that uses ``LANGUAGES`` at runtime.
|
``gettext()`` in any code that uses ``LANGUAGES`` at runtime.
|
||||||
|
|
||||||
LOCALE_PATHS
|
LOCALE_PATHS
|
||||||
------------
|
------------
|
||||||
|
@ -89,7 +89,7 @@ read Python's official documentation for the details.
|
|||||||
For example, this function has a docstring that describes what it does::
|
For example, this function has a docstring that describes what it does::
|
||||||
|
|
||||||
def add_two(num):
|
def add_two(num):
|
||||||
"Adds 2 to the given number and returns the result."
|
"Return the result of adding two to the provided number."
|
||||||
return num + 2
|
return num + 2
|
||||||
|
|
||||||
Because tests often make great documentation, putting tests directly in
|
Because tests often make great documentation, putting tests directly in
|
||||||
@ -600,8 +600,6 @@ Specifically, a ``Response`` object has the following attributes:
|
|||||||
``context`` will be a list of ``Context``
|
``context`` will be a list of ``Context``
|
||||||
objects, in the order in which they were rendered.
|
objects, in the order in which they were rendered.
|
||||||
|
|
||||||
``headers`` The HTTP headers of the response. This is a dictionary.
|
|
||||||
|
|
||||||
``request`` The request data that stimulated the response.
|
``request`` The request data that stimulated the response.
|
||||||
|
|
||||||
``status_code`` The HTTP status of the response, as an integer. See
|
``status_code`` The HTTP status of the response, as an integer. See
|
||||||
@ -619,6 +617,10 @@ Specifically, a ``Response`` object has the following attributes:
|
|||||||
which they were rendered.
|
which they were rendered.
|
||||||
=============== ==========================================================
|
=============== ==========================================================
|
||||||
|
|
||||||
|
You can also use dictionary syntax on the response object to query the value
|
||||||
|
of any settings in the HTTP headers. For example, you could determine the
|
||||||
|
content type of a response using ``response['Content-Type']``.
|
||||||
|
|
||||||
.. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
.. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||||
.. _template inheritance: ../templates/#template-inheritance
|
.. _template inheritance: ../templates/#template-inheritance
|
||||||
|
|
||||||
|
@ -161,6 +161,9 @@ database's connection parameters:
|
|||||||
this point. Do that with "``CREATE DATABASE database_name;``" within your
|
this point. Do that with "``CREATE DATABASE database_name;``" within your
|
||||||
database's interactive prompt.
|
database's interactive prompt.
|
||||||
|
|
||||||
|
If you're using SQLite, you don't need to create anything beforehand - the
|
||||||
|
database file will be created automatically when it is needed.
|
||||||
|
|
||||||
While you're editing ``settings.py``, take note of the ``INSTALLED_APPS``
|
While you're editing ``settings.py``, take note of the ``INSTALLED_APPS``
|
||||||
setting towards the bottom of the file. That variable holds the names of all
|
setting towards the bottom of the file. That variable holds the names of all
|
||||||
Django applications that are activated in this Django instance. Apps can be
|
Django applications that are activated in this Django instance. Apps can be
|
||||||
|
@ -64,7 +64,7 @@ methods to access the uploaded content:
|
|||||||
``UploadedFile.read()``
|
``UploadedFile.read()``
|
||||||
Read the entire uploaded data from the file. Be careful with this
|
Read the entire uploaded data from the file. Be careful with this
|
||||||
method: if the uploaded file is huge it can overwhelm your system if you
|
method: if the uploaded file is huge it can overwhelm your system if you
|
||||||
try to read it into memory. You'll probably want to use ``chunk()``
|
try to read it into memory. You'll probably want to use ``chunks()``
|
||||||
instead; see below.
|
instead; see below.
|
||||||
|
|
||||||
``UploadedFile.multiple_chunks()``
|
``UploadedFile.multiple_chunks()``
|
||||||
@ -91,7 +91,7 @@ objects; see `UploadedFile objects`_ for a complete reference.
|
|||||||
Putting it all together, here's a common way you might handle an uploaded file::
|
Putting it all together, here's a common way you might handle an uploaded file::
|
||||||
|
|
||||||
def handle_uploaded_file(f):
|
def handle_uploaded_file(f):
|
||||||
destination = open('some/file/name.txt', 'wb')
|
destination = open('some/file/name.txt', 'wb+')
|
||||||
for chunk in f.chunks():
|
for chunk in f.chunks():
|
||||||
destination.write(chunk)
|
destination.write(chunk)
|
||||||
|
|
||||||
@ -161,13 +161,13 @@ All ``UploadedFile`` objects define the following methods/attributes:
|
|||||||
Returns a byte string of length ``num_bytes``, or the complete file if
|
Returns a byte string of length ``num_bytes``, or the complete file if
|
||||||
``num_bytes`` is ``None``.
|
``num_bytes`` is ``None``.
|
||||||
|
|
||||||
``UploadedFile.chunk(self, chunk_size=None)``
|
``UploadedFile.chunks(self, chunk_size=None)``
|
||||||
A generator yielding small chunks from the file. If ``chunk_size`` isn't
|
A generator yielding small chunks from the file. If ``chunk_size`` isn't
|
||||||
given, chunks will be 64 kb.
|
given, chunks will be 64 KB.
|
||||||
|
|
||||||
``UploadedFile.multiple_chunks(self, chunk_size=None)``
|
``UploadedFile.multiple_chunks(self, chunk_size=None)``
|
||||||
Returns ``True`` if you can expect more than one chunk when calling
|
Returns ``True`` if you can expect more than one chunk when calling
|
||||||
``UploadedFile.chunk(self, chunk_size)``.
|
``UploadedFile.chunks(self, chunk_size)``.
|
||||||
|
|
||||||
``UploadedFile.file_size``
|
``UploadedFile.file_size``
|
||||||
The size, in bytes, of the uploaded file.
|
The size, in bytes, of the uploaded file.
|
||||||
@ -186,10 +186,14 @@ All ``UploadedFile`` objects define the following methods/attributes:
|
|||||||
For ``text/*`` content-types, the character set (i.e. ``utf8``) supplied
|
For ``text/*`` content-types, the character set (i.e. ``utf8``) supplied
|
||||||
by the browser. Again, "trust but verify" is the best policy here.
|
by the browser. Again, "trust but verify" is the best policy here.
|
||||||
|
|
||||||
|
``UploadedFile.__iter__()``
|
||||||
|
Iterates over the lines in the file.
|
||||||
|
|
||||||
``UploadedFile.temporary_file_path()``
|
``UploadedFile.temporary_file_path()``
|
||||||
Only files uploaded onto disk will have this method; it returns the full
|
Only files uploaded onto disk will have this method; it returns the full
|
||||||
path to the temporary uploaded file.
|
path to the temporary uploaded file.
|
||||||
|
|
||||||
|
|
||||||
Upload Handlers
|
Upload Handlers
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
@ -42,13 +42,13 @@ _django_completion()
|
|||||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
||||||
# Standalone options
|
# Standalone options
|
||||||
opts="--help --settings --pythonpath --noinput --noreload --format --indent --verbosity --adminmedia --version"
|
opts="--help --settings --pythonpath --noinput --noreload --format --indent --verbosity --adminmedia --version --locale --domain"
|
||||||
# Actions
|
# Actions
|
||||||
actions="adminindex createcachetable createsuperuser dbshell diffsettings \
|
actions="adminindex createcachetable createsuperuser compilemessages \
|
||||||
dumpdata flush inspectdb loaddata reset runfcgi runserver \
|
dbshell diffsettings dumpdata flush inspectdb loaddata \
|
||||||
shell sql sqlall sqlclear sqlcustom sqlflush sqlindexes \
|
makemessages reset runfcgi runserver shell sql sqlall sqlclear \
|
||||||
sqlreset sqlsequencereset startapp startproject \
|
sqlcustom sqlflush sqlindexes sqlreset sqlsequencereset startapp \
|
||||||
syncdb test validate"
|
startproject syncdb test validate"
|
||||||
# Action's options
|
# Action's options
|
||||||
action_shell_opts="--plain"
|
action_shell_opts="--plain"
|
||||||
action_runfcgi_opts="host port socket method maxspare minspare maxchildren daemonize pidfile workdir"
|
action_runfcgi_opts="host port socket method maxspare minspare maxchildren daemonize pidfile workdir"
|
||||||
@ -118,8 +118,9 @@ _django_completion()
|
|||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
|
||||||
createcachetable|dbshell|diffsettings| \
|
createcachetable|cleanup|compilemessages|dbshell| \
|
||||||
inspectdb|runserver|startapp|startproject|syncdb| \
|
diffsettings|inspectdb|makemessages| \
|
||||||
|
runserver|startapp|startproject|syncdb| \
|
||||||
validate)
|
validate)
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
return 0
|
return 0
|
||||||
|
@ -4,12 +4,18 @@
|
|||||||
|
|
||||||
This is a basic model with only two non-primary-key fields.
|
This is a basic model with only two non-primary-key fields.
|
||||||
"""
|
"""
|
||||||
|
# Python 2.3 doesn't have set as a builtin
|
||||||
try:
|
try:
|
||||||
set
|
set
|
||||||
except NameError:
|
except NameError:
|
||||||
from sets import Set as set
|
from sets import Set as set
|
||||||
|
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Article(models.Model):
|
class Article(models.Model):
|
||||||
|
@ -803,7 +803,7 @@ False
|
|||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> type(f.cleaned_data['file'])
|
>>> type(f.cleaned_data['file'])
|
||||||
<class 'django.newforms.fields.UploadedFile'>
|
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.file
|
>>> instance.file
|
||||||
u'...test1.txt'
|
u'...test1.txt'
|
||||||
@ -814,7 +814,7 @@ u'...test1.txt'
|
|||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> type(f.cleaned_data['file'])
|
>>> type(f.cleaned_data['file'])
|
||||||
<class 'django.newforms.fields.UploadedFile'>
|
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.file
|
>>> instance.file
|
||||||
u'...test1.txt'
|
u'...test1.txt'
|
||||||
@ -900,13 +900,13 @@ u'...test3.txt'
|
|||||||
... class Meta:
|
... class Meta:
|
||||||
... model = ImageFile
|
... model = ImageFile
|
||||||
|
|
||||||
>>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png")).read()
|
>>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png"), 'rb').read()
|
||||||
|
|
||||||
>>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)})
|
>>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> type(f.cleaned_data['image'])
|
>>> type(f.cleaned_data['image'])
|
||||||
<class 'django.newforms.fields.UploadedFile'>
|
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.image
|
>>> instance.image
|
||||||
u'...test.png'
|
u'...test.png'
|
||||||
@ -918,7 +918,7 @@ u'...test.png'
|
|||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> type(f.cleaned_data['image'])
|
>>> type(f.cleaned_data['image'])
|
||||||
<class 'django.newforms.fields.UploadedFile'>
|
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.image
|
>>> instance.image
|
||||||
u'...test.png'
|
u'...test.png'
|
||||||
|
@ -8,6 +8,11 @@ Alternatively, use positional arguments, and pass one or more expressions of
|
|||||||
clauses using the variable ``django.db.models.Q`` (or any object with an
|
clauses using the variable ``django.db.models.Q`` (or any object with an
|
||||||
add_to_query method).
|
add_to_query method).
|
||||||
"""
|
"""
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ __test__ = {'API_TESTS':"""
|
|||||||
# New/current API (Paginator/Page) #
|
# New/current API (Paginator/Page) #
|
||||||
####################################
|
####################################
|
||||||
|
|
||||||
>>> from django.core.paginator import Paginator, InvalidPage
|
>>> from django.core.paginator import Paginator
|
||||||
>>> paginator = Paginator(Article.objects.all(), 5)
|
>>> paginator = Paginator(Article.objects.all(), 5)
|
||||||
>>> paginator.count
|
>>> paginator.count
|
||||||
9
|
9
|
||||||
@ -82,15 +82,15 @@ True
|
|||||||
>>> p.end_index()
|
>>> p.end_index()
|
||||||
9
|
9
|
||||||
|
|
||||||
# Invalid pages raise InvalidPage.
|
# Empty pages raise EmptyPage.
|
||||||
>>> paginator.page(0)
|
>>> paginator.page(0)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
InvalidPage: ...
|
EmptyPage: ...
|
||||||
>>> paginator.page(3)
|
>>> paginator.page(3)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
InvalidPage: ...
|
EmptyPage: ...
|
||||||
|
|
||||||
# Empty paginators with allow_empty_first_page=True.
|
# Empty paginators with allow_empty_first_page=True.
|
||||||
>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=True)
|
>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=True)
|
||||||
@ -148,7 +148,7 @@ True
|
|||||||
>>> from warnings import filterwarnings
|
>>> from warnings import filterwarnings
|
||||||
>>> filterwarnings("ignore")
|
>>> filterwarnings("ignore")
|
||||||
|
|
||||||
>>> from django.core.paginator import ObjectPaginator, InvalidPage
|
>>> from django.core.paginator import ObjectPaginator, EmptyPage
|
||||||
>>> paginator = ObjectPaginator(Article.objects.all(), 5)
|
>>> paginator = ObjectPaginator(Article.objects.all(), 5)
|
||||||
>>> paginator.hits
|
>>> paginator.hits
|
||||||
9
|
9
|
||||||
@ -181,15 +181,15 @@ True
|
|||||||
>>> paginator.last_on_page(1)
|
>>> paginator.last_on_page(1)
|
||||||
9
|
9
|
||||||
|
|
||||||
# Invalid pages raise InvalidPage.
|
# Invalid pages raise EmptyPage.
|
||||||
>>> paginator.get_page(-1)
|
>>> paginator.get_page(-1)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
InvalidPage: ...
|
EmptyPage: ...
|
||||||
>>> paginator.get_page(2)
|
>>> paginator.get_page(2)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
InvalidPage: ...
|
EmptyPage: ...
|
||||||
|
|
||||||
# Empty paginators with allow_empty_first_page=True.
|
# Empty paginators with allow_empty_first_page=True.
|
||||||
>>> paginator = ObjectPaginator(Article.objects.filter(id=0), 5)
|
>>> paginator = ObjectPaginator(Article.objects.filter(id=0), 5)
|
||||||
|
@ -71,6 +71,12 @@ class ClientTest(TestCase):
|
|||||||
self.assertEqual(response.template.name, 'POST Template')
|
self.assertEqual(response.template.name, 'POST Template')
|
||||||
self.failUnless('Data received' in response.content)
|
self.failUnless('Data received' in response.content)
|
||||||
|
|
||||||
|
def test_response_headers(self):
|
||||||
|
"Check the value of HTTP headers returned in a response"
|
||||||
|
response = self.client.get("/test_client/header_view/")
|
||||||
|
|
||||||
|
self.assertEquals(response['X-DJANGO-TEST'], 'Slartibartfast')
|
||||||
|
|
||||||
def test_raw_post(self):
|
def test_raw_post(self):
|
||||||
"POST raw data (with a content type) to a view"
|
"POST raw data (with a content type) to a view"
|
||||||
test_doc = """<?xml version="1.0" encoding="utf-8"?><library><book><title>Blink</title><author>Malcolm Gladwell</author></book></library>"""
|
test_doc = """<?xml version="1.0" encoding="utf-8"?><library><book><title>Blink</title><author>Malcolm Gladwell</author></book></library>"""
|
||||||
|
@ -5,6 +5,7 @@ import views
|
|||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
(r'^get_view/$', views.get_view),
|
(r'^get_view/$', views.get_view),
|
||||||
(r'^post_view/$', views.post_view),
|
(r'^post_view/$', views.post_view),
|
||||||
|
(r'^header_view/$', views.view_with_header),
|
||||||
(r'^raw_post_view/$', views.raw_post_view),
|
(r'^raw_post_view/$', views.raw_post_view),
|
||||||
(r'^redirect_view/$', views.redirect_view),
|
(r'^redirect_view/$', views.redirect_view),
|
||||||
(r'^permanent_redirect_view/$', redirect_to, { 'url': '/test_client/get_view/' }),
|
(r'^permanent_redirect_view/$', redirect_to, { 'url': '/test_client/get_view/' }),
|
||||||
|
@ -32,6 +32,12 @@ def post_view(request):
|
|||||||
|
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
|
def view_with_header(request):
|
||||||
|
"A view that has a custom header"
|
||||||
|
response = HttpResponse()
|
||||||
|
response['X-DJANGO-TEST'] = 'Slartibartfast'
|
||||||
|
return response
|
||||||
|
|
||||||
def raw_post_view(request):
|
def raw_post_view(request):
|
||||||
"""A view which expects raw XML to be posted and returns content extracted
|
"""A view which expects raw XML to be posted and returns content extracted
|
||||||
from the XML"""
|
from the XML"""
|
||||||
|
0
tests/regressiontests/admin_scripts/__init__.py
Normal file
0
tests/regressiontests/admin_scripts/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from django.core.management.base import AppCommand
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
|
class Command(AppCommand):
|
||||||
|
help = 'Test Application-based commands'
|
||||||
|
requires_model_validation = False
|
||||||
|
args = '[appname ...]'
|
||||||
|
|
||||||
|
def handle_app(self, app, **options):
|
||||||
|
print 'EXECUTE:AppCommand app=%s, options=%s' % (app, sorted(options.items()))
|
||||||
|
|
@ -0,0 +1,20 @@
|
|||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from optparse import make_option
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
option_list = BaseCommand.option_list + (
|
||||||
|
make_option('--option_a','-a', action='store', dest='option_a', default='1'),
|
||||||
|
make_option('--option_b','-b', action='store', dest='option_b', default='2'),
|
||||||
|
make_option('--option_c','-c', action='store', dest='option_c', default='3'),
|
||||||
|
)
|
||||||
|
help = 'Test basic commands'
|
||||||
|
requires_model_validation = False
|
||||||
|
args = '[labels ...]'
|
||||||
|
|
||||||
|
def handle(self, *labels, **options):
|
||||||
|
print 'EXECUTE:BaseCommand labels=%s, options=%s' % (labels, sorted(options.items()))
|
@ -0,0 +1,14 @@
|
|||||||
|
from django.core.management.base import LabelCommand
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
|
class Command(LabelCommand):
|
||||||
|
help = "Test Label-based commands"
|
||||||
|
requires_model_validation = False
|
||||||
|
args = '<label>'
|
||||||
|
|
||||||
|
def handle_label(self, label, **options):
|
||||||
|
print 'EXECUTE:LabelCommand label=%s, options=%s' % (label, sorted(options.items()))
|
@ -0,0 +1,14 @@
|
|||||||
|
from django.core.management.base import NoArgsCommand
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
|
class Command(NoArgsCommand):
|
||||||
|
help = "Test No-args commands"
|
||||||
|
requires_model_validation = False
|
||||||
|
|
||||||
|
|
||||||
|
def handle_noargs(self, **options):
|
||||||
|
print 'EXECUTE:NoArgsCommand options=%s' % sorted(options.items())
|
12
tests/regressiontests/admin_scripts/models.py
Normal file
12
tests/regressiontests/admin_scripts/models.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Article(models.Model):
|
||||||
|
headline = models.CharField(max_length=100, default='Default headline')
|
||||||
|
pub_date = models.DateTimeField()
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.headline
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('-pub_date', 'headline')
|
||||||
|
|
908
tests/regressiontests/admin_scripts/tests.py
Normal file
908
tests/regressiontests/admin_scripts/tests.py
Normal file
@ -0,0 +1,908 @@
|
|||||||
|
"""
|
||||||
|
A series of tests to establish that the command-line managment tools work as
|
||||||
|
advertised - especially with regards to the handling of the DJANGO_SETTINGS_MODULE
|
||||||
|
and default settings.py files.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from django import conf, bin, get_version
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
class AdminScriptTestCase(unittest.TestCase):
|
||||||
|
def write_settings(self, filename, apps=None):
|
||||||
|
test_dir = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
settings_file = open(os.path.join(test_dir,filename), 'w')
|
||||||
|
settings_file.write('# Settings file automatically generated by regressiontests.admin_scripts test case\n')
|
||||||
|
exports = [
|
||||||
|
'DATABASE_ENGINE',
|
||||||
|
'DATABASE_NAME',
|
||||||
|
'DATABASE_USER',
|
||||||
|
'DATABASE_PASSWORD',
|
||||||
|
'DATABASE_HOST',
|
||||||
|
'DATABASE_PORT',
|
||||||
|
'ROOT_URLCONF'
|
||||||
|
]
|
||||||
|
for s in exports:
|
||||||
|
if hasattr(settings,s):
|
||||||
|
settings_file.write("%s = '%s'\n" % (s, str(getattr(settings,s))))
|
||||||
|
|
||||||
|
if apps is None:
|
||||||
|
apps = ['django.contrib.auth', 'django.contrib.contenttypes', 'admin_scripts']
|
||||||
|
|
||||||
|
if apps:
|
||||||
|
settings_file.write("INSTALLED_APPS = %s\n" % apps)
|
||||||
|
|
||||||
|
settings_file.close()
|
||||||
|
|
||||||
|
def remove_settings(self, filename):
|
||||||
|
test_dir = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
os.remove(os.path.join(test_dir, filename))
|
||||||
|
# Also try to remove the pyc file; if it exists, it could
|
||||||
|
# mess up later tests that depend upon the .py file not existing
|
||||||
|
try:
|
||||||
|
os.remove(os.path.join(test_dir, filename + 'c'))
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run_test(self, script, args, settings_file=None, apps=None):
|
||||||
|
test_dir = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
project_dir = os.path.dirname(test_dir)
|
||||||
|
base_dir = os.path.dirname(project_dir)
|
||||||
|
|
||||||
|
# Build the command line
|
||||||
|
cmd = 'python "%s"' % script
|
||||||
|
cmd += ''.join(' %s' % arg for arg in args)
|
||||||
|
|
||||||
|
# Remember the old environment
|
||||||
|
old_django_settings_module = os.environ.get('DJANGO_SETTINGS_MODULE', None)
|
||||||
|
old_python_path = os.environ.get('PYTHONPATH', None)
|
||||||
|
old_cwd = os.getcwd()
|
||||||
|
|
||||||
|
# Set the test environment
|
||||||
|
if settings_file:
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = settings_file
|
||||||
|
elif 'DJANGO_SETTINGS_MODULE' in os.environ:
|
||||||
|
del os.environ['DJANGO_SETTINGS_MODULE']
|
||||||
|
|
||||||
|
os.environ['PYTHONPATH'] = os.pathsep.join([test_dir,base_dir])
|
||||||
|
|
||||||
|
# Move to the test directory and run
|
||||||
|
os.chdir(test_dir)
|
||||||
|
stdin, stdout, stderr = os.popen3(cmd)
|
||||||
|
out, err = stdout.read(), stderr.read()
|
||||||
|
|
||||||
|
# Restore the old environment
|
||||||
|
if old_django_settings_module:
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = old_django_settings_module
|
||||||
|
if old_python_path:
|
||||||
|
os.environ['PYTHONPATH'] = old_python_path
|
||||||
|
|
||||||
|
# Move back to the old working directory
|
||||||
|
os.chdir(old_cwd)
|
||||||
|
|
||||||
|
return out, err
|
||||||
|
|
||||||
|
def run_django_admin(self, args, settings_file=None):
|
||||||
|
bin_dir = os.path.dirname(bin.__file__)
|
||||||
|
return self.run_test(os.path.join(bin_dir,'django-admin.py'), args, settings_file)
|
||||||
|
|
||||||
|
def run_manage(self, args, settings_file=None):
|
||||||
|
conf_dir = os.path.dirname(conf.__file__)
|
||||||
|
template_manage_py = os.path.join(conf_dir, 'project_template', 'manage.py')
|
||||||
|
|
||||||
|
test_dir = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
test_manage_py = os.path.join(test_dir, 'manage.py')
|
||||||
|
shutil.copyfile(template_manage_py, test_manage_py)
|
||||||
|
|
||||||
|
stdout, stderr = self.run_test('./manage.py', args, settings_file)
|
||||||
|
|
||||||
|
# Cleanup - remove the generated manage.py script
|
||||||
|
os.remove(test_manage_py)
|
||||||
|
|
||||||
|
return stdout, stderr
|
||||||
|
|
||||||
|
def assertNoOutput(self, stream):
|
||||||
|
"Utility assertion: assert that the given stream is empty"
|
||||||
|
self.assertEquals(len(stream), 0, "Stream should be empty: actually contains '%s'" % stream)
|
||||||
|
def assertOutput(self, stream, msg):
|
||||||
|
"Utility assertion: assert that the given message exists in the output"
|
||||||
|
self.assertTrue(msg in stream, "'%s' does not match actual output text '%s'" % (msg, stream))
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# DJANGO ADMIN TESTS
|
||||||
|
# This first series of test classes checks the environment processing
|
||||||
|
# of the django-admin.py script
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class DjangoAdminNoSettings(AdminScriptTestCase):
|
||||||
|
"A series of tests for django-admin.py when there is no settings.py file."
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"no settings: django-admin builtin commands fail with an import error when no settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"no settings: django-admin builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"no settings: django-admin builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
|
||||||
|
class DjangoAdminDefaultSettings(AdminScriptTestCase):
|
||||||
|
"""A series of tests for django-admin.py when using a settings.py file that
|
||||||
|
contains the test application.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('settings.py')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('settings.py')
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"default: django-admin builtin commands fail with an import error when no settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
|
||||||
|
|
||||||
|
def test_builtin_with_settings(self):
|
||||||
|
"default: django-admin builtin commands succeed if settings are provided as argument"
|
||||||
|
args = ['sqlall','--settings=settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_environment(self):
|
||||||
|
"default: django-admin builtin commands succeed if settings are provided in the environment"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'settings')
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"default: django-admin builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"default: django-admin builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_custom_command(self):
|
||||||
|
"default: django-admin can't execute user commands"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_settings(self):
|
||||||
|
"default: django-admin can't execute user commands, even if settings are provided as argument"
|
||||||
|
args = ['noargs_command', '--settings=settings']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_environment(self):
|
||||||
|
"default: django-admin can't execute user commands, even if settings are provided in environment"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_django_admin(args,'settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
class DjangoAdminMinimalSettings(AdminScriptTestCase):
|
||||||
|
"""A series of tests for django-admin.py when using a settings.py file that
|
||||||
|
doesn't contain the test application.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('settings.py')
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"minimal: django-admin builtin commands fail with an import error when no settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
|
||||||
|
|
||||||
|
def test_builtin_with_settings(self):
|
||||||
|
"minimal: django-admin builtin commands fail if settings are provided as argument"
|
||||||
|
args = ['sqlall','--settings=settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'App with label admin_scripts could not be found')
|
||||||
|
|
||||||
|
def test_builtin_with_environment(self):
|
||||||
|
"minimal: django-admin builtin commands fail if settings are provided in the environment"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'App with label admin_scripts could not be found')
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"minimal: django-admin builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"minimal: django-admin builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_custom_command(self):
|
||||||
|
"minimal: django-admin can't execute user commands"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_settings(self):
|
||||||
|
"minimal: django-admin can't execute user commands, even if settings are provided as argument"
|
||||||
|
args = ['noargs_command', '--settings=settings']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_environment(self):
|
||||||
|
"minimal: django-admin can't execute user commands, even if settings are provided in environment"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_django_admin(args,'settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
class DjangoAdminAlternateSettings(AdminScriptTestCase):
|
||||||
|
"""A series of tests for django-admin.py when using a settings file
|
||||||
|
with a name other than 'settings.py'.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"alternate: django-admin builtin commands fail with an import error when no settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
|
||||||
|
|
||||||
|
def test_builtin_with_settings(self):
|
||||||
|
"alternate: django-admin builtin commands succeed if settings are provided as argument"
|
||||||
|
args = ['sqlall','--settings=alternate_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_environment(self):
|
||||||
|
"alternate: django-admin builtin commands succeed if settings are provided in the environment"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'alternate_settings')
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"alternate: django-admin builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"alternate: django-admin builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_custom_command(self):
|
||||||
|
"alternate: django-admin can't execute user commands"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_settings(self):
|
||||||
|
"alternate: django-admin can't execute user commands, even if settings are provided as argument"
|
||||||
|
args = ['noargs_command', '--settings=alternate_settings']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_environment(self):
|
||||||
|
"alternate: django-admin can't execute user commands, even if settings are provided in environment"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_django_admin(args,'alternate_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
|
||||||
|
class DjangoAdminMultipleSettings(AdminScriptTestCase):
|
||||||
|
"""A series of tests for django-admin.py when multiple settings files
|
||||||
|
(including the default 'settings.py') are available. The default settings
|
||||||
|
file is insufficient for performing the operations described, so the
|
||||||
|
alternate settings must be used by the running script.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
|
||||||
|
self.write_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('settings.py')
|
||||||
|
self.remove_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"alternate: django-admin builtin commands fail with an import error when no settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
|
||||||
|
|
||||||
|
def test_builtin_with_settings(self):
|
||||||
|
"alternate: django-admin builtin commands succeed if settings are provided as argument"
|
||||||
|
args = ['sqlall','--settings=alternate_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_environment(self):
|
||||||
|
"alternate: django-admin builtin commands succeed if settings are provided in the environment"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'alternate_settings')
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"alternate: django-admin builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"alternate: django-admin builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_django_admin(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_custom_command(self):
|
||||||
|
"alternate: django-admin can't execute user commands"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_settings(self):
|
||||||
|
"alternate: django-admin can't execute user commands, even if settings are provided as argument"
|
||||||
|
args = ['noargs_command', '--settings=alternate_settings']
|
||||||
|
out, err = self.run_django_admin(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_environment(self):
|
||||||
|
"alternate: django-admin can't execute user commands, even if settings are provided in environment"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_django_admin(args,'alternate_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# MANAGE.PY TESTS
|
||||||
|
# This next series of test classes checks the environment processing
|
||||||
|
# of the generated manage.py script
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
class ManageNoSettings(AdminScriptTestCase):
|
||||||
|
"A series of tests for manage.py when there is no settings.py file."
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"no settings: manage.py builtin commands fail with an import error when no settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"no settings: manage.py builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"no settings: manage.py builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
|
||||||
|
class ManageDefaultSettings(AdminScriptTestCase):
|
||||||
|
"""A series of tests for manage.py when using a settings.py file that
|
||||||
|
contains the test application.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('settings.py')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('settings.py')
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"default: manage.py builtin commands succeed when default settings are appropriate"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_settings(self):
|
||||||
|
"default: manage.py builtin commands succeed if settings are provided as argument"
|
||||||
|
args = ['sqlall','--settings=settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_environment(self):
|
||||||
|
"default: manage.py builtin commands succeed if settings are provided in the environment"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'settings')
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"default: manage.py builtin commands succeed if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"default: manage.py builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'bad_settings')
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_custom_command(self):
|
||||||
|
"default: manage.py can execute user commands when default settings are appropriate"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:NoArgsCommand")
|
||||||
|
|
||||||
|
def test_custom_command_with_settings(self):
|
||||||
|
"default: manage.py can execute user commands when settings are provided as argument"
|
||||||
|
args = ['noargs_command', '--settings=settings']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:NoArgsCommand")
|
||||||
|
|
||||||
|
def test_custom_command_with_environment(self):
|
||||||
|
"default: manage.py can execute user commands when settings are provided in environment"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args,'settings')
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:NoArgsCommand")
|
||||||
|
|
||||||
|
class ManageMinimalSettings(AdminScriptTestCase):
|
||||||
|
"""A series of tests for manage.py when using a settings.py file that
|
||||||
|
doesn't contain the test application.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('settings.py')
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"minimal: manage.py builtin commands fail with an import error when no settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'App with label admin_scripts could not be found')
|
||||||
|
|
||||||
|
def test_builtin_with_settings(self):
|
||||||
|
"minimal: manage.py builtin commands fail if settings are provided as argument"
|
||||||
|
args = ['sqlall','--settings=settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'App with label admin_scripts could not be found')
|
||||||
|
|
||||||
|
def test_builtin_with_environment(self):
|
||||||
|
"minimal: manage.py builtin commands fail if settings are provided in the environment"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'App with label admin_scripts could not be found')
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"minimal: manage.py builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"minimal: manage.py builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'App with label admin_scripts could not be found')
|
||||||
|
|
||||||
|
def test_custom_command(self):
|
||||||
|
"minimal: manage.py can't execute user commands without appropriate settings"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_settings(self):
|
||||||
|
"minimal: manage.py can't execute user commands, even if settings are provided as argument"
|
||||||
|
args = ['noargs_command', '--settings=settings']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_environment(self):
|
||||||
|
"minimal: manage.py can't execute user commands, even if settings are provided in environment"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args,'settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
class ManageAlternateSettings(AdminScriptTestCase):
|
||||||
|
"""A series of tests for manage.py when using a settings file
|
||||||
|
with a name other than 'settings.py'.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"alternate: manage.py builtin commands fail with an import error when no default settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_builtin_with_settings(self):
|
||||||
|
"alternate: manage.py builtin commands fail if settings are provided as argument but no defaults"
|
||||||
|
args = ['sqlall','--settings=alternate_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_builtin_with_environment(self):
|
||||||
|
"alternate: manage.py builtin commands fail if settings are provided in the environment but no defaults"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'alternate_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"alternate: manage.py builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"alternate: manage.py builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_custom_command(self):
|
||||||
|
"alternate: manage.py can't execute user commands"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_custom_command_with_settings(self):
|
||||||
|
"alternate: manage.py can't execute user commands, even if settings are provided as argument"
|
||||||
|
args = ['noargs_command', '--settings=alternate_settings']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
def test_custom_command_with_environment(self):
|
||||||
|
"alternate: manage.py can't execute user commands, even if settings are provided in environment"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args,'alternate_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Can't find the file 'settings.py' in the directory containing './manage.py'")
|
||||||
|
|
||||||
|
|
||||||
|
class ManageMultipleSettings(AdminScriptTestCase):
|
||||||
|
"""A series of tests for manage.py when multiple settings files
|
||||||
|
(including the default 'settings.py') are available. The default settings
|
||||||
|
file is insufficient for performing the operations described, so the
|
||||||
|
alternate settings must be used by the running script.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
|
||||||
|
self.write_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('settings.py')
|
||||||
|
self.remove_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def test_builtin_command(self):
|
||||||
|
"multiple: manage.py builtin commands fail with an import error when no settings provided"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'App with label admin_scripts could not be found.')
|
||||||
|
|
||||||
|
def test_builtin_with_settings(self):
|
||||||
|
"multiple: manage.py builtin commands succeed if settings are provided as argument"
|
||||||
|
args = ['sqlall','--settings=alternate_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, 'CREATE TABLE')
|
||||||
|
|
||||||
|
def test_builtin_with_environment(self):
|
||||||
|
"multiple: manage.py builtin commands fail if settings are provided in the environment"
|
||||||
|
# FIXME: This doesn't seem to be the correct output.
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'alternate_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, 'App with label admin_scripts could not be found.')
|
||||||
|
|
||||||
|
def test_builtin_with_bad_settings(self):
|
||||||
|
"multiple: manage.py builtin commands fail if settings file (from argument) doesn't exist"
|
||||||
|
args = ['sqlall','--settings=bad_settings', 'admin_scripts']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Could not import settings 'bad_settings'")
|
||||||
|
|
||||||
|
def test_builtin_with_bad_environment(self):
|
||||||
|
"multiple: manage.py builtin commands fail if settings file (from environment) doesn't exist"
|
||||||
|
args = ['sqlall','admin_scripts']
|
||||||
|
out, err = self.run_manage(args,'bad_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "App with label admin_scripts could not be found")
|
||||||
|
|
||||||
|
def test_custom_command(self):
|
||||||
|
"multiple: manage.py can't execute user commands using default settings"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
def test_custom_command_with_settings(self):
|
||||||
|
"multiple: manage.py can execute user commands if settings are provided as argument"
|
||||||
|
args = ['noargs_command', '--settings=alternate_settings']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:NoArgsCommand")
|
||||||
|
|
||||||
|
def test_custom_command_with_environment(self):
|
||||||
|
"multiple: manage.py can execute user commands if settings are provided in environment"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args,'alternate_settings')
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
self.assertOutput(err, "Unknown command: 'noargs_command'")
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# COMMAND PROCESSING TESTS
|
||||||
|
# Check that user-space commands are correctly handled - in particular,
|
||||||
|
# that arguments to the commands are correctly parsed and processed.
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
class CommandTypes(AdminScriptTestCase):
|
||||||
|
"Tests for the various types of base command types that can be defined."
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('settings.py')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('settings.py')
|
||||||
|
|
||||||
|
def test_version(self):
|
||||||
|
"--version is handled as a special case"
|
||||||
|
args = ['--version']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
# Only check the first part of the version number
|
||||||
|
self.assertOutput(out, get_version().split('-')[0])
|
||||||
|
|
||||||
|
def test_help(self):
|
||||||
|
"--help is handled as a special case"
|
||||||
|
args = ['--help']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertOutput(out, "Usage: manage.py [options]")
|
||||||
|
self.assertOutput(err, "Type 'manage.py help <subcommand>' for help on a specific subcommand.")
|
||||||
|
|
||||||
|
def test_specific_help(self):
|
||||||
|
"--help can be used on a specific command"
|
||||||
|
args = ['sqlall','--help']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s).")
|
||||||
|
|
||||||
|
def test_base_command(self):
|
||||||
|
"User BaseCommands can execute when a label is provided"
|
||||||
|
args = ['base_command','testlabel']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_base_command_no_label(self):
|
||||||
|
"User BaseCommands can execute when no labels are provided"
|
||||||
|
args = ['base_command']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_base_command_multiple_label(self):
|
||||||
|
"User BaseCommands can execute when no labels are provided"
|
||||||
|
args = ['base_command','testlabel','anotherlabel']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_base_command_with_option(self):
|
||||||
|
"User BaseCommands can execute with options when a label is provided"
|
||||||
|
args = ['base_command','testlabel','--option_a=x']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_base_command_with_options(self):
|
||||||
|
"User BaseCommands can execute with multiple options when a label is provided"
|
||||||
|
args = ['base_command','testlabel','-a','x','--option_b=y']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_noargs(self):
|
||||||
|
"NoArg Commands can be executed"
|
||||||
|
args = ['noargs_command']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_noargs_with_args(self):
|
||||||
|
"NoArg Commands raise an error if an argument is provided"
|
||||||
|
args = ['noargs_command','argument']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertOutput(err, "Error: Command doesn't accept any arguments")
|
||||||
|
|
||||||
|
def test_app_command(self):
|
||||||
|
"User AppCommands can execute when a single app name is provided"
|
||||||
|
args = ['app_command', 'auth']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
|
||||||
|
self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
|
||||||
|
self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_app_command_no_apps(self):
|
||||||
|
"User AppCommands raise an error when no app name is provided"
|
||||||
|
args = ['app_command']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertOutput(err, 'Error: Enter at least one appname.')
|
||||||
|
|
||||||
|
def test_app_command_multiple_apps(self):
|
||||||
|
"User AppCommands raise an error when multiple app names are provided"
|
||||||
|
args = ['app_command','auth','contenttypes']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
|
||||||
|
self.assertOutput(out, os.sep.join(['django','contrib','auth','models.pyc']) + "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.contenttypes.models'")
|
||||||
|
self.assertOutput(out, os.sep.join(['django','contrib','contenttypes','models.py']))
|
||||||
|
self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_app_command_invalid_appname(self):
|
||||||
|
"User AppCommands can execute when a single app name is provided"
|
||||||
|
args = ['app_command', 'NOT_AN_APP']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertOutput(err, "App with label NOT_AN_APP could not be found")
|
||||||
|
|
||||||
|
def test_app_command_some_invalid_appnames(self):
|
||||||
|
"User AppCommands can execute when some of the provided app names are invalid"
|
||||||
|
args = ['app_command', 'auth', 'NOT_AN_APP']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertOutput(err, "App with label NOT_AN_APP could not be found")
|
||||||
|
|
||||||
|
def test_label_command(self):
|
||||||
|
"User LabelCommands can execute when a label is provided"
|
||||||
|
args = ['label_command','testlabel']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_label_command_no_label(self):
|
||||||
|
"User LabelCommands raise an error if no label is provided"
|
||||||
|
args = ['label_command']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertOutput(err, 'Enter at least one label')
|
||||||
|
|
||||||
|
def test_label_command_multiple_label(self):
|
||||||
|
"User LabelCommands are executed multiple times if multiple labels are provided"
|
||||||
|
args = ['label_command','testlabel','anotherlabel']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]")
|
||||||
|
|
||||||
|
class ArgumentOrder(AdminScriptTestCase):
|
||||||
|
"""Tests for 2-stage argument parsing scheme.
|
||||||
|
|
||||||
|
django-admin command arguments are parsed in 2 parts; the core arguments
|
||||||
|
(--settings, --traceback and --pythonpath) are parsed using a Lax parser.
|
||||||
|
This Lax parser ignores any unknown options. Then the full settings are
|
||||||
|
passed to the command parser, which extracts commands of interest to the
|
||||||
|
individual command.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
|
||||||
|
self.write_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_settings('settings.py')
|
||||||
|
self.remove_settings('alternate_settings.py')
|
||||||
|
|
||||||
|
def test_setting_then_option(self):
|
||||||
|
"Options passed after settings are correctly handled"
|
||||||
|
args = ['base_command','testlabel','--settings=alternate_settings','--option_a=x']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_setting_then_short_option(self):
|
||||||
|
"Short options passed after settings are correctly handled"
|
||||||
|
args = ['base_command','testlabel','--settings=alternate_settings','--option_a=x']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_option_then_setting(self):
|
||||||
|
"Options passed before settings are correctly handled"
|
||||||
|
args = ['base_command','testlabel','--option_a=x','--settings=alternate_settings']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_short_option_then_setting(self):
|
||||||
|
"Short options passed before settings are correctly handled"
|
||||||
|
args = ['base_command','testlabel','-a','x','--settings=alternate_settings']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")
|
||||||
|
|
||||||
|
def test_option_then_setting_then_option(self):
|
||||||
|
"Options are correctly handled when they are passed before and after a setting"
|
||||||
|
args = ['base_command','testlabel','--option_a=x','--settings=alternate_settings','--option_b=y']
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]")
|
||||||
|
|
@ -537,6 +537,12 @@ u'123'
|
|||||||
from django.template.defaultfilters import *
|
from django.template.defaultfilters import *
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import doctest
|
import doctest
|
||||||
doctest.testmod()
|
doctest.testmod()
|
||||||
|
@ -25,7 +25,7 @@ class FileUploadTests(TestCase):
|
|||||||
file2.seek(0)
|
file2.seek(0)
|
||||||
|
|
||||||
# This file contains chinese symbols for a name.
|
# This file contains chinese symbols for a name.
|
||||||
file3 = open(os.path.join(tdir, u'test_中文_Orl\u00e9ans.jpg'), 'w+b')
|
file3 = open(os.path.join(tdir, u'test_中文_Orl\u00e9ans.jpg'.encode('utf-8')), 'w+b')
|
||||||
file3.write('b' * (2 ** 10))
|
file3.write('b' * (2 ** 10))
|
||||||
file3.seek(0)
|
file3.seek(0)
|
||||||
|
|
||||||
@ -156,3 +156,26 @@ class FileUploadTests(TestCase):
|
|||||||
{'f': open(f.name)}
|
{'f': open(f.name)}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_fileupload_getlist(self):
|
||||||
|
file1 = tempfile.NamedTemporaryFile()
|
||||||
|
file1.write('a' * (2 ** 23))
|
||||||
|
|
||||||
|
file2 = tempfile.NamedTemporaryFile()
|
||||||
|
file2.write('a' * (2 * 2 ** 18))
|
||||||
|
|
||||||
|
file2a = tempfile.NamedTemporaryFile()
|
||||||
|
file2a.write('a' * (5 * 2 ** 20))
|
||||||
|
|
||||||
|
response = self.client.post('/file_uploads/getlist_count/', {
|
||||||
|
'file1': open(file1.name),
|
||||||
|
'field1': u'test',
|
||||||
|
'field2': u'test3',
|
||||||
|
'field3': u'test5',
|
||||||
|
'field4': u'test6',
|
||||||
|
'field5': u'test7',
|
||||||
|
'file2': (open(file2.name), open(file2a.name))
|
||||||
|
})
|
||||||
|
got = simplejson.loads(response.content)
|
||||||
|
|
||||||
|
self.assertEqual(got.get('file1'), 1)
|
||||||
|
self.assertEqual(got.get('file2'), 2)
|
||||||
|
@ -7,4 +7,5 @@ urlpatterns = patterns('',
|
|||||||
(r'^echo/$', views.file_upload_echo),
|
(r'^echo/$', views.file_upload_echo),
|
||||||
(r'^quota/$', views.file_upload_quota),
|
(r'^quota/$', views.file_upload_quota),
|
||||||
(r'^quota/broken/$', views.file_upload_quota_broken),
|
(r'^quota/broken/$', views.file_upload_quota_broken),
|
||||||
|
(r'^getlist_count/$', views.file_upload_getlist_count),
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ def file_upload_view(request):
|
|||||||
if isinstance(form_data.get('file_field'), UploadedFile) and isinstance(form_data['name'], unicode):
|
if isinstance(form_data.get('file_field'), UploadedFile) and isinstance(form_data['name'], unicode):
|
||||||
# If a file is posted, the dummy client should only post the file name,
|
# If a file is posted, the dummy client should only post the file name,
|
||||||
# not the full path.
|
# not the full path.
|
||||||
if os.path.dirname(form_data['file_field'].file_name) != '':
|
if os.path.dirname(form_data['file_field'].name) != '':
|
||||||
return HttpResponseServerError()
|
return HttpResponseServerError()
|
||||||
return HttpResponse('')
|
return HttpResponse('')
|
||||||
else:
|
else:
|
||||||
@ -29,7 +29,7 @@ def file_upload_view_verify(request):
|
|||||||
form_data.update(request.FILES)
|
form_data.update(request.FILES)
|
||||||
|
|
||||||
# Check to see if unicode names worked out.
|
# Check to see if unicode names worked out.
|
||||||
if not request.FILES['file_unicode'].file_name.endswith(u'test_\u4e2d\u6587_Orl\xe9ans.jpg'):
|
if not request.FILES['file_unicode'].name.endswith(u'test_\u4e2d\u6587_Orl\xe9ans.jpg'):
|
||||||
return HttpResponseServerError()
|
return HttpResponseServerError()
|
||||||
|
|
||||||
for key, value in form_data.items():
|
for key, value in form_data.items():
|
||||||
@ -51,7 +51,7 @@ def file_upload_echo(request):
|
|||||||
"""
|
"""
|
||||||
Simple view to echo back info about uploaded files for tests.
|
Simple view to echo back info about uploaded files for tests.
|
||||||
"""
|
"""
|
||||||
r = dict([(k, f.file_name) for k, f in request.FILES.items()])
|
r = dict([(k, f.name) for k, f in request.FILES.items()])
|
||||||
return HttpResponse(simplejson.dumps(r))
|
return HttpResponse(simplejson.dumps(r))
|
||||||
|
|
||||||
def file_upload_quota(request):
|
def file_upload_quota(request):
|
||||||
@ -68,3 +68,13 @@ def file_upload_quota_broken(request):
|
|||||||
response = file_upload_echo(request)
|
response = file_upload_echo(request)
|
||||||
request.upload_handlers.insert(0, QuotaUploadHandler())
|
request.upload_handlers.insert(0, QuotaUploadHandler())
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def file_upload_getlist_count(request):
|
||||||
|
"""
|
||||||
|
Check the .getlist() function to ensure we receive the correct number of files.
|
||||||
|
"""
|
||||||
|
file_counts = {}
|
||||||
|
|
||||||
|
for key in request.FILES.keys():
|
||||||
|
file_counts[key] = len(request.FILES.getlist(key))
|
||||||
|
return HttpResponse(simplejson.dumps(file_counts))
|
||||||
|
@ -800,10 +800,10 @@ Traceback (most recent call last):
|
|||||||
ValidationError: [u'The submitted file is empty.']
|
ValidationError: [u'The submitted file is empty.']
|
||||||
|
|
||||||
>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content')))
|
>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content')))
|
||||||
<class 'django.newforms.fields.UploadedFile'>
|
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||||
|
|
||||||
>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf'))
|
>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf'))
|
||||||
<class 'django.newforms.fields.UploadedFile'>
|
<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
|
||||||
|
|
||||||
# URLField ##################################################################
|
# URLField ##################################################################
|
||||||
|
|
||||||
|
@ -6,6 +6,12 @@ import datetime
|
|||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
class Place(models.Model):
|
class Place(models.Model):
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
address = models.CharField(max_length=80)
|
address = models.CharField(max_length=80)
|
||||||
|
@ -8,6 +8,12 @@ import pickle
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.query import Q
|
from django.db.models.query import Q
|
||||||
|
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
name = models.CharField(max_length=10)
|
name = models.CharField(max_length=10)
|
||||||
parent = models.ForeignKey('self', blank=True, null=True,
|
parent = models.ForeignKey('self', blank=True, null=True,
|
||||||
@ -805,5 +811,14 @@ Bug #7371
|
|||||||
>>> Related.objects.order_by('custom')
|
>>> Related.objects.order_by('custom')
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
Bug #7448, #7707 -- Complex objects should be converted to strings before being
|
||||||
|
used in lookups.
|
||||||
|
>>> Item.objects.filter(created__in=[time1, time2])
|
||||||
|
[<Item: one>, <Item: two>]
|
||||||
|
|
||||||
|
Bug #7698 -- People like to slice with '0' as the high-water mark.
|
||||||
|
>>> Item.objects.all()[0:0]
|
||||||
|
[]
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
|
||||||
|
@ -44,8 +44,15 @@
|
|||||||
>>> d.keys()
|
>>> d.keys()
|
||||||
[2, 1]
|
[2, 1]
|
||||||
>>> real_dict = dict(tuples)
|
>>> real_dict = dict(tuples)
|
||||||
>>> real_dict.values()
|
>>> sorted(real_dict.values())
|
||||||
['one', 'second-two']
|
['one', 'second-two']
|
||||||
>>> d.values()
|
>>> d.values() # Here the order of SortedDict values *is* what we are testing
|
||||||
['second-two', 'one']
|
['second-two', 'one']
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Python 2.3 doesn't have sorted()
|
||||||
|
try:
|
||||||
|
sorted
|
||||||
|
except NameError:
|
||||||
|
from django.utils.itercompat import sorted
|
||||||
|
|
15
tests/regressiontests/utils/itercompat.py
Normal file
15
tests/regressiontests/utils/itercompat.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
"""
|
||||||
|
# Tests of the utils itercompat library.
|
||||||
|
|
||||||
|
>>> from django.utils.itercompat import sorted as compat_sorted
|
||||||
|
|
||||||
|
# Check the replacement version of sorted
|
||||||
|
>>> x = [5,1,4,2,3]
|
||||||
|
>>> y = compat_sorted(x)
|
||||||
|
>>> print y
|
||||||
|
[1, 2, 3, 4, 5]
|
||||||
|
|
||||||
|
>>> print x
|
||||||
|
[5, 1, 4, 2, 3]
|
||||||
|
|
||||||
|
"""
|
@ -8,12 +8,14 @@ from django.utils import html, checksums
|
|||||||
|
|
||||||
import timesince
|
import timesince
|
||||||
import datastructures
|
import datastructures
|
||||||
|
import itercompat
|
||||||
from decorators import DecoratorFromMiddlewareTests
|
from decorators import DecoratorFromMiddlewareTests
|
||||||
|
|
||||||
# Extra tests
|
# Extra tests
|
||||||
__test__ = {
|
__test__ = {
|
||||||
'timesince': timesince,
|
'timesince': timesince,
|
||||||
'datastructures': datastructures,
|
'datastructures': datastructures,
|
||||||
|
'itercompat': itercompat,
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestUtilsHtml(TestCase):
|
class TestUtilsHtml(TestCase):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user