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:
parent
6f983870f3
commit
2bc7422b52
4
AUTHORS
4
AUTHORS
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
@ -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))
|
||||||
|
@ -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 ' <strong>%s</strong>' % truncate_words(obj, 14)
|
return ' <strong>%s</strong>' % escape(truncate_words(obj, 14))
|
||||||
|
|
||||||
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
||||||
"""
|
"""
|
||||||
|
@ -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)
|
|
||||||
|
@ -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,
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -16,7 +16,7 @@ def convert_geom(wkt, geo_field):
|
|||||||
|
|
||||||
if SpatialBackend.postgis:
|
if SpatialBackend.postgis:
|
||||||
def convert_extent(box):
|
def convert_extent(box):
|
||||||
# Box text will be something like "BOX(-90.0 30.0, -85.0 40.0)";
|
# Box text will be something like "BOX(-90.0 30.0, -85.0 40.0)";
|
||||||
# parsing out and returning as a 4-tuple.
|
# parsing out and returning as a 4-tuple.
|
||||||
ll, ur = box[4:-1].split(',')
|
ll, ur = box[4:-1].split(',')
|
||||||
xmin, ymin = map(float, ll.split())
|
xmin, ymin = map(float, ll.split())
|
||||||
@ -32,19 +32,28 @@ 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)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def convert_geom(clob, geo_field):
|
def convert_geom(clob, geo_field):
|
||||||
if clob:
|
if clob:
|
||||||
return SpatialBackend.Geometry(clob.read(), geo_field.srid)
|
return SpatialBackend.Geometry(clob.read(), geo_field.srid)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
@ -73,7 +82,7 @@ class GeoAggregate(Aggregate):
|
|||||||
self.extra.setdefault('tolerance', 0.05)
|
self.extra.setdefault('tolerance', 0.05)
|
||||||
|
|
||||||
# Can't use geographic aggregates on non-geometry fields.
|
# Can't use geographic aggregates on non-geometry fields.
|
||||||
if not isinstance(self.source, GeometryField):
|
if not isinstance(self.source, GeometryField):
|
||||||
raise ValueError('Geospatial aggregates only allowed on geometry fields.')
|
raise ValueError('Geospatial aggregates only allowed on geometry fields.')
|
||||||
|
|
||||||
# Making sure the SQL function is available for this spatial backend.
|
# Making sure the SQL function is available for this spatial backend.
|
||||||
@ -87,7 +96,7 @@ class Collect(GeoAggregate):
|
|||||||
class Extent(GeoAggregate):
|
class Extent(GeoAggregate):
|
||||||
is_extent = True
|
is_extent = True
|
||||||
sql_function = SpatialBackend.extent
|
sql_function = SpatialBackend.extent
|
||||||
|
|
||||||
if SpatialBackend.oracle:
|
if SpatialBackend.oracle:
|
||||||
# Have to change Extent's attributes here for Oracle.
|
# Have to change Extent's attributes here for Oracle.
|
||||||
Extent.conversion_class = GeomField
|
Extent.conversion_class = GeomField
|
||||||
|
@ -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.')
|
||||||
|
@ -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()
|
||||||
|
@ -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())
|
||||||
|
@ -279,11 +279,11 @@ class RelatedGeoModelTest(unittest.TestCase):
|
|||||||
def test14_collect(self):
|
def test14_collect(self):
|
||||||
"Testing the `collect` GeoQuerySet method and `Collect` aggregate."
|
"Testing the `collect` GeoQuerySet method and `Collect` aggregate."
|
||||||
# Reference query:
|
# Reference query:
|
||||||
# SELECT AsText(ST_Collect("relatedapp_location"."point")) FROM "relatedapp_city" LEFT OUTER JOIN
|
# SELECT AsText(ST_Collect("relatedapp_location"."point")) FROM "relatedapp_city" LEFT OUTER JOIN
|
||||||
# "relatedapp_location" ON ("relatedapp_city"."location_id" = "relatedapp_location"."id")
|
# "relatedapp_location" ON ("relatedapp_city"."location_id" = "relatedapp_location"."id")
|
||||||
# WHERE "relatedapp_city"."state" = 'TX';
|
# WHERE "relatedapp_city"."state" = 'TX';
|
||||||
ref_geom = fromstr('MULTIPOINT(-97.516111 33.058333,-96.801611 32.782057,-95.363151 29.763374,-96.801611 32.782057)')
|
ref_geom = fromstr('MULTIPOINT(-97.516111 33.058333,-96.801611 32.782057,-95.363151 29.763374,-96.801611 32.782057)')
|
||||||
|
|
||||||
c1 = City.objects.filter(state='TX').collect(field_name='location__point')
|
c1 = City.objects.filter(state='TX').collect(field_name='location__point')
|
||||||
c2 = City.objects.filter(state='TX').aggregate(Collect('location__point'))['location__point__collect']
|
c2 = City.objects.filter(state='TX').aggregate(Collect('location__point'))['location__point__collect']
|
||||||
|
|
||||||
@ -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():
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
|
@ -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.')
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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")
|
||||||
|
@ -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'
|
||||||
|
@ -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
|
||||||
|
@ -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')
|
||||||
|
|
||||||
|
@ -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 = ''
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -23,3 +23,4 @@ the hood".
|
|||||||
committers
|
committers
|
||||||
release-process
|
release-process
|
||||||
deprecation
|
deprecation
|
||||||
|
svn
|
||||||
|
@ -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
|
||||||
|
@ -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?>
|
||||||
|
|
||||||
|
@ -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::
|
||||||
|
@ -24,13 +24,13 @@ Quick start guide
|
|||||||
|
|
||||||
To get started using the ``comments`` app, follow these steps:
|
To get started using the ``comments`` app, follow these steps:
|
||||||
|
|
||||||
#. Install the comments framework by adding ``'django.contrib.comments'`` to
|
#. Install the comments framework by adding ``'django.contrib.comments'`` to
|
||||||
:setting:`INSTALLED_APPS`.
|
:setting:`INSTALLED_APPS`.
|
||||||
|
|
||||||
#. Run ``manage.py syncdb`` so that Django will create the comment tables.
|
#. Run ``manage.py syncdb`` so that Django will create the comment tables.
|
||||||
|
|
||||||
#. Add the comment app's URLs to your project's ``urls.py``:
|
#. Add the comment app's URLs to your project's ``urls.py``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
@ -41,9 +41,9 @@ To get started using the ``comments`` app, follow these steps:
|
|||||||
|
|
||||||
#. Use the `comment template tags`_ below to embed comments in your
|
#. Use the `comment template tags`_ below to embed comments in your
|
||||||
templates.
|
templates.
|
||||||
|
|
||||||
You might also want to examine :ref:`ref-contrib-comments-settings`.
|
You might also want to examine :ref:`ref-contrib-comments-settings`.
|
||||||
|
|
||||||
Comment template tags
|
Comment template tags
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
@ -67,20 +67,20 @@ different ways you can specify which object to attach to:
|
|||||||
#. Refer to the object directly -- the more common method. Most of the
|
#. Refer to the object directly -- the more common method. Most of the
|
||||||
time, you'll have some object in the template's context you want
|
time, you'll have some object in the template's context you want
|
||||||
to attach the comment to; you can simply use that object.
|
to attach the comment to; you can simply use that object.
|
||||||
|
|
||||||
For example, in a blog entry page that has a variable named ``entry``,
|
For example, in a blog entry page that has a variable named ``entry``,
|
||||||
you could use the following to load the number of comments::
|
you could use the following to load the number of comments::
|
||||||
|
|
||||||
{% get_comment_count for entry as comment_count %}.
|
{% get_comment_count for entry as comment_count %}.
|
||||||
|
|
||||||
#. Refer to the object by content-type and object id. You'd use this method
|
#. Refer to the object by content-type and object id. You'd use this method
|
||||||
if you, for some reason, don't actually have direct access to the object.
|
if you, for some reason, don't actually have direct access to the object.
|
||||||
|
|
||||||
Following the above example, if you knew the object ID was ``14`` but
|
Following the above example, if you knew the object ID was ``14`` but
|
||||||
didn't have access to the actual object, you could do something like::
|
didn't have access to the actual object, you could do something like::
|
||||||
|
|
||||||
{% get_comment_count for blog.entry 14 as comment_count %}
|
{% get_comment_count for blog.entry 14 as comment_count %}
|
||||||
|
|
||||||
In the above, ``blog.entry`` is the app label and (lower-cased) model
|
In the above, ``blog.entry`` is the app label and (lower-cased) model
|
||||||
name of the model class.
|
name of the model class.
|
||||||
|
|
||||||
@ -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] %}
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ For example::
|
|||||||
{% for comment in comment_list %}
|
{% for comment in comment_list %}
|
||||||
...
|
...
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
This returns a list of :class:`~django.contrib.comments.models.Comment` objects;
|
This returns a list of :class:`~django.contrib.comments.models.Comment` objects;
|
||||||
see :ref:`the comment model documentation <ref-contrib-comments-models>` for
|
see :ref:`the comment model documentation <ref-contrib-comments-models>` for
|
||||||
details.
|
details.
|
||||||
@ -116,9 +116,9 @@ To count comments attached to an object, use :ttag:`get_comment_count`::
|
|||||||
For example::
|
For example::
|
||||||
|
|
||||||
{% get_comment_count for event as comment_count %}
|
{% get_comment_count for event as comment_count %}
|
||||||
|
|
||||||
<p>This event has {{ comment_count }} comments.</p>
|
<p>This event has {{ comment_count }} comments.</p>
|
||||||
|
|
||||||
|
|
||||||
Displaying the comment post form
|
Displaying the comment post form
|
||||||
--------------------------------
|
--------------------------------
|
||||||
@ -153,7 +153,7 @@ If you want more control over the look and feel of the comment form, you use use
|
|||||||
you can use in the template::
|
you can use in the template::
|
||||||
|
|
||||||
{% get_comment_form for [object] as [varname] %}
|
{% get_comment_form for [object] as [varname] %}
|
||||||
|
|
||||||
A complete form might look like::
|
A complete form might look like::
|
||||||
|
|
||||||
{% get_comment_form for event as form %}
|
{% get_comment_form for event as form %}
|
||||||
@ -164,7 +164,7 @@ A complete form might look like::
|
|||||||
<td><input type="submit" name="preview" class="submit-post" value="Preview"></td>
|
<td><input type="submit" name="preview" class="submit-post" value="Preview"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
Be sure to read the `notes on the comment form`_, below, for some special
|
Be sure to read the `notes on the comment form`_, below, for some special
|
||||||
considerations you'll need to make if you're using this approach.
|
considerations you'll need to make if you're using this approach.
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ Redirecting after the comment post
|
|||||||
|
|
||||||
To specify the URL you want to redirect to after the comment has been posted,
|
To specify the URL you want to redirect to after the comment has been posted,
|
||||||
you can include a hidden form input called ``next`` in your comment form. For example::
|
you can include a hidden form input called ``next`` in your comment form. For example::
|
||||||
|
|
||||||
<input type="hidden" name="next" value="{% url my_comment_was_posted %}" />
|
<input type="hidden" name="next" value="{% url my_comment_was_posted %}" />
|
||||||
|
|
||||||
.. _notes-on-the-comment-form:
|
.. _notes-on-the-comment-form:
|
||||||
@ -198,24 +198,24 @@ should know about:
|
|||||||
|
|
||||||
* It contains a number of hidden fields that contain timestamps, information
|
* It contains a number of hidden fields that contain timestamps, information
|
||||||
about the object the comment should be attached to, and a "security hash"
|
about the object the comment should be attached to, and a "security hash"
|
||||||
used to validate this information. If someone tampers with this data --
|
used to validate this information. If someone tampers with this data --
|
||||||
something comment spammers will try -- the comment submission will fail.
|
something comment spammers will try -- the comment submission will fail.
|
||||||
|
|
||||||
If you're rendering a custom comment form, you'll need to make sure to
|
If you're rendering a custom comment form, you'll need to make sure to
|
||||||
pass these values through unchanged.
|
pass these values through unchanged.
|
||||||
|
|
||||||
* The timestamp is used to ensure that "reply attacks" can't continue very
|
* The timestamp is used to ensure that "reply attacks" can't continue very
|
||||||
long. Users who wait too long between requesting the form and posting a
|
long. Users who wait too long between requesting the form and posting a
|
||||||
comment will have their submissions refused.
|
comment will have their submissions refused.
|
||||||
|
|
||||||
* The comment form includes a "honeypot_" field. It's a trap: if any data is
|
* The comment form includes a "honeypot_" field. It's a trap: if any data is
|
||||||
entered in that field, the comment will be considered spam (spammers often
|
entered in that field, the comment will be considered spam (spammers often
|
||||||
automatically fill in all fields in an attempt to make valid submissions).
|
automatically fill in all fields in an attempt to make valid submissions).
|
||||||
|
|
||||||
The default form hides this field with a piece of CSS and further labels
|
The default form hides this field with a piece of CSS and further labels
|
||||||
it with a warning field; if you use the comment form with a custom
|
it with a warning field; if you use the comment form with a custom
|
||||||
template you should be sure to do the same.
|
template you should be sure to do the same.
|
||||||
|
|
||||||
.. _honeypot: http://en.wikipedia.org/wiki/Honeypot_(computing)
|
.. _honeypot: http://en.wikipedia.org/wiki/Honeypot_(computing)
|
||||||
|
|
||||||
More information
|
More information
|
||||||
@ -230,4 +230,4 @@ More information
|
|||||||
upgrade
|
upgrade
|
||||||
custom
|
custom
|
||||||
forms
|
forms
|
||||||
moderation
|
moderation
|
||||||
|
@ -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/
|
||||||
|
|
||||||
|
@ -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
|
||||||
------------------
|
------------------
|
||||||
|
@ -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.
|
||||||
|
@ -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``
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -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::
|
||||||
|
|
||||||
|
@ -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``.)
|
||||||
|
|
||||||
@ -319,7 +310,7 @@ a subclass of dictionary. Exceptions are outlined here:
|
|||||||
>>> q = QueryDict('a=1&a=2&a=3')
|
>>> q = QueryDict('a=1&a=2&a=3')
|
||||||
>>> q.items()
|
>>> q.items()
|
||||||
[('a', '3')]
|
[('a', '3')]
|
||||||
|
|
||||||
.. method:: QueryDict.iteritems()
|
.. method:: QueryDict.iteritems()
|
||||||
|
|
||||||
Just like the standard dictionary ``iteritems()`` method. Like
|
Just like the standard dictionary ``iteritems()`` method. Like
|
||||||
|
@ -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>`.
|
||||||
|
|
||||||
@ -98,7 +101,7 @@ pre_save
|
|||||||
|
|
||||||
.. data:: django.db.models.signals.pre_save
|
.. data:: django.db.models.signals.pre_save
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
This is sent at the beginning of a model's :meth:`~django.db.models.Model.save`
|
This is sent at the beginning of a model's :meth:`~django.db.models.Model.save`
|
||||||
method.
|
method.
|
||||||
|
|
||||||
@ -114,8 +117,8 @@ post_save
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
.. data:: django.db.models.signals.post_save
|
.. data:: django.db.models.signals.post_save
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
Like :data:`pre_save`, but sent at the end of the
|
Like :data:`pre_save`, but sent at the end of the
|
||||||
:meth:`~django.db.models.Model.save` method.
|
:meth:`~django.db.models.Model.save` method.
|
||||||
|
|
||||||
@ -135,7 +138,7 @@ pre_delete
|
|||||||
|
|
||||||
.. data:: django.db.models.signals.pre_delete
|
.. data:: django.db.models.signals.pre_delete
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
Sent at the beginning of a model's :meth:`~django.db.models.Model.delete`
|
Sent at the beginning of a model's :meth:`~django.db.models.Model.delete`
|
||||||
method.
|
method.
|
||||||
|
|
||||||
@ -151,8 +154,8 @@ post_delete
|
|||||||
-----------
|
-----------
|
||||||
|
|
||||||
.. data:: django.db.models.signals.post_delete
|
.. data:: django.db.models.signals.post_delete
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
Like :data:`pre_delete`, but sent at the end of the
|
Like :data:`pre_delete`, but sent at the end of the
|
||||||
:meth:`~django.db.models.Model.delete` method.
|
:meth:`~django.db.models.Model.delete` method.
|
||||||
|
|
||||||
@ -172,7 +175,7 @@ class_prepared
|
|||||||
|
|
||||||
.. data:: django.db.models.signals.class_prepared
|
.. data:: django.db.models.signals.class_prepared
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
Sent whenever a model class has been "prepared" -- that is, once model has
|
Sent whenever a model class has been "prepared" -- that is, once model has
|
||||||
been defined and registered with Django's model system. Django uses this
|
been defined and registered with Django's model system. Django uses this
|
||||||
signal internally; it's not generally used in third-party applications.
|
signal internally; it's not generally used in third-party applications.
|
||||||
@ -241,8 +244,8 @@ request_started
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
.. data:: django.core.signals.request_started
|
.. data:: django.core.signals.request_started
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
Sent when Django begins processing an HTTP request.
|
Sent when Django begins processing an HTTP request.
|
||||||
|
|
||||||
Arguments sent with this signal:
|
Arguments sent with this signal:
|
||||||
@ -258,7 +261,7 @@ request_finished
|
|||||||
|
|
||||||
.. data:: django.core.signals.request_finished
|
.. data:: django.core.signals.request_finished
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
Sent when Django finishes processing an HTTP request.
|
Sent when Django finishes processing an HTTP request.
|
||||||
|
|
||||||
Arguments sent with this signal:
|
Arguments sent with this signal:
|
||||||
@ -271,7 +274,7 @@ got_request_exception
|
|||||||
|
|
||||||
.. data:: django.core.signals.got_request_exception
|
.. data:: django.core.signals.got_request_exception
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
This signal is sent whenever Django encounters an exception while processing an incoming HTTP request.
|
This signal is sent whenever Django encounters an exception while processing an incoming HTTP request.
|
||||||
|
|
||||||
Arguments sent with this signal:
|
Arguments sent with this signal:
|
||||||
@ -295,7 +298,7 @@ template_rendered
|
|||||||
|
|
||||||
.. data:: django.test.signals.template_rendered
|
.. data:: django.test.signals.template_rendered
|
||||||
:module:
|
:module:
|
||||||
|
|
||||||
Sent when the test system renders a template. This signal is not emitted during
|
Sent when the test system renders a template. This signal is not emitted during
|
||||||
normal operation of a Django server -- it is only available during testing.
|
normal operation of a Django server -- it is only available during testing.
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ Django has a policy of :ref:`API stability <misc-api-stability>`. This means
|
|||||||
that, in general, code you develop against Django 1.0 should continue to work
|
that, in general, code you develop against Django 1.0 should continue to work
|
||||||
against 1.1 unchanged. However, we do sometimes make backwards-incompatible
|
against 1.1 unchanged. However, we do sometimes make backwards-incompatible
|
||||||
changes if they're necessary to resolve bugs, and there are a handful of such
|
changes if they're necessary to resolve bugs, and there are a handful of such
|
||||||
(minor) changes between Django 1.0 and Django 1.1.
|
(minor) changes between Django 1.0 and Django 1.1.
|
||||||
|
|
||||||
Before upgrading to Django 1.1 you should double-check that the following
|
Before upgrading to Django 1.1 you should double-check that the following
|
||||||
changes don't impact you, and upgrade your code if they do.
|
changes don't impact you, and upgrade your code if they do.
|
||||||
@ -41,12 +41,12 @@ However, **users on 64-bit platforms may experience some problems** using the
|
|||||||
would generate a 64-bit, 16 character digest in the constraint name; for
|
would generate a 64-bit, 16 character digest in the constraint name; for
|
||||||
example::
|
example::
|
||||||
|
|
||||||
ALTER TABLE myapp_sometable ADD CONSTRAINT object_id_refs_id_5e8f10c132091d1e FOREIGN KEY ...
|
ALTER TABLE myapp_sometable ADD CONSTRAINT object_id_refs_id_5e8f10c132091d1e FOREIGN KEY ...
|
||||||
|
|
||||||
Following this change, all platforms, regardless of word size, will generate a
|
Following this change, all platforms, regardless of word size, will generate a
|
||||||
32-bit, 8 character digest in the constraint name; for example::
|
32-bit, 8 character digest in the constraint name; for example::
|
||||||
|
|
||||||
ALTER TABLE myapp_sometable ADD CONSTRAINT object_id_refs_id_32091d1e FOREIGN KEY ...
|
ALTER TABLE myapp_sometable ADD CONSTRAINT object_id_refs_id_32091d1e FOREIGN KEY ...
|
||||||
|
|
||||||
As a result of this change, you will not be able to use the :djadmin:`reset`
|
As a result of this change, you will not be able to use the :djadmin:`reset`
|
||||||
management command on any table made by a 64-bit machine. This is because the
|
management command on any table made by a 64-bit machine. This is because the
|
||||||
@ -94,13 +94,13 @@ other than raise a ``DeprecationWarning``.
|
|||||||
If you've been relying on this middleware, the easiest upgrade path is:
|
If you've been relying on this middleware, the easiest upgrade path is:
|
||||||
|
|
||||||
* Examine `the code as it existed before it was removed`__.
|
* Examine `the code as it existed before it was removed`__.
|
||||||
|
|
||||||
* Verify that it works correctly with your upstream proxy, modifying
|
* Verify that it works correctly with your upstream proxy, modifying
|
||||||
it to support your particular proxy (if necessary).
|
it to support your particular proxy (if necessary).
|
||||||
|
|
||||||
* Introduce your modified version of ``SetRemoteAddrFromForwardedFor`` as a
|
* Introduce your modified version of ``SetRemoteAddrFromForwardedFor`` as a
|
||||||
piece of middleware in your own project.
|
piece of middleware in your own project.
|
||||||
|
|
||||||
__ http://code.djangoproject.com/browser/django/trunk/django/middleware/http.py?rev=11000#L33
|
__ http://code.djangoproject.com/browser/django/trunk/django/middleware/http.py?rev=11000#L33
|
||||||
|
|
||||||
Names of uploaded files are available later
|
Names of uploaded files are available later
|
||||||
@ -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
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
@ -132,8 +132,8 @@ public methods.
|
|||||||
Fixed the ``join`` filter's escaping behavior
|
Fixed the ``join`` filter's escaping behavior
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
The :ttag:`join` filter no longer escapes the literal value that is
|
The :ttag:`join` filter no longer escapes the literal value that is
|
||||||
passed in for the connector.
|
passed in for the connector.
|
||||||
|
|
||||||
This is backwards incompatible for the special situation of the literal string
|
This is backwards incompatible for the special situation of the literal string
|
||||||
containing one of the five special HTML characters. Thus, if you were writing
|
containing one of the five special HTML characters. Thus, if you were writing
|
||||||
@ -157,13 +157,13 @@ One feature has been marked as deprecated in Django 1.1:
|
|||||||
|
|
||||||
* You should no longer use ``AdminSite.root()`` to register that admin
|
* You should no longer use ``AdminSite.root()`` to register that admin
|
||||||
views. That is, if your URLconf contains the line::
|
views. That is, if your URLconf contains the line::
|
||||||
|
|
||||||
(r'^admin/(.*)', admin.site.root),
|
(r'^admin/(.*)', admin.site.root),
|
||||||
|
|
||||||
You should change it to read::
|
You should change it to read::
|
||||||
|
|
||||||
(r'^admin/', include(admin.site.urls)),
|
(r'^admin/', include(admin.site.urls)),
|
||||||
|
|
||||||
You should begin to remove use of this features from your code immediately.
|
You should begin to remove use of this features from your code immediately.
|
||||||
|
|
||||||
``AdminSite.root`` will will raise a ``PendingDeprecationWarning`` if used in
|
``AdminSite.root`` will will raise a ``PendingDeprecationWarning`` if used in
|
||||||
@ -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
|
||||||
@ -351,13 +350,13 @@ features:
|
|||||||
|
|
||||||
* Support for SpatiaLite_ -- a spatial database for SQLite -- as a spatial
|
* Support for SpatiaLite_ -- a spatial database for SQLite -- as a spatial
|
||||||
backend.
|
backend.
|
||||||
|
|
||||||
* Geographic aggregates (``Collect``, ``Extent``, ``MakeLine``, ``Union``)
|
* Geographic aggregates (``Collect``, ``Extent``, ``MakeLine``, ``Union``)
|
||||||
and ``F`` expressions.
|
and ``F`` expressions.
|
||||||
|
|
||||||
* New ``GeoQuerySet`` methods: ``collect``, ``geojson``, and
|
* New ``GeoQuerySet`` methods: ``collect``, ``geojson``, and
|
||||||
``snap_to_grid``.
|
``snap_to_grid``.
|
||||||
|
|
||||||
* A new list interface methods for ``GEOSGeometry`` objects.
|
* A new list interface methods for ``GEOSGeometry`` objects.
|
||||||
|
|
||||||
For more details, see the `GeoDjango documentation`_.
|
For more details, see the `GeoDjango documentation`_.
|
||||||
@ -412,23 +411,23 @@ Other new features and changes introduced since Django 1.0 include:
|
|||||||
* The :djadmin:`dumpdata` management command now accepts individual
|
* The :djadmin:`dumpdata` management command now accepts individual
|
||||||
model names as arguments, allowing you to export the data just from
|
model names as arguments, allowing you to export the data just from
|
||||||
particular models.
|
particular models.
|
||||||
|
|
||||||
* There's a new :tfilter:`safeseq` template filter which works just like
|
* There's a new :tfilter:`safeseq` template filter which works just like
|
||||||
:tfilter:`safe` for lists, marking each item in the list as safe.
|
:tfilter:`safe` for lists, marking each item in the list as safe.
|
||||||
|
|
||||||
* :ref:`Cache backends <topics-cache>` now support ``incr()`` and
|
* :ref:`Cache backends <topics-cache>` now support ``incr()`` and
|
||||||
``decr()`` commands to increment and decrement the value of a cache key.
|
``decr()`` commands to increment and decrement the value of a cache key.
|
||||||
On cache backends that support atomic increment/decrement -- most
|
On cache backends that support atomic increment/decrement -- most
|
||||||
notably, the memcached backend -- these operations will be atomic, and
|
notably, the memcached backend -- these operations will be atomic, and
|
||||||
quite fast.
|
quite fast.
|
||||||
|
|
||||||
* Django now can :ref:`easily delegate authentication to the web server
|
* Django now can :ref:`easily delegate authentication to the web server
|
||||||
<howto-auth-remote-user>` via a new authentication backend that supports
|
<howto-auth-remote-user>` via a new authentication backend that supports
|
||||||
the standard ``REMOTE_USER`` environment variable used for this purpose.
|
the standard ``REMOTE_USER`` environment variable used for this purpose.
|
||||||
|
|
||||||
* There's a new :func:`django.shortcuts.redirect` function that makes it
|
* There's a new :func:`django.shortcuts.redirect` function that makes it
|
||||||
easier to issue redirects given an object, a view name, or a URL.
|
easier to issue redirects given an object, a view name, or a URL.
|
||||||
|
|
||||||
* The ``postgresql_psycopg2`` backend now supports :ref:`native PostgreSQL
|
* The ``postgresql_psycopg2`` backend now supports :ref:`native PostgreSQL
|
||||||
autocommit <postgresql-notes>`. This is an advanced, PostgreSQL-specific
|
autocommit <postgresql-notes>`. This is an advanced, PostgreSQL-specific
|
||||||
feature, that can make certain read-heavy applications a good deal
|
feature, that can make certain read-heavy applications a good deal
|
||||||
@ -448,7 +447,7 @@ mailing list:
|
|||||||
join the discussions!
|
join the discussions!
|
||||||
|
|
||||||
Django's online documentation also includes pointers on how to contribute to
|
Django's online documentation also includes pointers on how to contribute to
|
||||||
Django:
|
Django:
|
||||||
|
|
||||||
* :ref:`How to contribute to Django <internals-contributing>`
|
* :ref:`How to contribute to Django <internals-contributing>`
|
||||||
|
|
||||||
|
@ -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
|
||||||
@ -723,14 +729,14 @@ the following line to your URLconf::
|
|||||||
|
|
||||||
* ``next``: The URL to redirect to after successful login. This may
|
* ``next``: The URL to redirect to after successful login. This may
|
||||||
contain a query string, too.
|
contain a query string, too.
|
||||||
|
|
||||||
* ``site``: The current :class:`~django.contrib.sites.models.Site`,
|
* ``site``: The current :class:`~django.contrib.sites.models.Site`,
|
||||||
according to the :setting:`SITE_ID` setting. If you don't have the
|
according to the :setting:`SITE_ID` setting. If you don't have the
|
||||||
site framework installed, this will be set to an instance of
|
site framework installed, this will be set to an instance of
|
||||||
:class:`~django.contrib.sites.models.RequestSite`, which derives the
|
:class:`~django.contrib.sites.models.RequestSite`, which derives the
|
||||||
site name and domain from the current
|
site name and domain from the current
|
||||||
:class:`~django.http.HttpRequest`.
|
:class:`~django.http.HttpRequest`.
|
||||||
|
|
||||||
* ``site_name``: An alias for ``site.name``. If you don't have the site
|
* ``site_name``: An alias for ``site.name``. If you don't have the site
|
||||||
framework installed, this will be set to the value of
|
framework installed, this will be set to the value of
|
||||||
:attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
|
:attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
|
||||||
@ -742,11 +748,11 @@ the following line to your URLconf::
|
|||||||
:file:`myapp/login.html` instead::
|
:file:`myapp/login.html` instead::
|
||||||
|
|
||||||
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
|
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
|
||||||
|
|
||||||
You can also specify the name of the ``GET`` field which contains the URL
|
You can also specify the name of the ``GET`` field which contains the URL
|
||||||
to redirect to after login by passing ``redirect_field_name`` to the view.
|
to redirect to after login by passing ``redirect_field_name`` to the view.
|
||||||
By default, the field is called ``next``.
|
By default, the field is called ``next``.
|
||||||
|
|
||||||
Here's a sample :file:`registration/login.html` template you can use as a
|
Here's a sample :file:`registration/login.html` template you can use as a
|
||||||
starting point. It assumes you have a :file:`base.html` template that
|
starting point. It assumes you have a :file:`base.html` template that
|
||||||
defines a ``content`` block:
|
defines a ``content`` block:
|
||||||
@ -800,7 +806,7 @@ includes a few other useful built-in views located in
|
|||||||
* ``template_name``: The full name of a template to display after
|
* ``template_name``: The full name of a template to display after
|
||||||
logging the user out. This will default to
|
logging the user out. This will default to
|
||||||
:file:`registration/logged_out.html` if no argument is supplied.
|
:file:`registration/logged_out.html` if no argument is supplied.
|
||||||
|
|
||||||
* ``redirect_field_name``: The name of a ``GET`` field containing the
|
* ``redirect_field_name``: The name of a ``GET`` field containing the
|
||||||
URL to redirect to after log out. Overrides ``next_page`` if the given
|
URL to redirect to after log out. Overrides ``next_page`` if the given
|
||||||
``GET`` parameter is passed.
|
``GET`` parameter is passed.
|
||||||
@ -859,17 +865,17 @@ includes a few other useful built-in views located in
|
|||||||
* ``email_template_name``: The full name of a template to use for
|
* ``email_template_name``: The full name of a template to use for
|
||||||
generating the e-mail with the new password. This will default to
|
generating the e-mail with the new password. This will default to
|
||||||
:file:`registration/password_reset_email.html` if not supplied.
|
:file:`registration/password_reset_email.html` if not supplied.
|
||||||
|
|
||||||
* ``password_reset_form``: Form that will be used to set the password.
|
* ``password_reset_form``: Form that will be used to set the password.
|
||||||
Defaults to ``SetPasswordForm``.
|
Defaults to ``SetPasswordForm``.
|
||||||
|
|
||||||
* ``token_generator``: Instance of the class to check the password. This
|
* ``token_generator``: Instance of the class to check the password. This
|
||||||
will default to ``default_token_generator``, it's an instance of
|
will default to ``default_token_generator``, it's an instance of
|
||||||
``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
|
``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
|
||||||
|
|
||||||
* ``post_reset_redirect``: The URL to redirect to after a successful
|
* ``post_reset_redirect``: The URL to redirect to after a successful
|
||||||
password change.
|
password change.
|
||||||
|
|
||||||
**Template context:**
|
**Template context:**
|
||||||
|
|
||||||
* ``form``: The form for resetting the user's password.
|
* ``form``: The form for resetting the user's password.
|
||||||
@ -897,11 +903,11 @@ includes a few other useful built-in views located in
|
|||||||
|
|
||||||
* ``login_url``: The URL of the login page to redirect to. This will
|
* ``login_url``: The URL of the login page to redirect to. This will
|
||||||
default to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied.
|
default to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied.
|
||||||
|
|
||||||
* ``redirect_field_name``: The name of a ``GET`` field containing the
|
* ``redirect_field_name``: The name of a ``GET`` field containing the
|
||||||
URL to redirect to after log out. Overrides ``next`` if the given
|
URL to redirect to after log out. Overrides ``next`` if the given
|
||||||
``GET`` parameter is passed.
|
``GET`` parameter is passed.
|
||||||
|
|
||||||
.. function:: password_reset_confirm(request[, uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect])
|
.. function:: password_reset_confirm(request[, uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect])
|
||||||
|
|
||||||
Presents a form for entering a new password.
|
Presents a form for entering a new password.
|
||||||
@ -926,7 +932,7 @@ includes a few other useful built-in views located in
|
|||||||
Presents a view which informs the user that the password has been
|
Presents a view which informs the user that the password has been
|
||||||
successfully changed.
|
successfully changed.
|
||||||
|
|
||||||
**Optional arguments:**
|
**Optional arguments:**
|
||||||
|
|
||||||
* ``template_name``: The full name of a template to display the view.
|
* ``template_name``: The full name of a template to display the view.
|
||||||
This will default to :file:`registration/password_reset_complete.html`.
|
This will default to :file:`registration/password_reset_complete.html`.
|
||||||
@ -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::
|
||||||
|
@ -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
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
@ -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
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
@ -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``
|
||||||
-----------------------------------------------------------------
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -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).
|
||||||
|
|
||||||
|
@ -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
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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 = {
|
||||||
|
@ -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,19 +11,20 @@ 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
|
||||||
|
|
||||||
class LongString(unittest.TestCase):
|
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)])
|
||||||
|
@ -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
|
||||||
|
|
||||||
@ -84,4 +89,65 @@ class DecoratorsTest(TestCase):
|
|||||||
response = callback(request)
|
response = callback(request)
|
||||||
|
|
||||||
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)
|
||||||
|
@ -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>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
...
|
...
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ def get_filter_tests():
|
|||||||
'filter-force-escape05': ('{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}', {"a": "x&y"}, u"x&y"),
|
'filter-force-escape05': ('{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}', {"a": "x&y"}, u"x&y"),
|
||||||
'filter-force-escape06': ('{{ a|force_escape|escape }}', {"a": "x&y"}, u"x&y"),
|
'filter-force-escape06': ('{{ a|force_escape|escape }}', {"a": "x&y"}, u"x&y"),
|
||||||
'filter-force-escape07': ('{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}', {"a": "x&y"}, u"x&y"),
|
'filter-force-escape07': ('{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}', {"a": "x&y"}, u"x&y"),
|
||||||
'filter-force-escape07': ('{{ a|escape|force_escape }}', {"a": "x&y"}, u"x&y"),
|
'filter-force-escape08': ('{{ a|escape|force_escape }}', {"a": "x&y"}, u"x&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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user