1
0
mirror of https://github.com/django/django.git synced 2025-07-05 02:09:13 +00:00

[soc2009/multidb] Updated to trunk r11603. This includes a critical security fix.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11614 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2009-10-09 22:16:10 +00:00
parent 6f983870f3
commit 2bc7422b52
63 changed files with 733 additions and 431 deletions

View File

@ -202,6 +202,7 @@ answer newbie questions, and generally made Django that much better:
Kieran Holland <http://www.kieranholland.com> Kieran Holland <http://www.kieranholland.com>
Sung-Jin Hong <serialx.net@gmail.com> Sung-Jin Hong <serialx.net@gmail.com>
Leo "hylje" Honkanen <sealage@gmail.com> Leo "hylje" Honkanen <sealage@gmail.com>
Tareque Hossain <http://www.codexn.com>
Richard House <Richard.House@i-logue.com> Richard House <Richard.House@i-logue.com>
Robert Rock Howard <http://djangomojo.com/> Robert Rock Howard <http://djangomojo.com/>
John Huddleston <huddlej@wwu.edu> John Huddleston <huddlej@wwu.edu>
@ -241,6 +242,7 @@ answer newbie questions, and generally made Django that much better:
Igor Kolar <ike@email.si> Igor Kolar <ike@email.si>
Gasper Koren Gasper Koren
Martin Kosír <martin@martinkosir.net> Martin Kosír <martin@martinkosir.net>
Arthur Koziel <http://arthurkoziel.com>
Meir Kriheli <http://mksoft.co.il/> Meir Kriheli <http://mksoft.co.il/>
Bruce Kroeze <http://coderseye.com/> Bruce Kroeze <http://coderseye.com/>
krzysiek.pawlik@silvermedia.pl krzysiek.pawlik@silvermedia.pl
@ -401,7 +403,7 @@ answer newbie questions, and generally made Django that much better:
Vasiliy Stavenko <stavenko@gmail.com> Vasiliy Stavenko <stavenko@gmail.com>
Thomas Steinacher <http://www.eggdrop.ch/> Thomas Steinacher <http://www.eggdrop.ch/>
Johan C. Stöver <johan@nilling.nl> Johan C. Stöver <johan@nilling.nl>
nowell strite Nowell Strite <http://nowell.strite.org/>
Thomas Stromberg <tstromberg@google.com> Thomas Stromberg <tstromberg@google.com>
Pascal Varet Pascal Varet
SuperJared SuperJared

View File

@ -391,7 +391,7 @@ msgstr "הוספת %s"
#: contrib/admin/options.py:1003 #: contrib/admin/options.py:1003
#, python-format #, python-format
msgid "%(name)s object with primary key %(key)r does not exist." msgid "%(name)s object with primary key %(key)r does not exist."
msgstr "הפריט %(name)s עם המקש %(key)r אינו קיים." msgstr "הפריט %(name)s עם המפתח הראשי %(key)r אינו קיים."
#: contrib/admin/options.py:860 #: contrib/admin/options.py:860
#, python-format #, python-format

View File

@ -300,7 +300,7 @@ class AdminSite(object):
user = authenticate(username=username, password=password) user = authenticate(username=username, password=password)
if user is None: if user is None:
message = ERROR_MESSAGE message = ERROR_MESSAGE
if u'@' in username: if username is not None and u'@' in username:
# Mistakenly entered e-mail address instead of username? Look it up. # Mistakenly entered e-mail address instead of username? Look it up.
try: try:
user = User.objects.get(email=username) user = User.objects.get(email=username)

View File

@ -15,6 +15,7 @@
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %} {% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
</tr></thead> </tr></thead>
<tbody>
{% for inline_admin_form in inline_admin_formset %} {% for inline_admin_form in inline_admin_formset %}
{% if inline_admin_form.form.non_field_errors %} {% if inline_admin_form.form.non_field_errors %}
<tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr> <tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
@ -57,7 +58,7 @@
</tr> </tr>
{% endfor %} {% endfor %}
</tbody>
</table> </table>
</fieldset> </fieldset>

View File

@ -22,7 +22,7 @@ def paginator_number(cl,i):
elif i == cl.page_num: elif i == cl.page_num:
return mark_safe(u'<span class="this-page">%d</span> ' % (i+1)) return mark_safe(u'<span class="this-page">%d</span> ' % (i+1))
else: else:
return mark_safe(u'<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1)) return mark_safe(u'<a href="%s"%s>%d</a> ' % (escape(cl.get_query_string({PAGE_VAR: i})), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1))
paginator_number = register.simple_tag(paginator_number) paginator_number = register.simple_tag(paginator_number)
def pagination(cl): def pagination(cl):
@ -265,7 +265,7 @@ def date_hierarchy(cl):
day_lookup = cl.params.get(day_field) day_lookup = cl.params.get(day_field)
year_month_format, month_day_format = get_partial_date_formats() year_month_format, month_day_format = get_partial_date_formats()
link = lambda d: mark_safe(cl.get_query_string(d, [field_generic])) link = lambda d: cl.get_query_string(d, [field_generic])
if year_lookup and month_lookup and day_lookup: if year_lookup and month_lookup and day_lookup:
day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup)) day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup))

View File

@ -7,6 +7,7 @@ import copy
from django import forms from django import forms
from django.forms.widgets import RadioFieldRenderer from django.forms.widgets import RadioFieldRenderer
from django.forms.util import flatatt from django.forms.util import flatatt
from django.utils.html import escape
from django.utils.text import truncate_words from django.utils.text import truncate_words
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -148,7 +149,7 @@ class ForeignKeyRawIdWidget(forms.TextInput):
def label_for_value(self, value): def label_for_value(self, value):
key = self.rel.get_related_field().name key = self.rel.get_related_field().name
obj = self.rel.to._default_manager.get(**{key: value}) obj = self.rel.to._default_manager.get(**{key: value})
return '&nbsp;<strong>%s</strong>' % truncate_words(obj, 14) return '&nbsp;<strong>%s</strong>' % escape(truncate_words(obj, 14))
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
""" """

View File

@ -1,11 +1,12 @@
try: try:
from functools import update_wrapper from functools import update_wrapper, wraps
except ImportError: except ImportError:
from django.utils.functional import update_wrapper # Python 2.3, 2.4 fallback. from django.utils.functional import update_wrapper, wraps # Python 2.3, 2.4 fallback.
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.http import urlquote from django.utils.http import urlquote
from django.utils.decorators import auto_adapt_to_methods
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
""" """
@ -13,9 +14,19 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
redirecting to the log-in page if necessary. The test should be a callable redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes. that takes the user object and returns True if the user passes.
""" """
def decorate(view_func): if not login_url:
return _CheckLogin(view_func, test_func, login_url, redirect_field_name) from django.conf import settings
return decorate login_url = settings.LOGIN_URL
def decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
path = urlquote(request.get_full_path())
tup = login_url, redirect_field_name, path
return HttpResponseRedirect('%s?%s=%s' % tup)
return wraps(view_func)(_wrapped_view)
return auto_adapt_to_methods(decorator)
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME): def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
""" """
@ -36,46 +47,3 @@ def permission_required(perm, login_url=None):
enabled, redirecting to the log-in page if necessary. enabled, redirecting to the log-in page if necessary.
""" """
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url) return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
class _CheckLogin(object):
"""
Class that checks that the user passes the given test, redirecting to
the log-in page if necessary. If the test is passed, the view function
is invoked. The test should be a callable that takes the user object
and returns True if the user passes.
We use a class here so that we can define __get__. This way, when a
_CheckLogin object is used as a method decorator, the view function
is properly bound to its instance.
"""
def __init__(self, view_func, test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
self.view_func = view_func
self.test_func = test_func
self.login_url = login_url
self.redirect_field_name = redirect_field_name
# We can't blindly apply update_wrapper because it udpates __dict__ and
# if the view function is already a _CheckLogin object then
# self.test_func and friends will get stomped. However, we also can't
# *not* update the wrapper's dict because then view function attributes
# don't get updated into the wrapper. So we need to split the
# difference: don't let update_wrapper update __dict__, but then update
# the (parts of) __dict__ that we care about ourselves.
update_wrapper(self, view_func, updated=())
for k in view_func.__dict__:
if k not in self.__dict__:
self.__dict__[k] = view_func.__dict__[k]
def __get__(self, obj, cls=None):
view_func = self.view_func.__get__(obj, cls)
return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name)
def __call__(self, request, *args, **kwargs):
if self.test_func(request.user):
return self.view_func(request, *args, **kwargs)
path = urlquote(request.get_full_path())
tup = self.login_url, self.redirect_field_name, path
return HttpResponseRedirect('%s?%s=%s' % tup)

View File

@ -10,10 +10,6 @@ from django.contrib.auth.tests.tokens import TOKEN_GENERATOR_TESTS
__test__ = { __test__ = {
'BASIC_TESTS': BASIC_TESTS, 'BASIC_TESTS': BASIC_TESTS,
'PASSWORDRESET_TESTS': PasswordResetTest,
'FORM_TESTS': FORM_TESTS, 'FORM_TESTS': FORM_TESTS,
'TOKEN_GENERATOR_TESTS': TOKEN_GENERATOR_TESTS, 'TOKEN_GENERATOR_TESTS': TOKEN_GENERATOR_TESTS,
'CHANGEPASSWORD_TESTS': ChangePasswordTest,
'LOGIN_TESTS': LoginTest,
'LOGOUT_TESTS': LogoutTest,
} }

View File

@ -20,6 +20,7 @@ class CommentsAdmin(admin.ModelAdmin):
list_filter = ('submit_date', 'site', 'is_public', 'is_removed') list_filter = ('submit_date', 'site', 'is_public', 'is_removed')
date_hierarchy = 'submit_date' date_hierarchy = 'submit_date'
ordering = ('-submit_date',) ordering = ('-submit_date',)
raw_id_fields = ('user',)
search_fields = ('comment', 'user__username', 'user_name', 'user_email', 'user_url', 'ip_address') search_fields = ('comment', 'user__username', 'user_name', 'user_email', 'user_url', 'ip_address')
# Only register the default admin if the model is the built-in comment model # Only register the default admin if the model is the built-in comment model

View File

@ -32,11 +32,20 @@ elif SpatialBackend.oracle:
def convert_extent(clob): def convert_extent(clob):
if clob: if clob:
# Oracle returns a polygon for the extent, we construct # Generally, Oracle returns a polygon for the extent -- however,
# the 4-tuple from the coordinates in the polygon. # it can return a single point if there's only one Point in the
poly = SpatialBackend.Geometry(clob.read()) # table.
shell = poly.shell ext_geom = SpatialBackend.Geometry(clob.read())
ll, ur = shell[0], shell[2] gtype = str(ext_geom.geom_type)
if gtype == 'Polygon':
# Construct the 4-tuple from the coordinates in the polygon.
shell = ext_geom.shell
ll, ur = shell[0][:2], shell[2][:2]
elif gtype == 'Point':
ll = ext_geom.coords[:2]
ur = ll
else:
raise Exception('Unexpected geometry type returned for extent: %s' % gtype)
xmin, ymin = ll xmin, ymin = ll
xmax, ymax = ur xmax, ymax = ur
return (xmin, ymin, xmax, ymax) return (xmin, ymin, xmax, ymax)

View File

@ -131,7 +131,7 @@ class Command(InspectCommand):
if srid != 4326: extra_params['srid'] = srid if srid != 4326: extra_params['srid'] = srid
else: else:
try: try:
field_type = connection.introspection.data_types_reverse[row[1]] field_type = connection.introspection.get_field_type(row[1], row)
except KeyError: except KeyError:
field_type = 'TextField' field_type = 'TextField'
comment_notes.append('This field type is a guess.') comment_notes.append('This field type is a guess.')

View File

@ -1,4 +1,5 @@
import cStringIO, zipfile import cStringIO, zipfile
from django.conf import settings
from django.http import HttpResponse from django.http import HttpResponse
from django.template import loader from django.template import loader
@ -6,7 +7,7 @@ def compress_kml(kml):
"Returns compressed KMZ from the given KML string." "Returns compressed KMZ from the given KML string."
kmz = cStringIO.StringIO() kmz = cStringIO.StringIO()
zf = zipfile.ZipFile(kmz, 'a', zipfile.ZIP_DEFLATED) zf = zipfile.ZipFile(kmz, 'a', zipfile.ZIP_DEFLATED)
zf.writestr('doc.kml', kml) zf.writestr('doc.kml', kml.encode(settings.DEFAULT_CHARSET))
zf.close() zf.close()
kmz.seek(0) kmz.seek(0)
return kmz.read() return kmz.read()

View File

@ -1,6 +1,7 @@
import os, unittest import os, unittest
from django.contrib.gis.db.backend import SpatialBackend from django.contrib.gis.db.backend import SpatialBackend
from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_postgis from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_postgis, no_spatialite
from django.contrib.gis.shortcuts import render_to_kmz
from models import City from models import City
class GeoRegressionTests(unittest.TestCase): class GeoRegressionTests(unittest.TestCase):
@ -16,3 +17,19 @@ class GeoRegressionTests(unittest.TestCase):
self.assertEqual(pnt, City.objects.get(name='Pueblo').point) self.assertEqual(pnt, City.objects.get(name='Pueblo').point)
City.objects.filter(name='Pueblo').update(point=bak) City.objects.filter(name='Pueblo').update(point=bak)
self.assertEqual(bak, City.objects.get(name='Pueblo').point) self.assertEqual(bak, City.objects.get(name='Pueblo').point)
def test02_kmz(self):
"Testing `render_to_kmz` with non-ASCII data, see #11624."
name = '\xc3\x85land Islands'.decode('iso-8859-1')
places = [{'name' : name,
'description' : name,
'kml' : '<Point><coordinates>5.0,23.0</coordinates></Point>'
}]
kmz = render_to_kmz('gis/kml/placemarks.kml', {'places' : places})
@no_spatialite
def test03_extent(self):
"Testing `extent` on a table with a single point, see #11827."
pnt = City.objects.get(name='Pueblo').point
ref_ext = (pnt.x, pnt.y, pnt.x, pnt.y)
self.assertEqual(ref_ext, City.objects.filter(name='Pueblo').extent())

View File

@ -293,7 +293,6 @@ class RelatedGeoModelTest(unittest.TestCase):
self.assertEqual(4, len(coll)) self.assertEqual(4, len(coll))
self.assertEqual(ref_geom, coll) self.assertEqual(ref_geom, coll)
# TODO: Related tests for KML, GML, and distance lookups. # TODO: Related tests for KML, GML, and distance lookups.
def suite(): def suite():

View File

@ -27,7 +27,7 @@ class FRPhoneNumberField(Field):
'0X XX XX XX XX'. '0X XX XX XX XX'.
""" """
default_error_messages = { default_error_messages = {
'invalid': u'Phone numbers must be in 0X XX XX XX XX format.', 'invalid': _('Phone numbers must be in 0X XX XX XX XX format.'),
} }
def clean(self, value): def clean(self, value):

View File

@ -134,8 +134,8 @@ class ModPythonRequest(http.HttpRequest):
if not hasattr(self, '_meta'): if not hasattr(self, '_meta'):
self._meta = { self._meta = {
'AUTH_TYPE': self._req.ap_auth_type, 'AUTH_TYPE': self._req.ap_auth_type,
'CONTENT_LENGTH': self._req.clength, # This may be wrong 'CONTENT_LENGTH': self._req.headers_in.get('content-length', 0),
'CONTENT_TYPE': self._req.content_type, # This may be wrong 'CONTENT_TYPE': self._req.headers_in.get('content-type'),
'GATEWAY_INTERFACE': 'CGI/1.1', 'GATEWAY_INTERFACE': 'CGI/1.1',
'PATH_INFO': self.path_info, 'PATH_INFO': self.path_info,
'PATH_TRANSLATED': None, # Not supported 'PATH_TRANSLATED': None, # Not supported

View File

@ -261,6 +261,82 @@ class ManagementUtility(object):
sys.exit(1) sys.exit(1)
return klass return klass
def autocomplete(self):
"""
Output completion suggestions for BASH.
The output of this function is passed to BASH's `COMREPLY` variable and
treated as completion suggestions. `COMREPLY` expects a space
separated string as the result.
The `COMP_WORDS` and `COMP_CWORD` BASH environment variables are used
to get information about the cli input. Please refer to the BASH
man-page for more information about this variables.
Subcommand options are saved as pairs. A pair consists of
the long option string (e.g. '--exclude') and a boolean
value indicating if the option requires arguments. When printing to
stdout, a equal sign is appended to options which require arguments.
Note: If debugging this function, it is recommended to write the debug
output in a separate file. Otherwise the debug output will be treated
and formatted as potential completion suggestions.
"""
# Don't complete if user hasn't sourced bash_completion file.
if not os.environ.has_key('DJANGO_AUTO_COMPLETE'):
return
cwords = os.environ['COMP_WORDS'].split()[1:]
cword = int(os.environ['COMP_CWORD'])
try:
curr = cwords[cword-1]
except IndexError:
curr = ''
subcommands = get_commands().keys() + ['help']
options = [('--help', None)]
# subcommand
if cword == 1:
print ' '.join(filter(lambda x: x.startswith(curr), subcommands))
# subcommand options
# special case: the 'help' subcommand has no options
elif cwords[0] in subcommands and cwords[0] != 'help':
subcommand_cls = self.fetch_command(cwords[0])
# special case: 'runfcgi' stores additional options as
# 'key=value' pairs
if cwords[0] == 'runfcgi':
from django.core.servers.fastcgi import FASTCGI_OPTIONS
options += [(k, 1) for k in FASTCGI_OPTIONS]
# special case: add the names of installed apps to options
elif cwords[0] in ('dumpdata', 'reset', 'sql', 'sqlall',
'sqlclear', 'sqlcustom', 'sqlindexes',
'sqlreset', 'sqlsequencereset', 'test'):
try:
from django.conf import settings
# Get the last part of the dotted path as the app name.
options += [(a.split('.')[-1], 0) for a in settings.INSTALLED_APPS]
except ImportError:
# Fail silently if DJANGO_SETTINGS_MODULE isn't set. The
# user will find out once they execute the command.
pass
options += [(s_opt.get_opt_string(), s_opt.nargs) for s_opt in
subcommand_cls.option_list]
# filter out previously specified options from available options
prev_opts = [x.split('=')[0] for x in cwords[1:cword-1]]
options = filter(lambda (x, v): x not in prev_opts, options)
# filter options by current input
options = [(k, v) for k, v in options if k.startswith(curr)]
for option in options:
opt_label = option[0]
# append '=' to options which require args
if option[1]:
opt_label += '='
print opt_label
sys.exit(1)
def execute(self): def execute(self):
""" """
Given the command-line arguments, this figures out which subcommand is Given the command-line arguments, this figures out which subcommand is
@ -272,6 +348,7 @@ class ManagementUtility(object):
parser = LaxOptionParser(usage="%prog subcommand [options] [args]", parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
version=get_version(), version=get_version(),
option_list=BaseCommand.option_list) option_list=BaseCommand.option_list)
self.autocomplete()
try: try:
options, args = parser.parse_args(self.argv) options, args = parser.parse_args(self.argv)
handle_default_options(options) handle_default_options(options)

View File

@ -398,7 +398,9 @@ def copy_helper(style, app_or_project, name, directory, other_name=''):
if subdir.startswith('.'): if subdir.startswith('.'):
del subdirs[i] del subdirs[i]
for f in files: for f in files:
if f.endswith('.pyc'): if not f.endswith('.py'):
# Ignore .pyc, .pyo, .py.class etc, as they cause various
# breakages.
continue continue
path_old = os.path.join(d, f) path_old = os.path.join(d, f)
path_new = os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name)) path_new = os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name))

View File

@ -82,7 +82,7 @@ class Command(NoArgsCommand):
extra_params['db_column'] = column_name extra_params['db_column'] = column_name
else: else:
try: try:
field_type = connection.introspection.data_types_reverse[row[1]] field_type = connection.introspection.get_field_type(row[1], row)
except KeyError: except KeyError:
field_type = 'TextField' field_type = 'TextField'
comment_notes.append('This field type is a guess.') comment_notes.append('This field type is a guess.')

View File

@ -287,8 +287,17 @@ class RegexURLResolver(object):
candidate = result % unicode_kwargs candidate = result % unicode_kwargs
if re.search(u'^%s' % pattern, candidate, re.UNICODE): if re.search(u'^%s' % pattern, candidate, re.UNICODE):
return candidate return candidate
# lookup_view can be URL label, or dotted path, or callable, Any of
# these can be passed in at the top, but callables are not friendly in
# error messages.
m = getattr(lookup_view, '__module__', None)
n = getattr(lookup_view, '__name__', None)
if m is not None and n is not None:
lookup_view_s = "%s.%s" % (m, n)
else:
lookup_view_s = lookup_view
raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword " raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
"arguments '%s' not found." % (lookup_view, args, kwargs)) "arguments '%s' not found." % (lookup_view_s, args, kwargs))
def resolve(path, urlconf=None): def resolve(path, urlconf=None):
return get_resolver(urlconf).resolve(path) return get_resolver(urlconf).resolve(path)

View File

@ -480,6 +480,14 @@ class BaseDatabaseIntrospection(object):
def __init__(self, connection): def __init__(self, connection):
self.connection = connection self.connection = connection
def get_field_type(self, data_type, description):
"""Hook for a database backend to use the cursor description to
match a Django field type to a database column.
For Oracle, the column data_type on its own is insufficient to
distinguish between a FloatField and IntegerField, for example."""
return self.data_types_reverse[data_type]
def table_name_converter(self, name): def table_name_converter(self, name):
"""Apply a conversion to the name for the purposes of comparison. """Apply a conversion to the name for the purposes of comparison.

View File

@ -36,6 +36,14 @@ DatabaseError = Database.DatabaseError
IntegrityError = Database.IntegrityError IntegrityError = Database.IntegrityError
# Check whether cx_Oracle was compiled with the WITH_UNICODE option. This will
# also be True in Python 3.0.
if int(Database.version.split('.', 1)[0]) >= 5 and not hasattr(Database, 'UNICODE'):
convert_unicode = force_unicode
else:
convert_unicode = smart_str
class DatabaseFeatures(BaseDatabaseFeatures): class DatabaseFeatures(BaseDatabaseFeatures):
empty_fetchmany_value = () empty_fetchmany_value = ()
needs_datetime_string_cast = False needs_datetime_string_cast = False
@ -176,10 +184,10 @@ WHEN (new.%(col_name)s IS NULL)
return "RETURNING %s INTO %%s", (InsertIdVar(),) return "RETURNING %s INTO %%s", (InsertIdVar(),)
def savepoint_create_sql(self, sid): def savepoint_create_sql(self, sid):
return "SAVEPOINT " + self.quote_name(sid) return convert_unicode("SAVEPOINT " + self.quote_name(sid))
def savepoint_rollback_sql(self, sid): def savepoint_rollback_sql(self, sid):
return "ROLLBACK TO SAVEPOINT " + self.quote_name(sid) return convert_unicode("ROLLBACK TO SAVEPOINT " + self.quote_name(sid))
def sql_flush(self, style, tables, sequences): def sql_flush(self, style, tables, sequences):
# Return a list of 'TRUNCATE x;', 'TRUNCATE y;', # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
@ -310,7 +318,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def _cursor(self): def _cursor(self):
cursor = None cursor = None
if not self._valid_connection(): if not self._valid_connection():
conn_string = self._connect_string() conn_string = convert_unicode(self._connect_string())
self.connection = Database.connect(conn_string, **self.settings_dict['DATABASE_OPTIONS']) self.connection = Database.connect(conn_string, **self.settings_dict['DATABASE_OPTIONS'])
cursor = FormatStylePlaceholderCursor(self.connection) cursor = FormatStylePlaceholderCursor(self.connection)
# Set oracle date to ansi date format. This only needs to execute # Set oracle date to ansi date format. This only needs to execute
@ -361,7 +369,8 @@ class OracleParam(object):
if hasattr(param, 'bind_parameter'): if hasattr(param, 'bind_parameter'):
self.smart_str = param.bind_parameter(cursor) self.smart_str = param.bind_parameter(cursor)
else: else:
self.smart_str = smart_str(param, cursor.charset, strings_only) self.smart_str = convert_unicode(param, cursor.charset,
strings_only)
if hasattr(param, 'input_size'): if hasattr(param, 'input_size'):
# If parameter has `input_size` attribute, use that. # If parameter has `input_size` attribute, use that.
self.input_size = param.input_size self.input_size = param.input_size
@ -429,7 +438,7 @@ class FormatStylePlaceholderCursor(object):
# is being passed to SQL*Plus. # is being passed to SQL*Plus.
if query.endswith(';') or query.endswith('/'): if query.endswith(';') or query.endswith('/'):
query = query[:-1] query = query[:-1]
query = smart_str(query, self.charset) % tuple(args) query = convert_unicode(query % tuple(args), self.charset)
self._guess_input_sizes([params]) self._guess_input_sizes([params])
try: try:
return self.cursor.execute(query, self._param_generator(params)) return self.cursor.execute(query, self._param_generator(params))
@ -451,7 +460,7 @@ class FormatStylePlaceholderCursor(object):
# is being passed to SQL*Plus. # is being passed to SQL*Plus.
if query.endswith(';') or query.endswith('/'): if query.endswith(';') or query.endswith('/'):
query = query[:-1] query = query[:-1]
query = smart_str(query, self.charset) % tuple(args) query = convert_unicode(query % tuple(args), self.charset)
formatted = [self._format_params(i) for i in params] formatted = [self._format_params(i) for i in params]
self._guess_input_sizes(formatted) self._guess_input_sizes(formatted)
try: try:

View File

@ -26,6 +26,14 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
except AttributeError: except AttributeError:
pass pass
def get_field_type(self, data_type, description):
# If it's a NUMBER with scale == 0, consider it an IntegerField
if data_type == cx_Oracle.NUMBER and description[5] == 0:
return 'IntegerField'
else:
return super(DatabaseIntrospection, self).get_field_type(
data_type, description)
def get_table_list(self, cursor): def get_table_list(self, cursor):
"Returns a list of table names in the current database." "Returns a list of table names in the current database."
cursor.execute("SELECT TABLE_NAME FROM USER_TABLES") cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")

View File

@ -62,7 +62,7 @@ class SQLEvaluator(object):
else: else:
sql, params = '%s', (child,) sql, params = '%s', (child,)
if hasattr(child, 'children') > 1: if len(getattr(child, 'children', [])) > 1:
format = '(%s)' format = '(%s)'
else: else:
format = '%s' format = '%s'

View File

@ -29,7 +29,6 @@ from django.utils.encoding import 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, DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
from django.core.files.uploadedfile import SimpleUploadedFile as UploadedFile
__all__ = ( __all__ = (
'Field', 'CharField', 'IntegerField', 'Field', 'CharField', 'IntegerField',
@ -422,7 +421,7 @@ class RegexField(CharField):
email_re = re.compile( email_re = re.compile(
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
r')@(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE) # domain
class EmailField(RegexField): class EmailField(RegexField):
default_error_messages = { default_error_messages = {
@ -533,7 +532,7 @@ class ImageField(FileField):
url_re = re.compile( url_re = re.compile(
r'^https?://' # http:// or https:// r'^https?://' # http:// or https://
r'(?:(?:[A-Z0-9]+(?:-*[A-Z0-9]+)*\.)+[A-Z]{2,6}|' #domain... r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' #domain...
r'localhost|' #localhost... r'localhost|' #localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port r'(?::\d+)?' # optional port

View File

@ -243,13 +243,13 @@ class BaseForm(StrAndUnicode):
value = getattr(self, 'clean_%s' % name)() value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value self.cleaned_data[name] = value
except ValidationError, e: except ValidationError, e:
self._errors[name] = e.messages self._errors[name] = self.error_class(e.messages)
if name in self.cleaned_data: if name in self.cleaned_data:
del self.cleaned_data[name] del self.cleaned_data[name]
try: try:
self.cleaned_data = self.clean() self.cleaned_data = self.clean()
except ValidationError, e: except ValidationError, e:
self._errors[NON_FIELD_ERRORS] = e.messages self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
if self._errors: if self._errors:
delattr(self, 'cleaned_data') delattr(self, 'cleaned_data')

View File

@ -275,9 +275,10 @@ class FileInput(Input):
class Textarea(Widget): class Textarea(Widget):
def __init__(self, attrs=None): def __init__(self, attrs=None):
# The 'rows' and 'cols' attributes are required for HTML correctness. # The 'rows' and 'cols' attributes are required for HTML correctness.
self.attrs = {'cols': '40', 'rows': '10'} default_attrs = {'cols': '40', 'rows': '10'}
if attrs: if attrs:
self.attrs.update(attrs) default_attrs.update(attrs)
super(Textarea, self).__init__(default_attrs)
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
if value is None: value = '' if value is None: value = ''

View File

@ -117,7 +117,7 @@ class SortedDict(dict):
return iter(self.keyOrder) return iter(self.keyOrder)
def values(self): def values(self):
return [super(SortedDict, self).__getitem__(k) for k in self.keyOrder] return map(super(SortedDict, self).__getitem__, self.keyOrder)
def itervalues(self): def itervalues(self):
for key in self.keyOrder: for key in self.keyOrder:

View File

@ -2,60 +2,86 @@
import types import types
try: try:
from functools import wraps from functools import wraps, update_wrapper
except ImportError: except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback. from django.utils.functional import wraps, update_wrapper # Python 2.3, 2.4 fallback.
class MethodDecoratorAdaptor(object):
"""
Generic way of creating decorators that adapt to being
used on methods
"""
def __init__(self, decorator, func):
update_wrapper(self, func)
# NB: update the __dict__ first, *then* set
# our own .func and .decorator, in case 'func' is actually
# another MethodDecoratorAdaptor object, which has its
# 'func' and 'decorator' attributes in its own __dict__
self.decorator = decorator
self.func = func
def __call__(self, *args, **kwargs):
return self.decorator(self.func)(*args, **kwargs)
def __get__(self, instance, owner):
return self.decorator(self.func.__get__(instance, owner))
def auto_adapt_to_methods(decorator):
"""
Takes a decorator function, and returns a decorator-like callable that can
be used on methods as well as functions.
"""
def adapt(func):
return MethodDecoratorAdaptor(decorator, func)
return wraps(decorator)(adapt)
def decorator_from_middleware_with_args(middleware_class):
"""
Like decorator_from_middleware, but returns a function
that accepts the arguments to be passed to the middleware_class.
Use like::
cache_page = decorator_from_middleware_with_args(CacheMiddleware)
# ...
@cache_page(3600)
def my_view(request):
# ...
"""
return make_middleware_decorator(middleware_class)
def decorator_from_middleware(middleware_class): def decorator_from_middleware(middleware_class):
""" """
Given a middleware class (not an instance), returns a view decorator. This Given a middleware class (not an instance), returns a view decorator. This
lets you use middleware functionality on a per-view basis. lets you use middleware functionality on a per-view basis. The middleware
is created with no params passed.
""" """
def _decorator_from_middleware(*args, **kwargs): return make_middleware_decorator(middleware_class)()
# For historical reasons, these "decorators" are also called as
# dec(func, *args) instead of dec(*args)(func). We handle both forms
# for backwards compatibility.
has_func = True
try:
view_func = kwargs.pop('view_func')
except KeyError:
if len(args):
view_func, args = args[0], args[1:]
else:
has_func = False
if not (has_func and isinstance(view_func, types.FunctionType)):
# We are being called as a decorator.
if has_func:
args = (view_func,) + args
middleware = middleware_class(*args, **kwargs)
def decorator_func(fn): def make_middleware_decorator(middleware_class):
return _decorator_from_middleware(fn, *args, **kwargs) def _make_decorator(*m_args, **m_kwargs):
return decorator_func middleware = middleware_class(*m_args, **m_kwargs)
def _decorator(view_func):
middleware = middleware_class(*args, **kwargs) def _wrapped_view(request, *args, **kwargs):
if hasattr(middleware, 'process_request'):
def _wrapped_view(request, *args, **kwargs): result = middleware.process_request(request)
if hasattr(middleware, 'process_request'):
result = middleware.process_request(request)
if result is not None:
return result
if hasattr(middleware, 'process_view'):
result = middleware.process_view(request, view_func, args, kwargs)
if result is not None:
return result
try:
response = view_func(request, *args, **kwargs)
except Exception, e:
if hasattr(middleware, 'process_exception'):
result = middleware.process_exception(request, e)
if result is not None: if result is not None:
return result return result
raise if hasattr(middleware, 'process_view'):
if hasattr(middleware, 'process_response'): result = middleware.process_view(request, view_func, args, kwargs)
result = middleware.process_response(request, response) if result is not None:
if result is not None: return result
return result try:
return response response = view_func(request, *args, **kwargs)
return wraps(view_func)(_wrapped_view) except Exception, e:
return _decorator_from_middleware if hasattr(middleware, 'process_exception'):
result = middleware.process_exception(request, e)
if result is not None:
return result
raise
if hasattr(middleware, 'process_response'):
result = middleware.process_response(request, response)
if result is not None:
return result
return response
return wraps(view_func)(_wrapped_view)
return auto_adapt_to_methods(_decorator)
return _make_decorator

View File

@ -16,11 +16,37 @@ try:
except ImportError: except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback. from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
from django.utils.decorators import decorator_from_middleware from django.utils.decorators import decorator_from_middleware_with_args, auto_adapt_to_methods
from django.utils.cache import patch_cache_control, add_never_cache_headers from django.utils.cache import patch_cache_control, add_never_cache_headers
from django.middleware.cache import CacheMiddleware from django.middleware.cache import CacheMiddleware
cache_page = decorator_from_middleware(CacheMiddleware) def cache_page(*args, **kwargs):
# We need backwards compatibility with code which spells it this way:
# def my_view(): pass
# my_view = cache_page(my_view, 123)
# and this way:
# my_view = cache_page(123)(my_view)
# and this:
# my_view = cache_page(my_view, 123, key_prefix="foo")
# and this:
# my_view = cache_page(123, key_prefix="foo")(my_view)
# and possibly this way (?):
# my_view = cache_page(123, my_view)
# We also add some asserts to give better error messages in case people are
# using other ways to call cache_page that no longer work.
key_prefix = kwargs.pop('key_prefix', None)
assert not kwargs, "The only keyword argument accepted is key_prefix"
if len(args) > 1:
assert len(args) == 2, "cache_page accepts at most 2 arguments"
if callable(args[0]):
return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[1], key_prefix=key_prefix)(args[0])
elif callable(args[1]):
return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[0], key_prefix=key_prefix)(args[1])
else:
assert False, "cache_page must be passed either a single argument (timeout) or a view function and a timeout"
else:
return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[0], key_prefix=key_prefix)
def cache_control(**kwargs): def cache_control(**kwargs):
@ -33,7 +59,7 @@ def cache_control(**kwargs):
return wraps(viewfunc)(_cache_controlled) return wraps(viewfunc)(_cache_controlled)
return _cache_controller return auto_adapt_to_methods(_cache_controller)
def never_cache(view_func): def never_cache(view_func):
""" """
@ -45,3 +71,4 @@ def never_cache(view_func):
add_never_cache_headers(response) add_never_cache_headers(response)
return response return response
return wraps(view_func)(_wrapped_view_func) return wraps(view_func)(_wrapped_view_func)
never_cache = auto_adapt_to_methods(never_cache)

View File

@ -126,7 +126,7 @@ Here's the default ``static/directory_index.html`` template:
listings. The template that was loaded had to be called listings. The template that was loaded had to be called
``static/directory_listing`` (with no ``.html`` extension). For backwards ``static/directory_listing`` (with no ``.html`` extension). For backwards
compatibility with earlier versions, Django will still load templates with compatibility with earlier versions, Django will still load templates with
the older (no extension) name, but it will prefer a the the older (no extension) name, but it will prefer the
``directory_index.html`` version. ``directory_index.html`` version.
Limiting use to DEBUG=True Limiting use to DEBUG=True

View File

@ -23,3 +23,4 @@ the hood".
committers committers
release-process release-process
deprecation deprecation
svn

View File

@ -114,14 +114,9 @@ not; in either case there comes a time when the branch is no longer
being actively worked on by any developer. At this point the branch is being actively worked on by any developer. At this point the branch is
considered closed. considered closed.
Unfortunately, Subversion has no standard way of indicating Unfortunately, Subversion has no standard way of indicating this. As a
this. Generally, you can recognize a dead branch by viewing it through workaround, branches of Django which are closed and no longer
the web interface, which lists the date of the most recent change to maintained are moved into the directory ``django/branches/attic``.
the branch. Branches which have gone more than a month or two with no
activity can usually be assumed to be closed. In the future, the
layout of branches in the repository may be rearranged to make it
easier to tell which branches are still active (e.g., by moving closed
or abandoned branches into the ``django/branches/attic`` directory).
For reference, the following are branches whose code eventually became For reference, the following are branches whose code eventually became
part of Django itself, and so are no longer separately maintained: part of Django itself, and so are no longer separately maintained:
@ -184,6 +179,9 @@ were never finished:
* ``sqlalchemy`` * ``sqlalchemy``
All of the above-mentioned branches now reside in
``django/branches/attic``.
Support and bugfix branches Support and bugfix branches
--------------------------- ---------------------------
@ -201,7 +199,7 @@ will be created there approximately one month after each new Django
release. For example, shortly after the release of Django 1.0, the release. For example, shortly after the release of Django 1.0, the
branch ``django/branches/releases/1.0.X`` was created to receive bug branch ``django/branches/releases/1.0.X`` was created to receive bug
fixes, and shortly after the release of Django 1.1 the branch fixes, and shortly after the release of Django 1.1 the branch
``django/branches/releases/1.1.X`` will be created. ``django/branches/releases/1.1.X`` was created.
Prior to the Django 1.0 release, these branches were maintaind within Prior to the Django 1.0 release, these branches were maintaind within
the top-level ``django/branches`` directory, and so the following the top-level ``django/branches`` directory, and so the following

View File

@ -644,8 +644,7 @@ Save these changes and start a new Python interactive shell by running
>>> Poll.objects.filter(question__startswith='What') >>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>] [<Poll: What's up?>]
# Get the poll whose year is 2007. Of course, if you're going through this # Get the poll whose year is 2007.
# tutorial in another year, change as appropriate.
>>> Poll.objects.get(pub_date__year=2007) >>> Poll.objects.get(pub_date__year=2007)
<Poll: What's up?> <Poll: What's up?>

View File

@ -25,21 +25,26 @@ Django's admin interface.
Overview Overview
======== ========
There are five steps in activating the Django admin site: There are six steps in activating the Django admin site:
1. Add ``django.contrib.admin`` to your ``INSTALLED_APPS`` setting. 1. Add ``'django.contrib.admin'`` to your :setting:`INSTALLED_APPS`
setting.
2. Determine which of your application's models should be editable in the 2. Admin has two dependencies - ``django.contrib.auth`` and
``django.contrib.contenttypes``. If these applications are not
in your :setting:`INSTALLED_APPS` list, add them.
3. Determine which of your application's models should be editable in the
admin interface. admin interface.
3. For each of those models, optionally create a ``ModelAdmin`` class that 4. For each of those models, optionally create a ``ModelAdmin`` class that
encapsulates the customized admin functionality and options for that encapsulates the customized admin functionality and options for that
particular model. particular model.
4. Instantiate an ``AdminSite`` and tell it about each of your models and 5. Instantiate an ``AdminSite`` and tell it about each of your models and
``ModelAdmin`` classes. ``ModelAdmin`` classes.
5. Hook the ``AdminSite`` instance into your URLconf. 6. Hook the ``AdminSite`` instance into your URLconf.
Other topics Other topics
------------ ------------
@ -845,7 +850,7 @@ the admin application URL dispatching handler to render the pages that deal
with model instances CRUD operations. As a result, completely overriding these with model instances CRUD operations. As a result, completely overriding these
methods will significantly change the behavior of the admin application. methods will significantly change the behavior of the admin application.
One comon reason for overriding these methods is to augment the context data One common reason for overriding these methods is to augment the context data
that is provided to the template that renders the view. In the following that is provided to the template that renders the view. In the following
example, the change view is overridden so that the rendered template is example, the change view is overridden so that the rendered template is
provided some extra mapping data that would not otherwise be available:: provided some extra mapping data that would not otherwise be available::

View File

@ -89,7 +89,7 @@ different ways you can specify which object to attach to:
Displaying comments Displaying comments
------------------- -------------------
To get a the list of comments for some object, use :ttag:`get_comment_list`:: To get the list of comments for some object, use :ttag:`get_comment_list`::
{% get_comment_list for [object] as [varname] %} {% get_comment_list for [object] as [varname] %}

View File

@ -256,7 +256,6 @@ Here's a sample configuration which uses a MySQL option file::
} }
} }
# my.cnf # my.cnf
[client] [client]
database = DATABASE_NAME database = DATABASE_NAME
@ -481,6 +480,10 @@ version of the driver should **not** be used with Django;
``cx_Oracle`` 5.0.1 resolved this issue, so if you'd like to use a ``cx_Oracle`` 5.0.1 resolved this issue, so if you'd like to use a
more recent ``cx_Oracle``, use version 5.0.1. more recent ``cx_Oracle``, use version 5.0.1.
``cx_Oracle`` 5.0.1 or greater can optionally be compiled with the
``WITH_UNICODE`` environment variable. This is recommended but not
required.
.. _`Oracle Database Server`: http://www.oracle.com/ .. _`Oracle Database Server`: http://www.oracle.com/
.. _`cx_Oracle`: http://cx-oracle.sourceforge.net/ .. _`cx_Oracle`: http://cx-oracle.sourceforge.net/

View File

@ -24,6 +24,13 @@ commonly used groups of widgets:
Password input: ``<input type='password' ...>`` Password input: ``<input type='password' ...>``
Takes one optional argument:
.. attribute:: PasswordInput.render_value
Determines whether the widget will have a value filled in when the
form is re-displayed after a validation error (default is ``True``).
.. class:: HiddenInput .. class:: HiddenInput
Hidden input: ``<input type='hidden' ...>`` Hidden input: ``<input type='hidden' ...>``
@ -88,6 +95,14 @@ commonly used groups of widgets:
Checkbox: ``<input type='checkbox' ...>`` Checkbox: ``<input type='checkbox' ...>``
Takes one optional argument:
.. attribute:: CheckboxInput.check_test
A callable that takes the value of the CheckBoxInput
and returns ``True`` if the checkbox should be checked for
that value.
.. class:: Select .. class:: Select
Select widget: ``<select><option ...>...</select>`` Select widget: ``<select><option ...>...</select>``
@ -144,6 +159,16 @@ commonly used groups of widgets:
.. versionchanged:: 1.1 .. versionchanged:: 1.1
The ``date_format`` and ``time_format`` arguments were not supported in Django 1.0. The ``date_format`` and ``time_format`` arguments were not supported in Django 1.0.
.. class:: SelectDateWidget
Wrapper around three select widgets: one each for month, day, and year.
Note that this widget lives in a separate file from the standard widgets.
.. code-block:: python
from django.forms.extras.widgets import SelectDateWidget
date = forms.DateField(widget=SelectDateWidget())
Specifying widgets Specifying widgets
------------------ ------------------

View File

@ -844,7 +844,7 @@ define the details of how the relation works.
current date/time to be chosen. current date/time to be chosen.
Instead of a dictionary this can also be a :class:`~django.db.models.Q` Instead of a dictionary this can also be a :class:`~django.db.models.Q`
object (an object with a :meth:`get_sql` method) for more complex queries. object for more :ref:`complex queries <complex-lookups-with-q>`.
``limit_choices_to`` has no effect on the inline FormSets that are created ``limit_choices_to`` has no effect on the inline FormSets that are created
to display related objects in the admin. to display related objects in the admin.

View File

@ -19,6 +19,17 @@ Available ``Meta`` options
If ``True``, this model will be an :ref:`abstract base class <abstract-base-classes>`. If ``True``, this model will be an :ref:`abstract base class <abstract-base-classes>`.
``app_label``
-------------
.. attribute:: Options.app_label
If a model exists outside of the standard :file:`models.py` (for instance, if
the app's models are in submodules of ``myapp.models``), the model must define
which app it is part of::
app_label = 'myapp'
``db_table`` ``db_table``
------------ ------------

View File

@ -20,7 +20,7 @@ Throughout this reference we'll use the :ref:`example weblog models
When QuerySets are evaluated When QuerySets are evaluated
============================ ============================
Internally, a ``QuerySet`` can be constructed, filter, sliced, and generally Internally, a ``QuerySet`` can be constructed, filtered, sliced, and generally
passed around without actually hitting the database. No database activity passed around without actually hitting the database. No database activity
actually occurs until you do something to evaluate the queryset. actually occurs until you do something to evaluate the queryset.
@ -1530,9 +1530,10 @@ regex
Case-sensitive regular expression match. Case-sensitive regular expression match.
The regular expression syntax is that of the database backend in use. In the The regular expression syntax is that of the database backend in use.
case of SQLite, which doesn't natively support regular-expression lookups, the In the case of SQLite, which has no built in regular expression support,
syntax is that of Python's ``re`` module. this feature is provided by a (Python) user-defined REGEXP function, and
the regular expression syntax is therefore that of Python's ``re`` module.
Example:: Example::

View File

@ -232,16 +232,7 @@ Methods
Returns ``True`` if the request was made via an ``XMLHttpRequest``, by Returns ``True`` if the request was made via an ``XMLHttpRequest``, by
checking the ``HTTP_X_REQUESTED_WITH`` header for the string checking the ``HTTP_X_REQUESTED_WITH`` header for the string
``'XMLHttpRequest'``. The following major JavaScript libraries all send this ``'XMLHttpRequest'``. Most modern JavaScript libraries send this header.
header:
* jQuery
* Dojo
* MochiKit
* MooTools
* Prototype
* YUI
If you write your own XMLHttpRequest call (on the browser side), you'll If you write your own XMLHttpRequest call (on the browser side), you'll
have to set this header manually if you want ``is_ajax()`` to work. have to set this header manually if you want ``is_ajax()`` to work.
@ -271,7 +262,7 @@ a subclass of dictionary. Exceptions are outlined here:
Returns the value for the given key. If the key has more than one value, Returns the value for the given key. If the key has more than one value,
``__getitem__()`` returns the last value. Raises ``__getitem__()`` returns the last value. Raises
``django.utils.datastructure.MultiValueDictKeyError`` if the key does not ``django.utils.datastructures.MultiValueDictKeyError`` if the key does not
exist. (This is a subclass of Python's standard ``KeyError``, so you can exist. (This is a subclass of Python's standard ``KeyError``, so you can
stick to catching ``KeyError``.) stick to catching ``KeyError``.)

View File

@ -8,6 +8,9 @@ A list of all the signals that Django sends.
.. seealso:: .. seealso::
See the documentation on the :ref:`signal dispatcher <topics-signals>` for
information regarding how to register for and receive signals.
The :ref:`comment framework <ref-contrib-comments-index>` sends a :ref:`set The :ref:`comment framework <ref-contrib-comments-index>` sends a :ref:`set
of comment-related signals <ref-contrib-comments-signals>`. of comment-related signals <ref-contrib-comments-signals>`.

View File

@ -115,7 +115,7 @@ it was available in a model's pre-save signal handler.
In Django 1.1 the file is saved as part of saving the model in the database, so In Django 1.1 the file is saved as part of saving the model in the database, so
the actual file name used on disk cannot be relied on until *after* the model the actual file name used on disk cannot be relied on until *after* the model
has been saved saved. has been saved.
Changes to how model formsets are saved Changes to how model formsets are saved
--------------------------------------- ---------------------------------------
@ -289,7 +289,6 @@ test client:
* The test :class:`Client` now can automatically follow redirects with the * The test :class:`Client` now can automatically follow redirects with the
``follow`` argument to :meth:`Client.get` and :meth:`Client.post`. This ``follow`` argument to :meth:`Client.get` and :meth:`Client.post`. This
makes testing views that issue redirects simpler. makes testing views that issue redirects simpler.
* It's now easier to get at the template context in the response returned * It's now easier to get at the template context in the response returned
the test client: you'll simply access the context as the test client: you'll simply access the context as
``request.context[key]``. The old way, which treats ``request.context`` as ``request.context[key]``. The old way, which treats ``request.context`` as

View File

@ -29,13 +29,16 @@ Installation
Authentication support is bundled as a Django application in Authentication support is bundled as a Django application in
``django.contrib.auth``. To install it, do the following: ``django.contrib.auth``. To install it, do the following:
1. Put ``'django.contrib.auth'`` in your :setting:`INSTALLED_APPS` setting. 1. Put ``'django.contrib.auth'`` and ``'django.contrib.contenttypes'`` in
your :setting:`INSTALLED_APPS` setting.
(The :class:`~django.contrib.auth.models.Permisson` model in
:mod:`django.contrib.auth` depends on :mod:`django.contrib.contenttypes`.)
2. Run the command ``manage.py syncdb``. 2. Run the command ``manage.py syncdb``.
Note that the default :file:`settings.py` file created by Note that the default :file:`settings.py` file created by
:djadmin:`django-admin.py startproject` includes ``'django.contrib.auth'`` in :djadmin:`django-admin.py startproject` includes ``'django.contrib.auth'`` and
:setting:`INSTALLED_APPS` for convenience. If your :setting:`INSTALLED_APPS` ``'django.contrib.contenttypes'`` in :setting:`INSTALLED_APPS` for convenience.
already contains ``'django.contrib.auth'``, feel free to run If your :setting:`INSTALLED_APPS` already contains these apps, feel free to run
:djadmin:`manage.py syncdb` again; you can run that command as many times as :djadmin:`manage.py syncdb` again; you can run that command as many times as
you'd like, and each time it'll only install what's needed. you'd like, and each time it'll only install what's needed.
@ -209,14 +212,15 @@ Methods
.. method:: models.User.has_perm(perm) .. method:: models.User.has_perm(perm)
Returns ``True`` if the user has the specified permission, where perm is Returns ``True`` if the user has the specified permission, where perm is
in the format ``"<application name>.<lowercased model name>"``. If the in the format ``"<app label>.<permission codename>"``.
user is inactive, this method will always return ``False``. If the user is inactive, this method will always return ``False``.
.. method:: models.User.has_perms(perm_list) .. method:: models.User.has_perms(perm_list)
Returns ``True`` if the user has each of the specified permissions, Returns ``True`` if the user has each of the specified permissions,
where each perm is in the format ``"package.codename"``. If the user is where each perm is in the format
inactive, this method will always return ``False``. ``"<app label>.<permission codename>"``. If the user is inactive,
this method will always return ``False``.
.. method:: models.User.has_module_perms(package_name) .. method:: models.User.has_module_perms(package_name)
@ -686,8 +690,10 @@ The login_required decorator
* If the user isn't logged in, redirect to * If the user isn't logged in, redirect to
:setting:`settings.LOGIN_URL <LOGIN_URL>` (``/accounts/login/`` by :setting:`settings.LOGIN_URL <LOGIN_URL>` (``/accounts/login/`` by
default), passing the current absolute URL in the query string as default), passing the current absolute URL in the query string. The
``next`` or the value of ``redirect_field_name``. For example: name of the GET argument is determined by the ``redirect_field_name``
argument provided to the decorator. The default argument name is
``next``. For example:
``/accounts/login/?next=/polls/3/``. ``/accounts/login/?next=/polls/3/``.
* If the user is logged in, execute the view normally. The view code is * If the user is logged in, execute the view normally. The view code is
@ -1057,8 +1063,8 @@ The permission_required decorator
my_view = permission_required('polls.can_vote')(my_view) my_view = permission_required('polls.can_vote')(my_view)
As for the :meth:`User.has_perm` method, permission names take the form As for the :meth:`User.has_perm` method, permission names take the form
``"<application name>.<lowercased model name>"`` (i.e. ``polls.choice`` for ``"<app label>.<permission codename>"`` (i.e. ``polls.can_vote`` for a
a ``Choice`` model in the ``polls`` application). permission on a model in the ``polls`` application).
Note that :func:`~django.contrib.auth.decorators.permission_required()` Note that :func:`~django.contrib.auth.decorators.permission_required()`
also takes an optional ``login_url`` parameter. Example:: also takes an optional ``login_url`` parameter. Example::

View File

@ -361,6 +361,17 @@ then requests to ``/foo/1/`` and ``/foo/23/`` will be cached separately, as
you may expect. But once a particular URL (e.g., ``/foo/23/``) has been you may expect. But once a particular URL (e.g., ``/foo/23/``) has been
requested, subsequent requests to that URL will use the cache. requested, subsequent requests to that URL will use the cache.
``cache_page`` can also take an optional keyword argument, ``key_prefix``, which
works in the same way as the ``CACHE_MIDDLEWARE_KEY_PREFIX`` setting for the
middleware. It can be used like this::
my_view = cache_page(my_view, 60 * 15, key_prefix="site1")
Or, using Python 2.4's decorator syntax::
@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
Specifying per-view cache in the URLconf Specifying per-view cache in the URLconf
---------------------------------------- ----------------------------------------

View File

@ -622,6 +622,8 @@ To avoid this problem, simply save the ``QuerySet`` and reuse it::
>>> print [p.headline for p in queryset] # Evaluate the query set. >>> print [p.headline for p in queryset] # Evaluate the query set.
>>> print [p.pub_date for p in queryset] # Re-use the cache from the evaluation. >>> print [p.pub_date for p in queryset] # Re-use the cache from the evaluation.
.. _complex-lookups-with-q:
Complex lookups with Q objects Complex lookups with Q objects
============================== ==============================

View File

@ -371,6 +371,35 @@ parameter when declaring the form field::
... class Meta: ... class Meta:
... model = Article ... model = Article
.. note::
If you explicitly instantiate a form field like this, Django assumes that you
want to completely define its behavior; therefore, default attributes (such as
``max_length`` or ``required``) are not drawn from the corresponding model. If
you want to maintain the behavior specified in the model, you must set the
relevant arguments explicitly when declaring the form field.
For example, if the ``Article`` model looks like this::
class Article(models.Model):
headline = models.CharField(max_length=200, null=True, blank=True,
help_text="Use puns liberally")
content = models.TextField()
and you want to do some custom validation for ``headline``, while keeping
the ``blank`` and ``help_text`` values as specified, you might define
``ArticleForm`` like this::
class ArticleForm(ModelForm):
headline = MyFormField(max_length=200, required=False,
help_text="Use puns liberally")
class Meta:
model = Article
See the :ref:`form field documentation <ref-forms-fields>` for more information
on fields and their arguments.
Changing the order of fields Changing the order of fields
---------------------------- ----------------------------
@ -512,6 +541,12 @@ Then, pass your ``BaseAuthorFormSet`` class to the factory function::
>>> AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet) >>> AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet)
If you want to return a formset that doesn't include *any* pre-existing
instances of the model, you can specify an empty QuerySet::
>>> AuthorFormSet(queryset=Author.objects.none())
Controlling which fields are used with ``fields`` and ``exclude`` Controlling which fields are used with ``fields`` and ``exclude``
----------------------------------------------------------------- -----------------------------------------------------------------

View File

@ -64,7 +64,7 @@ code! --, actually the ``direct_to_template`` view simply grabs information from
the extra-parameters dictionary and uses that information when rendering the the extra-parameters dictionary and uses that information when rendering the
view. view.
Because this generic view -- and all the others -- is a regular view functions Because this generic view -- and all the others -- is a regular view function
like any other, we can reuse it inside our own views. As an example, let's like any other, we can reuse it inside our own views. As an example, let's
extend our "about" example to map URLs of the form ``/about/<whatever>/`` to extend our "about" example to map URLs of the form ``/about/<whatever>/`` to
statically rendered ``about/<whatever>.html``. We'll do this by first modifying statically rendered ``about/<whatever>.html``. We'll do this by first modifying
@ -150,7 +150,7 @@ be using these models::
publisher = models.ForeignKey(Publisher) publisher = models.ForeignKey(Publisher)
publication_date = models.DateField() publication_date = models.DateField()
To build a list page of all books, we'd use a URLconf along these lines:: To build a list page of all publishers, we'd use a URLconf along these lines::
from django.conf.urls.defaults import * from django.conf.urls.defaults import *
from django.views.generic import list_detail from django.views.generic import list_detail
@ -176,7 +176,7 @@ version of the model's name.
.. highlightlang:: html+django .. highlightlang:: html+django
This template will be rendered against a context containing a variable called This template will be rendered against a context containing a variable called
``object_list`` that contains all the book objects. A very simple template ``object_list`` that contains all the publisher objects. A very simple template
might look like the following:: might look like the following::
{% extends "base.html" %} {% extends "base.html" %}
@ -217,7 +217,7 @@ Making "friendly" template contexts
You might have noticed that our sample publisher list template stores all the You might have noticed that our sample publisher list template stores all the
books in a variable named ``object_list``. While this works just fine, it isn't books in a variable named ``object_list``. While this works just fine, it isn't
all that "friendly" to template authors: they have to "just know" that they're all that "friendly" to template authors: they have to "just know" that they're
dealing with books here. A better name for that variable would be dealing with publishers here. A better name for that variable would be
``publisher_list``; that variable's content is pretty obvious. ``publisher_list``; that variable's content is pretty obvious.
We can change the name of that variable easily with the ``template_object_name`` We can change the name of that variable easily with the ``template_object_name``
@ -241,14 +241,14 @@ Adding extra context
-------------------- --------------------
Often you simply need to present some extra information beyond that provided by Often you simply need to present some extra information beyond that provided by
the generic view. For example, think of showing a list of all the other the generic view. For example, think of showing a list of all the books on each
publishers on each publisher detail page. The ``object_detail`` generic view publisher detail page. The ``object_detail`` generic view provides the
provides the publisher to the context, but it seems there's no way to get a list publisher to the context, but it seems there's no way to get additional
of *all* publishers in that template. information in that template.
But there is: all generic views take an extra optional parameter, But there is: all generic views take an extra optional parameter,
``extra_context``. This is a dictionary of extra objects that will be added to ``extra_context``. This is a dictionary of extra objects that will be added to
the template's context. So, to provide the list of all publishers on the detail the template's context. So, to provide the list of all books on the detail
detail view, we'd use an info dict like this: detail view, we'd use an info dict like this:
.. parsed-literal:: .. parsed-literal::
@ -268,9 +268,9 @@ generic view. It's very handy.
However, there's actually a subtle bug here -- can you spot it? However, there's actually a subtle bug here -- can you spot it?
The problem has to do with when the queries in ``extra_context`` are evaluated. The problem has to do with when the queries in ``extra_context`` are evaluated.
Because this example puts ``Publisher.objects.all()`` in the URLconf, it will Because this example puts ``Book.objects.all()`` in the URLconf, it will
be evaluated only once (when the URLconf is first loaded). Once you add or be evaluated only once (when the URLconf is first loaded). Once you add or
remove publishers, you'll notice that the generic view doesn't reflect those remove books, you'll notice that the generic view doesn't reflect those
changes until you reload the Web server (see :ref:`caching-and-querysets` changes until you reload the Web server (see :ref:`caching-and-querysets`
for more information about when QuerySets are cached and evaluated). for more information about when QuerySets are cached and evaluated).

View File

@ -453,6 +453,20 @@ Default: ``'sessionid'``
The name of the cookie to use for sessions. This can be whatever you want. The name of the cookie to use for sessions. This can be whatever you want.
SESSION_COOKIE_PATH
-------------------
.. versionadded:: 1.0
Default: ``'/'``
The path set on the session cookie. This should either match the URL path of
your Django installation or be parent of that path.
This is useful if you have multiple Django instances running under the same
hostname. They can use different cookie paths, and each instance will only see
its own session cookie.
SESSION_COOKIE_SECURE SESSION_COOKIE_SECURE
--------------------- ---------------------

View File

@ -147,7 +147,7 @@ will be returned::
``get_object_or_404`` ``get_object_or_404``
===================== =====================
.. function:: get_object_or_404(object, *args, **kwargs) .. function:: get_object_or_404(klass, *args, **kwargs)
Calls :meth:`~django.db.models.QuerySet.get()` on a given model manager, Calls :meth:`~django.db.models.QuerySet.get()` on a given model manager,
but it raises ``django.http.Http404`` instead of the model's but it raises ``django.http.Http404`` instead of the model's

View File

@ -515,7 +515,7 @@ arguments at time of construction:
>>> c = Client() >>> c = Client()
>>> c.get('/customers/details/?name=fred&age=7') >>> c.get('/customers/details/?name=fred&age=7')
If you provide URL both an encoded GET data and a data argument, If you provide a URL with both an encoded GET data and a data argument,
the data argument will take precedence. the data argument will take precedence.
If you set ``follow`` to ``True`` the client will follow any redirects If you set ``follow`` to ``True`` the client will follow any redirects
@ -627,7 +627,7 @@ arguments at time of construction:
.. versionadded:: 1.1 .. versionadded:: 1.1
Makes an PUT request on the provided ``path`` and returns a Makes a PUT request on the provided ``path`` and returns a
``Response`` object. Useful for testing RESTful interfaces. Acts just ``Response`` object. Useful for testing RESTful interfaces. Acts just
like :meth:`Client.post` except with the PUT request method. like :meth:`Client.post` except with the PUT request method.
@ -1127,11 +1127,11 @@ Django, such as your machine's mail server, if you're running one.)
During test running, each outgoing e-mail is saved in During test running, each outgoing e-mail is saved in
``django.core.mail.outbox``. This is a simple list of all ``django.core.mail.outbox``. This is a simple list of all
:class:`<~django.core.mail.EmailMessage>` instances that have been sent. :class:`~django.core.mail.EmailMessage` instances that have been sent.
It does not exist under normal execution conditions, i.e., when you're not It does not exist under normal execution conditions, i.e., when you're not
running unit tests. The outbox is created during test setup, along with the running unit tests. The outbox is created during test setup, along with the
dummy :class:`<~django.core.mail.SMTPConnection>`. When the test framework is dummy :class:`~django.core.mail.SMTPConnection`. When the test framework is
torn down, the standard :class:`<~django.core.mail.SMTPConnection>` class is torn down, the standard :class:`~django.core.mail.SMTPConnection` class is
restored, and the test outbox is destroyed. restored, and the test outbox is destroyed.
The ``outbox`` attribute is a special attribute that is created *only* when The ``outbox`` attribute is a special attribute that is created *only* when

View File

@ -31,136 +31,10 @@
# #
# To uninstall, just remove the line from your .bash_profile and .bashrc. # To uninstall, just remove the line from your .bash_profile and .bashrc.
# Enable extended pattern matching operators.
shopt -s extglob
_django_completion() _django_completion()
{ {
local cur prev opts actions action_shell_opts action_runfcgi_opts COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \
COMPREPLY=() COMP_CWORD=$COMP_CWORD \
cur="${COMP_WORDS[COMP_CWORD]}" DJANGO_AUTO_COMPLETE=1 $1 ) )
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Standalone options
opts="--help --settings --pythonpath --noinput --noreload --format --indent --verbosity --adminmedia --version --locale --domain"
# Actions
actions="createcachetable createsuperuser compilemessages \
dbshell diffsettings dumpdata flush inspectdb loaddata \
makemessages reset runfcgi runserver shell sql sqlall sqlclear \
sqlcustom sqlflush sqlindexes sqlreset sqlsequencereset startapp \
startproject syncdb test validate"
# Action's options
action_shell_opts="--plain"
action_runfcgi_opts="host port socket method maxspare minspare maxchildren daemonize pidfile workdir"
if [[ # django-admin.py, django-admin, ./manage, manage.py
( ${COMP_CWORD} -eq 1 &&
( ${COMP_WORDS[0]} == django-admin.py ||
${COMP_WORDS[0]} == django-admin ||
${COMP_WORDS[0]} == ./manage.py ||
${COMP_WORDS[0]} == manage.py ) )
||
# python manage.py, /some/path/python manage.py (if manage.py exists)
( ${COMP_CWORD} -eq 2 &&
( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename -- ${COMP_WORDS[1]} ) == manage.py) &&
( -r ${COMP_WORDS[1]} ) )
||
( ${COMP_CWORD} -eq 2 &&
( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename -- ${COMP_WORDS[1]} ) == django-admin.py) &&
( -r ${COMP_WORDS[1]} ) )
||
( ${COMP_CWORD} -eq 2 &&
( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename -- ${COMP_WORDS[1]} ) == django-admin) &&
( -r ${COMP_WORDS[1]} ) ) ]] ; then
case ${cur} in
-*)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
action=$COMPREPLY
return 0
;;
*)
COMPREPLY=( $(compgen -W "${actions}" -- ${cur}) )
action=$COMPREPLY
return 0
;;
esac
else
case ${prev} in
dumpdata|reset| \
sql|sqlall|sqlclear|sqlcustom|sqlindexes| \
sqlreset|sqlsequencereset|test)
# App completion
settings=""
# If settings.py in the PWD, use that
if [ -e settings.py ] ; then
settings="$PWD/settings.py"
else
# Use the ENV variable if it is set
if [ $DJANGO_SETTINGS_MODULE ] ; then
settings=$DJANGO_SETTINGS_MODULE
fi
fi
# Couldn't find settings so return nothing
if [ -z $settings ] ; then
COMPREPLY=()
# Otherwise inspect settings.py file
else
apps=`sed -n "/INSTALLED_APPS = (/,/)/p" $settings | \
grep -v "django.contrib" |
sed -n "s/^[ ]*'\(.*\.\)*\(.*\)'.*$/\2 /pg" | \
tr -d "\n"`
COMPREPLY=( $(compgen -W "${apps}" -- ${cur}) )
fi
return 0
;;
createcachetable|cleanup|compilemessages|dbshell| \
diffsettings|inspectdb|makemessages| \
runserver|startapp|startproject|syncdb| \
validate)
COMPREPLY=()
return 0
;;
shell)
COMPREPLY=( $(compgen -W "$action_shell_opts" -- ${cur}) )
return 0
;;
runfcgi)
COMPREPLY=( $(compgen -W "$action_runfcgi_opts" -- ${cur}) )
return 0
;;
host*|port*|socket*|method*|maxspare*|minspare*|maxchildren*|daemonize*|pidfile*|workdir*)
if [ "$action" == "runfcgi" ] ; then
COMPREPLY=( $(compgen -W "$action_runfcgi_opts" -- ${cur}) )
return 0
fi
return 0
;;
*)
#COMPREPLY=( $(compgen -W "auth core" -- ${cur}) )
COMPREPLY=()
return 0
;;
esac
fi
} }
complete -F _django_completion -o default django-admin.py manage.py
complete -F _django_completion django-admin.py manage.py django-admin
# Support for multiple interpreters.
unset pythons
if command -v whereis &>/dev/null; then
python_interpreters=$(whereis python | cut -d " " -f 2-)
for python in $python_interpreters; do
pythons="${pythons} $(basename -- $python)"
done
pythons=$(echo $pythons | tr " " "\n" | sort -u | tr "\n" " ")
else
pythons=python
fi
complete -F _django_completion -o default $pythons

View File

@ -56,6 +56,16 @@ __test__ = {'API_TESTS': """
>>> company_query >>> company_query
[{'num_chairs': 2302, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 5, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 34, 'name': u'Test GmbH', 'num_employees': 32}] [{'num_chairs': 2302, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 5, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 34, 'name': u'Test GmbH', 'num_employees': 32}]
# Law of order of operations is followed
>>> _ =company_query.update(num_chairs=F('num_employees') + 2 * F('num_employees'))
>>> company_query
[{'num_chairs': 6900, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 9, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 96, 'name': u'Test GmbH', 'num_employees': 32}]
# Law of order of operations can be overridden by parentheses
>>> _ =company_query.update(num_chairs=((F('num_employees') + 2) * F('num_employees')))
>>> company_query
[{'num_chairs': 5294600, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 15, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 1088, 'name': u'Test GmbH', 'num_employees': 32}]
# The relation of a foreign key can become copied over to an other foreign key. # The relation of a foreign key can become copied over to an other foreign key.
>>> Company.objects.update(point_of_contact=F('ceo')) >>> Company.objects.update(point_of_contact=F('ceo'))
3 3

View File

@ -6,7 +6,7 @@
<field type="BooleanField" name="alive">True</field> <field type="BooleanField" name="alive">True</field>
</object> </object>
<object pk="2" model="admin_views.person"> <object pk="2" model="admin_views.person">
<field type="CharField" name="name">Grace Hooper</field> <field type="CharField" name="name">Grace Hopper</field>
<field type="IntegerField" name="gender">1</field> <field type="IntegerField" name="gender">1</field>
<field type="BooleanField" name="alive">False</field> <field type="BooleanField" name="alive">False</field>
</object> </object>

View File

@ -353,6 +353,9 @@ class AdminViewPermissionsTest(TestCase):
LOGIN_FORM_KEY: 1, LOGIN_FORM_KEY: 1,
'username': 'joepublic', 'username': 'joepublic',
'password': 'secret'} 'password': 'secret'}
self.no_username_login = {
LOGIN_FORM_KEY: 1,
'password': 'secret'}
def testLogin(self): def testLogin(self):
""" """
@ -416,6 +419,14 @@ class AdminViewPermissionsTest(TestCase):
# Login.context is a list of context dicts we just need to check the first one. # Login.context is a list of context dicts we just need to check the first one.
self.assert_(login.context[0].get('error_message')) self.assert_(login.context[0].get('error_message'))
# Requests without username should not return 500 errors.
request = self.client.get('/test_admin/admin/')
self.failUnlessEqual(request.status_code, 200)
login = self.client.post('/test_admin/admin/', self.no_username_login)
self.failUnlessEqual(login.status_code, 200)
# Login.context is a list of context dicts we just need to check the first one.
self.assert_(login.context[0].get('error_message'))
def testLoginSuccessfullyRedirectsToOriginalUrl(self): def testLoginSuccessfullyRedirectsToOriginalUrl(self):
request = self.client.get('/test_admin/admin/') request = self.client.get('/test_admin/admin/')
self.failUnlessEqual(request.status_code, 200) self.failUnlessEqual(request.status_code, 200)
@ -897,7 +908,7 @@ class AdminViewListEditable(TestCase):
self.client.post('/test_admin/admin/admin_views/person/', data) self.client.post('/test_admin/admin/admin_views/person/', data)
self.failUnlessEqual(Person.objects.get(name="John Mauchly").alive, False) self.failUnlessEqual(Person.objects.get(name="John Mauchly").alive, False)
self.failUnlessEqual(Person.objects.get(name="Grace Hooper").gender, 2) self.failUnlessEqual(Person.objects.get(name="Grace Hopper").gender, 2)
# test a filtered page # test a filtered page
data = { data = {

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Unit and doctests for specific database backends. # Unit and doctests for specific database backends.
import unittest import unittest
from django.db import connection, DEFAULT_DB_ALIAS from django.db import backend, connection, DEFAULT_DB_ALIAS
from django.db.backends.signals import connection_created from django.db.backends.signals import connection_created
from django.conf import settings from django.conf import settings
@ -11,9 +11,10 @@ class Callproc(unittest.TestCase):
# If the backend is Oracle, test that we can call a standard # If the backend is Oracle, test that we can call a standard
# stored procedure through our cursor wrapper. # stored procedure through our cursor wrapper.
if settings.DATABASES[DEFAULT_DB_ALIAS]['DATABASE_ENGINE'] == 'oracle': if settings.DATABASES[DEFAULT_DB_ALIAS]['DATABASE_ENGINE'] == 'oracle':
convert_unicode = backend.convert_unicode
cursor = connection.cursor() cursor = connection.cursor()
cursor.callproc('DBMS_SESSION.SET_IDENTIFIER', cursor.callproc(convert_unicode('DBMS_SESSION.SET_IDENTIFIER'),
['_django_testing!',]) [convert_unicode('_django_testing!'),])
return True return True
else: else:
return True return True
@ -23,7 +24,7 @@ class LongString(unittest.TestCase):
def test_long_string(self): def test_long_string(self):
# If the backend is Oracle, test that we can save a text longer # If the backend is Oracle, test that we can save a text longer
# than 4000 chars and read it properly # than 4000 chars and read it properly
if settings.DATABASES[DEFAULT_DB_ALIAS]['DATABASE_ENGINE'] == 'oracle': if settings.DATABASE_ENGINE == 'oracle':
c = connection.cursor() c = connection.cursor()
c.execute('CREATE TABLE ltext ("TEXT" NCLOB)') c.execute('CREATE TABLE ltext ("TEXT" NCLOB)')
long_str = ''.join([unicode(x) for x in xrange(4000)]) long_str = ''.join([unicode(x) for x in xrange(4000)])

View File

@ -1,11 +1,16 @@
from unittest import TestCase from unittest import TestCase
from sys import version_info from sys import version_info
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
from django.http import HttpResponse from django.http import HttpResponse, HttpRequest
from django.utils.functional import allow_lazy, lazy, memoize from django.utils.functional import allow_lazy, lazy, memoize
from django.views.decorators.http import require_http_methods, require_GET, require_POST from django.views.decorators.http import require_http_methods, require_GET, require_POST
from django.views.decorators.vary import vary_on_headers, vary_on_cookie from django.views.decorators.vary import vary_on_headers, vary_on_cookie
from django.views.decorators.cache import cache_page, never_cache, cache_control from django.views.decorators.cache import cache_page, never_cache, cache_control
from django.utils.decorators import auto_adapt_to_methods
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
@ -85,3 +90,64 @@ class DecoratorsTest(TestCase):
self.assertEqual(response, ['test2', 'test1']) self.assertEqual(response, ['test2', 'test1'])
def test_cache_page_new_style(self):
"""
Test that we can call cache_page the new way
"""
def my_view(request):
return "response"
my_view_cached = cache_page(123)(my_view)
self.assertEqual(my_view_cached(HttpRequest()), "response")
my_view_cached2 = cache_page(123, key_prefix="test")(my_view)
self.assertEqual(my_view_cached2(HttpRequest()), "response")
def test_cache_page_old_style(self):
"""
Test that we can call cache_page the old way
"""
def my_view(request):
return "response"
my_view_cached = cache_page(my_view, 123)
self.assertEqual(my_view_cached(HttpRequest()), "response")
my_view_cached2 = cache_page(my_view, 123, key_prefix="test")
self.assertEqual(my_view_cached2(HttpRequest()), "response")
class MethodDecoratorAdapterTests(TestCase):
def test_auto_adapt_to_methods(self):
"""
Test that auto_adapt_to_methods actually works.
"""
# Need 2 decorators with auto_adapt_to_methods,
# to check it plays nicely with composing itself.
def my_decorator(func):
def wrapped(*args, **kwargs):
# need to ensure that the first arg isn't 'self'
self.assertEqual(args[0], "test")
return "my_decorator:" + func(*args, **kwargs)
wrapped.my_decorator_custom_attribute = True
return wraps(func)(wrapped)
my_decorator = auto_adapt_to_methods(my_decorator)
def my_decorator2(func):
def wrapped(*args, **kwargs):
# need to ensure that the first arg isn't 'self'
self.assertEqual(args[0], "test")
return "my_decorator2:" + func(*args, **kwargs)
wrapped.my_decorator2_custom_attribute = True
return wraps(func)(wrapped)
my_decorator2 = auto_adapt_to_methods(my_decorator2)
class MyClass(object):
def my_method(self, *args, **kwargs):
return "my_method:%r %r" % (args, kwargs)
my_method = my_decorator2(my_decorator(my_method))
obj = MyClass()
self.assertEqual(obj.my_method("test", 123, name='foo'),
"my_decorator2:my_decorator:my_method:('test', 123) {'name': 'foo'}")
self.assertEqual(obj.my_method.__name__, 'my_method')
self.assertEqual(getattr(obj.my_method, 'my_decorator_custom_attribute', False),
True)
self.assertEqual(getattr(obj.my_method, 'my_decorator2_custom_attribute', False),
True)

View File

@ -358,4 +358,42 @@ ValidationError: [u'NOT A LIST OF VALUES']
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValidationError: [u'4 IS INVALID CHOICE'] ValidationError: [u'4 IS INVALID CHOICE']
# Subclassing ErrorList #######################################################
>>> from django.utils.safestring import mark_safe
>>>
>>> class TestForm(Form):
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
...
... def clean(self):
... raise ValidationError("I like to be awkward.")
...
>>> class CustomErrorList(util.ErrorList):
... def __unicode__(self):
... return self.as_divs()
... def as_divs(self):
... if not self: return u''
... return mark_safe(u'<div class="error">%s</div>'
... % ''.join([u'<p>%s</p>' % e for e in self]))
...
This form should print errors the default way.
>>> form1 = TestForm({'first_name': 'John'})
>>> print form1['last_name'].errors
<ul class="errorlist"><li>This field is required.</li></ul>
>>> print form1.errors['__all__']
<ul class="errorlist"><li>I like to be awkward.</li></ul>
This one should wrap error groups in the customized way.
>>> form2 = TestForm({'first_name': 'John'}, error_class=CustomErrorList)
>>> print form2['last_name'].errors
<div class="error"><p>This field is required.</p></div>
>>> print form2.errors['__all__']
<div class="error"><p>I like to be awkward.</p></div>
""" """

View File

@ -767,6 +767,13 @@ u'example@valid-----hyphens.com'
>>> f.clean('example@valid-with-hyphens.com') >>> f.clean('example@valid-with-hyphens.com')
u'example@valid-with-hyphens.com' u'example@valid-with-hyphens.com'
# Check for runaway regex security problem. This will take for-freeking-ever
# if the security fix isn't in place.
>>> f.clean('viewx3dtextx26qx3d@yahoo.comx26latlngx3d15854521645943074058')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid e-mail address.']
>>> f = EmailField(required=False) >>> f = EmailField(required=False)
>>> f.clean('') >>> f.clean('')
u'' u''
@ -972,6 +979,32 @@ ValidationError: [u'Enter a valid URL.']
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValidationError: [u'Enter a valid URL.'] ValidationError: [u'Enter a valid URL.']
>>> f.clean('.')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid URL.']
>>> f.clean('com.')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid URL.']
>>> f.clean('http://example.com.')
u'http://example.com./'
>>> f.clean('example.com.')
u'http://example.com./'
# hangs "forever" if catastrophic backtracking in ticket:#11198 not fixed
>>> f.clean('http://%s' % ("X"*200,))
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid URL.']
# a second test, to make sure the problem is really addressed, even on
# domains that don't fail the domain label length check in the regex
>>> f.clean('http://%s' % ("X"*60,))
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid URL.']
>>> f.clean('http://.com') >>> f.clean('http://.com')
Traceback (most recent call last): Traceback (most recent call last):
... ...

View File

@ -76,7 +76,7 @@ class IntrospectionTests(TestCase):
def test_get_table_description_types(self): def test_get_table_description_types(self):
cursor = connection.cursor() cursor = connection.cursor()
desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table) desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
self.assertEqual([datatype(r[1]) for r in desc], self.assertEqual([datatype(r[1], r) for r in desc],
['IntegerField', 'CharField', 'CharField', 'CharField']) ['IntegerField', 'CharField', 'CharField', 'CharField'])
# Regression test for #9991 - 'real' types in postgres # Regression test for #9991 - 'real' types in postgres
@ -86,7 +86,7 @@ class IntrospectionTests(TestCase):
cursor.execute("CREATE TABLE django_ixn_real_test_table (number REAL);") cursor.execute("CREATE TABLE django_ixn_real_test_table (number REAL);")
desc = connection.introspection.get_table_description(cursor, 'django_ixn_real_test_table') desc = connection.introspection.get_table_description(cursor, 'django_ixn_real_test_table')
cursor.execute('DROP TABLE django_ixn_real_test_table;') cursor.execute('DROP TABLE django_ixn_real_test_table;')
self.assertEqual(datatype(desc[0][1]), 'FloatField') self.assertEqual(datatype(desc[0][1], desc[0]), 'FloatField')
def test_get_relations(self): def test_get_relations(self):
cursor = connection.cursor() cursor = connection.cursor()
@ -104,9 +104,10 @@ class IntrospectionTests(TestCase):
indexes = connection.introspection.get_indexes(cursor, Article._meta.db_table) indexes = connection.introspection.get_indexes(cursor, Article._meta.db_table)
self.assertEqual(indexes['reporter_id'], {'unique': False, 'primary_key': False}) self.assertEqual(indexes['reporter_id'], {'unique': False, 'primary_key': False})
def datatype(dbtype):
def datatype(dbtype, description):
"""Helper to convert a data type into a string.""" """Helper to convert a data type into a string."""
dt = connection.introspection.data_types_reverse[dbtype] dt = connection.introspection.get_field_type(dbtype, description)
if type(dt) is tuple: if type(dt) is tuple:
return dt[0] return dt[0]
else: else:

View File

@ -67,6 +67,9 @@ class Author(models.Model):
num = models.IntegerField(unique=True) num = models.IntegerField(unique=True)
extra = models.ForeignKey(ExtraInfo) extra = models.ForeignKey(ExtraInfo)
class Meta:
ordering = ['name']
def __unicode__(self): def __unicode__(self):
return self.name return self.name

View File

@ -196,7 +196,7 @@ def get_filter_tests():
'filter-force-escape05': ('{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}', {"a": "x&y"}, u"x&amp;y"), 'filter-force-escape05': ('{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}', {"a": "x&y"}, u"x&amp;y"),
'filter-force-escape06': ('{{ a|force_escape|escape }}', {"a": "x&y"}, u"x&amp;y"), 'filter-force-escape06': ('{{ a|force_escape|escape }}', {"a": "x&y"}, u"x&amp;y"),
'filter-force-escape07': ('{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}', {"a": "x&y"}, u"x&amp;y"), 'filter-force-escape07': ('{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}', {"a": "x&y"}, u"x&amp;y"),
'filter-force-escape07': ('{{ a|escape|force_escape }}', {"a": "x&y"}, u"x&amp;y"), 'filter-force-escape08': ('{{ a|escape|force_escape }}', {"a": "x&y"}, u"x&amp;y"),
# The contents in "linebreaks" and "linebreaksbr" are escaped # The contents in "linebreaks" and "linebreaksbr" are escaped
# according to the current autoescape setting. # according to the current autoescape setting.