Merge remote-tracking branch 'django/master' into t3011

This commit is contained in:
Russell Keith-Magee 2012-09-15 14:31:54 +08:00
commit 57ac6e3d32
42 changed files with 195 additions and 356 deletions

View File

@ -31,6 +31,8 @@ The PRIMARY AUTHORS are (and/or have been):
* Claude Paroz
* Anssi Kääriäinen
* Florian Apolloner
* Jeremy Dunck
* Bryan Veloso
More information on the main contributors to Django can be found in
docs/internals/committers.txt.
@ -167,7 +169,6 @@ answer newbie questions, and generally made Django that much better:
dready <wil@mojipage.com>
Maximillian Dornseif <md@hudora.de>
Daniel Duan <DaNmarner@gmail.com>
Jeremy Dunck <http://dunck.us/>
Andrew Durdin <adurdin@gmail.com>
dusk@woofle.net
Andy Dustman <farcepest@gmail.com>

View File

@ -24,7 +24,9 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
if test_func(request.user):
return view_func(request, *args, **kwargs)
path = request.build_absolute_uri()
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
# urlparse chokes on lazy objects in Python 3, force to str
resolved_login_url = force_str(
resolve_url(login_url or settings.LOGIN_URL))
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
@ -33,7 +35,8 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(path, login_url, redirect_field_name)
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
return _wrapped_view
return decorator

View File

@ -11,7 +11,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, identify_hasher
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, identify_hasher
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import get_current_site
@ -24,16 +24,16 @@ mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p
class ReadOnlyPasswordHashWidget(forms.Widget):
def render(self, name, value, attrs):
encoded = value
if not is_password_usable(encoded):
return "None"
final_attrs = self.build_attrs(attrs)
if encoded == '' or encoded == UNUSABLE_PASSWORD:
summary = mark_safe("<strong>%s</strong>" % ugettext("No password set."))
else:
try:
hasher = identify_hasher(encoded)
except ValueError:
summary = mark_safe("<strong>Invalid password format or unknown hashing algorithm.</strong>")
summary = mark_safe("<strong>%s</strong>" % ugettext(
"Invalid password format or unknown hashing algorithm."))
else:
summary = format_html_join('',
"<strong>{0}</strong>: {1} ",

View File

@ -28,7 +28,13 @@ def reset_hashers(**kwargs):
def is_password_usable(encoded):
return (encoded is not None and encoded != UNUSABLE_PASSWORD)
if encoded is None or encoded == UNUSABLE_PASSWORD:
return False
try:
hasher = identify_hasher(encoded)
except ValueError:
return False
return True
def check_password(password, encoded, setter=None, preferred='default'):

View File

@ -240,23 +240,29 @@ class UserChangeFormTest(TestCase):
# Just check we can create it
form = MyUserForm({})
def test_unsuable_password(self):
user = User.objects.get(username='empty_password')
user.set_unusable_password()
user.save()
form = UserChangeForm(instance=user)
self.assertIn(_("No password set."), form.as_table())
def test_bug_17944_empty_password(self):
user = User.objects.get(username='empty_password')
form = UserChangeForm(instance=user)
# Just check that no error is raised.
form.as_table()
self.assertIn(_("No password set."), form.as_table())
def test_bug_17944_unmanageable_password(self):
user = User.objects.get(username='unmanageable_password')
form = UserChangeForm(instance=user)
# Just check that no error is raised.
form.as_table()
self.assertIn(_("Invalid password format or unknown hashing algorithm."),
form.as_table())
def test_bug_17944_unknown_password_algorithm(self):
user = User.objects.get(username='unknown_password')
form = UserChangeForm(instance=user)
# Just check that no error is raised.
form.as_table()
self.assertIn(_("Invalid password format or unknown hashing algorithm."),
form.as_table())
@skipIfCustomUser

View File

@ -100,6 +100,10 @@ class TestUtilsHashPass(unittest.TestCase):
self.assertRaises(ValueError, doit)
self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")
def test_bad_encoded(self):
self.assertFalse(is_password_usable('letmein_badencoded'))
self.assertFalse(is_password_usable(''))
def test_low_level_pkbdf2(self):
hasher = PBKDF2PasswordHasher()
encoded = hasher.encode('letmein', 'seasalt')

View File

@ -90,8 +90,6 @@ class BaseSpatialOperations(object):
# For quoting column values, rather than columns.
def geo_quote_name(self, name):
if isinstance(name, six.text_type):
name = name.encode('ascii')
return "'%s'" % name
# GeometryField operations

View File

@ -3,20 +3,6 @@ A collection of utility routines and classes used by the spatial
backends.
"""
from django.utils import six
def gqn(val):
"""
The geographic quote name function; used for quoting tables and
geometries (they use single rather than the double quotes of the
backend quotename function).
"""
if isinstance(val, six.string_types):
if isinstance(val, six.text_type): val = val.encode('ascii')
return "'%s'" % val
else:
return str(val)
class SpatialOperation(object):
"""
Base class for generating spatial SQL.

View File

@ -181,7 +181,11 @@ class DataSourceTest(unittest.TestCase):
# Making sure the SpatialReference is as expected.
if hasattr(source, 'srs_wkt'):
self.assertEqual(source.srs_wkt, g.srs.wkt)
self.assertEqual(
source.srs_wkt,
# Depending on lib versions, WGS_84 might be WGS_1984
g.srs.wkt.replace('SPHEROID["WGS_84"', 'SPHEROID["WGS_1984"')
)
def test06_spatial_filter(self):
"Testing the Layer.spatial_filter property."

View File

@ -1,3 +1,4 @@
import json
from binascii import b2a_hex
try:
from django.utils.six.moves import cPickle as pickle
@ -111,8 +112,9 @@ class OGRGeomTest(unittest.TestCase, TestDataMixin):
for g in self.geometries.json_geoms:
geom = OGRGeometry(g.wkt)
if not hasattr(g, 'not_equal'):
self.assertEqual(g.json, geom.json)
self.assertEqual(g.json, geom.geojson)
# Loading jsons to prevent decimal differences
self.assertEqual(json.loads(g.json), json.loads(geom.json))
self.assertEqual(json.loads(g.json), json.loads(geom.geojson))
self.assertEqual(OGRGeometry(g.wkt), OGRGeometry(geom.json))
def test02_points(self):

View File

@ -110,7 +110,7 @@ def geos_version_info():
is a release candidate (and what number release candidate), and the C API
version.
"""
ver = geos_version()
ver = geos_version().decode()
m = version_regex.match(ver)
if not m: raise GEOSException('Could not parse version info string "%s"' % ver)
return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))

View File

@ -215,15 +215,18 @@ class ListMixin(object):
"Standard list reverse method"
self[:] = self[-1::-1]
def sort(self, cmp=cmp, key=None, reverse=False):
def sort(self, cmp=None, key=None, reverse=False):
"Standard list sort method"
if key:
temp = [(key(v),v) for v in self]
temp.sort(cmp=cmp, key=lambda x: x[0], reverse=reverse)
temp.sort(key=lambda x: x[0], reverse=reverse)
self[:] = [v[1] for v in temp]
else:
temp = list(self)
if cmp is not None:
temp.sort(cmp=cmp, reverse=reverse)
else:
temp.sort(reverse=reverse)
self[:] = temp
### Private routines ###

View File

@ -16,7 +16,8 @@ test_suites = [
def suite():
"Builds a test suite for the GEOS tests."
s = TestSuite()
map(s.addTest, test_suites)
for suite in test_suites:
s.addTest(suite)
return s
def run(verbosity=1):

View File

@ -1,4 +1,5 @@
import ctypes
import json
import random
from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
@ -204,8 +205,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
for g in self.geometries.json_geoms:
geom = GEOSGeometry(g.wkt)
if not hasattr(g, 'not_equal'):
self.assertEqual(g.json, geom.json)
self.assertEqual(g.json, geom.geojson)
# Loading jsons to prevent decimal differences
self.assertEqual(json.loads(g.json), json.loads(geom.json))
self.assertEqual(json.loads(g.json), json.loads(geom.geojson))
self.assertEqual(GEOSGeometry(g.wkt), GEOSGeometry(geom.json))
def test_fromfile(self):

View File

@ -55,14 +55,14 @@ class ListMixinTest(unittest.TestCase):
def lists_of_len(self, length=None):
if length is None: length = self.limit
pl = range(length)
pl = list(range(length))
return pl, self.listType(pl)
def limits_plus(self, b):
return range(-self.limit - b, self.limit + b)
def step_range(self):
return range(-1 - self.limit, 0) + range(1, 1 + self.limit)
return list(range(-1 - self.limit, 0)) + list(range(1, 1 + self.limit))
def test01_getslice(self):
'Slice retrieval'
@ -160,13 +160,13 @@ class ListMixinTest(unittest.TestCase):
del pl[i:j]
del ul[i:j]
self.assertEqual(pl[:], ul[:], 'del slice [%d:%d]' % (i,j))
for k in range(-Len - 1,0) + range(1,Len):
for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[i:j:k]
del ul[i:j:k]
self.assertEqual(pl[:], ul[:], 'del slice [%d:%d:%d]' % (i,j,k))
for k in range(-Len - 1,0) + range(1,Len):
for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[:i:k]
del ul[:i:k]
@ -177,7 +177,7 @@ class ListMixinTest(unittest.TestCase):
del ul[i::k]
self.assertEqual(pl[:], ul[:], 'del slice [%d::%d]' % (i,k))
for k in range(-Len - 1,0) + range(1,Len):
for k in list(range(-Len - 1, 0)) + list(range(1, Len)):
pl, ul = self.lists_of_len(Len)
del pl[::k]
del ul[::k]
@ -320,7 +320,7 @@ class ListMixinTest(unittest.TestCase):
pl.sort()
ul.sort()
self.assertEqual(pl[:], ul[:], 'sort')
mid = pl[len(pl) / 2]
mid = pl[len(pl) // 2]
pl.sort(key=lambda x: (mid-x)**2)
ul.sort(key=lambda x: (mid-x)**2)
self.assertEqual(pl[:], ul[:], 'sort w/ key')
@ -330,7 +330,7 @@ class ListMixinTest(unittest.TestCase):
pl.sort(reverse=True)
ul.sort(reverse=True)
self.assertEqual(pl[:], ul[:], 'sort w/ reverse')
mid = pl[len(pl) / 2]
mid = pl[len(pl) // 2]
pl.sort(key=lambda x: (mid-x)**2)
ul.sort(key=lambda x: (mid-x)**2)
self.assertEqual(pl[:], ul[:], 'sort w/ key')
@ -338,7 +338,7 @@ class ListMixinTest(unittest.TestCase):
def test_12_arithmetic(self):
'Arithmetic'
pl, ul = self.lists_of_len()
al = range(10,14)
al = list(range(10,14))
self.assertEqual(list(pl + al), list(ul + al), 'add')
self.assertEqual(type(ul), type(ul + al), 'type of add result')
self.assertEqual(list(al + pl), list(al + ul), 'radd')

View File

@ -8,9 +8,11 @@ from django.utils import unittest
test_srs = ({'srid' : 4326,
'auth_name' : ('EPSG', True),
'auth_srid' : 4326,
'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
'srtext14' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
'proj4' : '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
# Only the beginning, because there are differences depending on installed libs
'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84"',
'proj4' : ['+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
# +ellps=WGS84 has been removed in the 4326 proj string in proj-4.8
'+proj=longlat +datum=WGS84 +no_defs '],
'spheroid' : 'WGS 84', 'name' : 'WGS 84',
'geographic' : True, 'projected' : False, 'spatialite' : True,
'ellipsoid' : (6378137.0, 6356752.3, 298.257223563), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
@ -19,9 +21,9 @@ test_srs = ({'srid' : 4326,
{'srid' : 32140,
'auth_name' : ('EPSG', False),
'auth_srid' : 32140,
'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',
'srtext14': 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],AUTHORITY["EPSG","32140"],AXIS["X",EAST],AXIS["Y",NORTH]]',
'proj4' : '+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980"',
'proj4' : ['+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
'+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
'spheroid' : 'GRS 1980', 'name' : 'NAD83 / Texas South Central',
'geographic' : False, 'projected' : True, 'spatialite' : False,
'ellipsoid' : (6378137.0, 6356752.31414, 298.257222101), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
@ -51,17 +53,12 @@ class SpatialRefSysTest(unittest.TestCase):
# No proj.4 and different srtext on oracle backends :(
if postgis:
if connection.ops.spatial_version >= (1, 4, 0):
srtext = sd['srtext14']
else:
srtext = sd['srtext']
self.assertEqual(srtext, srs.wkt)
self.assertEqual(sd['proj4'], srs.proj4text)
self.assertTrue(srs.wkt.startswith(sd['srtext']))
self.assertTrue(srs.proj4text in sd['proj4'])
@no_mysql
def test02_osr(self):
"Testing getting OSR objects from SpatialRefSys model objects."
from django.contrib.gis.gdal import GDAL_VERSION
for sd in test_srs:
sr = SpatialRefSys.objects.get(srid=sd['srid'])
self.assertEqual(True, sr.spheroid.startswith(sd['spheroid']))
@ -76,15 +73,10 @@ class SpatialRefSysTest(unittest.TestCase):
# Testing the SpatialReference object directly.
if postgis or spatialite:
srs = sr.srs
if GDAL_VERSION <= (1, 8):
self.assertEqual(sd['proj4'], srs.proj4)
self.assertTrue(srs.proj4 in sd['proj4'])
# No `srtext` field in the `spatial_ref_sys` table in SpatiaLite
if not spatialite:
if connection.ops.spatial_version >= (1, 4, 0):
srtext = sd['srtext14']
else:
srtext = sd['srtext']
self.assertEqual(srtext, srs.wkt)
self.assertTrue(srs.wkt.startswith(sd['srtext']))
@no_mysql
def test03_ellipsoid(self):

View File

@ -222,7 +222,6 @@ class WSGIHandler(base.BaseHandler):
set_script_prefix(base.get_script_name(environ))
signals.request_started.send(sender=self.__class__)
try:
try:
request = self.request_class(environ)
except UnicodeDecodeError:

View File

@ -1,4 +1,5 @@
from optparse import make_option
from datetime import datetime
import os
import re
import sys
@ -90,10 +91,12 @@ class Command(BaseCommand):
self.stdout.write("Validating models...\n\n")
self.validate(display_num_errors=True)
self.stdout.write((
"%(started_at)s\n"
"Django version %(version)s, using settings %(settings)r\n"
"Development server is running at http://%(addr)s:%(port)s/\n"
"Quit the server with %(quit_command)s.\n"
) % {
"started_at": datetime.now().strftime('%B %d, %Y - %X'),
"version": self.get_version(),
"settings": settings.SETTINGS_MODULE,
"addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr,

View File

@ -37,6 +37,7 @@ from django.db.backends.mysql.client import DatabaseClient
from django.db.backends.mysql.creation import DatabaseCreation
from django.db.backends.mysql.introspection import DatabaseIntrospection
from django.db.backends.mysql.validation import DatabaseValidation
from django.utils.encoding import force_str
from django.utils.functional import cached_property
from django.utils.safestring import SafeBytes, SafeText
from django.utils import six
@ -390,7 +391,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if settings_dict['NAME']:
kwargs['db'] = settings_dict['NAME']
if settings_dict['PASSWORD']:
kwargs['passwd'] = settings_dict['PASSWORD']
kwargs['passwd'] = force_str(settings_dict['PASSWORD'])
if settings_dict['HOST'].startswith('/'):
kwargs['unix_socket'] = settings_dict['HOST']
elif settings_dict['HOST']:

View File

@ -13,6 +13,7 @@ from django.db.backends.postgresql_psycopg2.client import DatabaseClient
from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation
from django.db.backends.postgresql_psycopg2.version import get_version
from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
from django.utils.encoding import force_str
from django.utils.log import getLogger
from django.utils.safestring import SafeText, SafeBytes
from django.utils import six
@ -172,7 +173,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
if settings_dict['USER']:
conn_params['user'] = settings_dict['USER']
if settings_dict['PASSWORD']:
conn_params['password'] = settings_dict['PASSWORD']
conn_params['password'] = force_str(settings_dict['PASSWORD'])
if settings_dict['HOST']:
conn_params['host'] = settings_dict['HOST']
if settings_dict['PORT']:

View File

@ -199,7 +199,7 @@ class CharField(Field):
def widget_attrs(self, widget):
attrs = super(CharField, self).widget_attrs(widget)
if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
if self.max_length is not None and isinstance(widget, TextInput):
# The HTML attribute is maxlength, not max_length.
attrs.update({'maxlength': str(self.max_length)})
return attrs

View File

@ -260,10 +260,17 @@ class Input(Widget):
final_attrs['value'] = force_text(self._format_value(value))
return format_html('<input{0} />', flatatt(final_attrs))
class TextInput(Input):
input_type = 'text'
class PasswordInput(Input):
def __init__(self, attrs=None):
if attrs is not None:
self.input_type = attrs.pop('type', self.input_type)
super(TextInput, self).__init__(attrs)
class PasswordInput(TextInput):
input_type = 'password'
def __init__(self, attrs=None, render_value=False):
@ -400,9 +407,8 @@ class Textarea(Widget):
flatatt(final_attrs),
force_text(value))
class DateInput(Input):
input_type = 'text'
class DateInput(TextInput):
def __init__(self, attrs=None, format=None):
super(DateInput, self).__init__(attrs)
if format:
@ -431,9 +437,8 @@ class DateInput(Input):
pass
return super(DateInput, self)._has_changed(self._format_value(initial), data)
class DateTimeInput(Input):
input_type = 'text'
class DateTimeInput(TextInput):
def __init__(self, attrs=None, format=None):
super(DateTimeInput, self).__init__(attrs)
if format:
@ -462,9 +467,8 @@ class DateTimeInput(Input):
pass
return super(DateTimeInput, self)._has_changed(self._format_value(initial), data)
class TimeInput(Input):
input_type = 'text'
class TimeInput(TextInput):
def __init__(self, attrs=None, format=None):
super(TimeInput, self).__init__(attrs)
if format:

View File

@ -105,7 +105,7 @@ class CsrfViewMiddleware(object):
if getattr(callback, 'csrf_exempt', False):
return None
# Assume that anything not defined as 'safe' by RC2616 needs protection
# Assume that anything not defined as 'safe' by RFC2616 needs protection
if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
if getattr(request, '_dont_enforce_csrf_checks', False):
# Mechanism to turn off CSRF checks for test suite.

View File

@ -531,11 +531,9 @@ def cycle(parser, token):
The optional flag "silent" can be used to prevent the cycle declaration
from returning any value::
{% cycle 'row1' 'row2' as rowcolors silent %}{# no value here #}
{% for o in some_list %}
<tr class="{% cycle rowcolors %}">{# first value will be "row1" #}
...
</tr>
{% cycle 'row1' 'row2' as rowcolors silent %}
<tr class="{{ rowcolors }}">{% include "subtemplate.html " %}</tr>
{% endfor %}
"""

View File

@ -5,8 +5,7 @@ import sys
current_version = sys.version_info
use_workaround = (
(current_version < (2, 6, 8)) or
(current_version >= (2, 7) and current_version < (2, 7, 3)) or
(current_version < (2, 7, 3)) or
(current_version >= (3, 0) and current_version < (3, 2, 3))
)

View File

@ -28,14 +28,3 @@ Indices, glossary and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`glossary`
Deprecated/obsolete documentation
=================================
The following documentation covers features that have been deprecated or that
have been replaced in newer versions of Django.
.. toctree::
:maxdepth: 2
obsolete/index

View File

@ -91,8 +91,7 @@ The dynamically-generated admin site is ugly! How can I change it?
We like it, but if you don't agree, you can modify the admin site's
presentation by editing the CSS stylesheet and/or associated image files. The
site is built using semantic HTML and plenty of CSS hooks, so any changes you'd
like to make should be possible by editing the stylesheet. We've got a
:doc:`guide to the CSS used in the admin </obsolete/admin-css>` to get you started.
like to make should be possible by editing the stylesheet.
What browsers are supported for using the admin?
------------------------------------------------

View File

@ -5,7 +5,7 @@
Django documentation
====================
.. rubric:: Everything you need to know about Django (and then some).
.. rubric:: Everything you need to know about Django.
Getting help
============

View File

@ -379,6 +379,34 @@ Florian Apolloner
.. _Graz University of Technology: http://tugraz.at/
.. _Ubuntuusers webteam: http://wiki.ubuntuusers.de/ubuntuusers/Webteam
Jeremy Dunck
Jeremy was rescued from corporate IT drudgery by Free Software and, in part,
Django. Many of Jeremy's interests center around access to information.
Jeremy was the lead developer of Pegasus News, one of the first uses of
Django outside World Online, and has since joined Votizen, a startup intent
on reducing the influence of money in politics.
He serves as DSF Secretary, organizes and helps organize sprints, cares
about the health and equity of the Django community. He has gone an
embarrassingly long time without a working blog.
Jeremy lives in Mountain View, CA, USA.
`Bryan Veloso`_
Bryan found Django 0.96 through a fellow designer who was evangelizing
its use. It was his first foray outside of the land that was PHP-based
templating. Although he has only ever used Django for personal projects,
it is the very reason he considers himself a designer/developer
hybrid and is working to further design within the Django community.
Bryan works as a designer at GitHub by day, and masquerades as a `vlogger`_
and `shoutcaster`_ in the after-hours. Bryan lives in Los Angeles, CA, USA.
.. _bryan veloso: http://avalonstar.com/
.. _vlogger: http://youtube.com/bryanveloso/
.. _shoutcaster: http://twitch.tv/vlogalonstar/
Specialists
-----------
@ -403,16 +431,6 @@ Ian Kelly
Matt Boersma
Matt is also responsible for Django's Oracle support.
Jeremy Dunck
Jeremy is the lead developer of Pegasus News, a personalized local site based
in Dallas, Texas. An early contributor to Greasemonkey and Django, he sees
technology as a tool for communication and access to knowledge.
Jeremy helped kick off GeoDjango development, and is mostly responsible for
the serious speed improvements that signals received in Django 1.0.
Jeremy lives in Dallas, Texas, USA.
`Simon Meers`_
Simon discovered Django 0.96 during his Computer Science PhD research and
has been developing with it full-time ever since. His core code

View File

@ -67,8 +67,7 @@ different needs:
whathaveyou.
* Finally, there's some "specialized" documentation not usually relevant to
most developers. This includes the :doc:`release notes </releases/index>`,
:doc:`documentation of obsolete features </obsolete/index>`,
most developers. This includes the :doc:`release notes </releases/index>` and
:doc:`internals documentation </internals/index>` for those who want to add
code to Django itself, and a :doc:`few other things that simply don't fit
elsewhere </misc/index>`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,186 +0,0 @@
======================================
Customizing the Django admin interface
======================================
.. warning::
The design of the admin has changed somewhat since this document was
written, and parts may not apply any more. This document is no longer
maintained since an official API for customizing the Django admin interface
is in development.
Django's dynamic admin interface gives you a fully-functional admin for free
with no hand-coding required. The dynamic admin is designed to be
production-ready, not just a starting point, so you can use it as-is on a real
site. While the underlying format of the admin pages is built in to Django, you
can customize the look and feel by editing the admin stylesheet and images.
Here's a quick and dirty overview some of the main styles and classes used in
the Django admin CSS.
Modules
=======
The ``.module`` class is a basic building block for grouping content in the
admin. It's generally applied to a ``div`` or a ``fieldset``. It wraps the content
group in a box and applies certain styles to the elements within. An ``h2``
within a ``div.module`` will align to the top of the ``div`` as a header for the
whole group.
.. image:: _images/module.png
:alt: Example use of module class on admin homepage
Column Types
============
.. note::
All admin pages (except the dashboard) are fluid-width. All fixed-width
classes from previous Django versions have been removed.
The base template for each admin page has a block that defines the column
structure for the page. This sets a class on the page content area
(``div#content``) so everything on the page knows how wide it should be. There
are three column types available.
colM
This is the default column setting for all pages. The "M" stands for "main".
Assumes that all content on the page is in one main column
(``div#content-main``).
colMS
This is for pages with one main column and a sidebar on the right. The "S"
stands for "sidebar". Assumes that main content is in ``div#content-main``
and sidebar content is in ``div#content-related``. This is used on the main
admin page.
colSM
Same as above, with the sidebar on the left. The source order of the columns
doesn't matter.
For instance, you could stick this in a template to make a two-column page with
the sidebar on the right:
.. code-block:: html+django
{% block coltype %}colMS{% endblock %}
Text Styles
===========
Font Sizes
----------
Most HTML elements (headers, lists, etc.) have base font sizes in the stylesheet
based on context. There are three classes are available for forcing text to a
certain size in any context.
small
11px
tiny
10px
mini
9px (use sparingly)
Font Styles and Alignment
-------------------------
There are also a few styles for styling text.
.quiet
Sets font color to light gray. Good for side notes in instructions. Combine
with ``.small`` or ``.tiny`` for sheer excitement.
.help
This is a custom class for blocks of inline help text explaining the
function of form elements. It makes text smaller and gray, and when applied
to ``p`` elements within ``.form-row`` elements (see Form Styles below),
it will offset the text to align with the form field. Use this for help
text, instead of ``small quiet``. It works on other elements, but try to
put the class on a ``p`` whenever you can.
.align-left
It aligns the text left. Only works on block elements containing inline
elements.
.align-right
Are you paying attention?
.nowrap
Keeps text and inline objects from wrapping. Comes in handy for table
headers you want to stay on one line.
Floats and Clears
-----------------
float-left
floats left
float-right
floats right
clear
clears all
Object Tools
============
Certain actions which apply directly to an object are used in form and
changelist pages. These appear in a "toolbar" row above the form or changelist,
to the right of the page. The tools are wrapped in a ``ul`` with the class
``object-tools``. There are two custom tool types which can be defined with an
additional class on the ``a`` for that tool. These are ``.addlink`` and
``.viewsitelink``.
Example from a changelist page:
.. code-block:: html+django
<ul class="object-tools">
<li><a href="/stories/add/" class="addlink">Add redirect</a></li>
</ul>
.. image:: _images/objecttools_01.png
:alt: Object tools on a changelist page
and from a form page:
.. code-block:: html+django
<ul class="object-tools">
<li><a href="/history/303/152383/">History</a></li>
<li><a href="/r/303/152383/" class="viewsitelink">View on site</a></li>
</ul>
.. image:: _images/objecttools_02.png
:alt: Object tools on a form page
Form Styles
===========
Fieldsets
---------
Admin forms are broken up into groups by ``fieldset`` elements. Each form fieldset
should have a class ``.module``. Each fieldset should have a header ``h2`` within the
fieldset at the top (except the first group in the form, and in some cases where the
group of fields doesn't have a logical label).
Each fieldset can also take extra classes in addition to ``.module`` to apply
appropriate formatting to the group of fields.
.aligned
This will align the labels and inputs side by side on the same line.
.wide
Used in combination with ``.aligned`` to widen the space available for the
labels.
Form Rows
---------
Each row of the form (within the ``fieldset``) should be enclosed in a ``div``
with class ``form-row``. If the field in the row is required, a class of
``required`` should also be added to the ``div.form-row``.
.. image:: _images/formrow.png
:alt: Example use of form-row class
Labels
------
Form labels should always precede the field, except in the case
of checkboxes and radio buttons, where the ``input`` should come first. Any
explanation or help text should follow the ``label`` in a ``p`` with class
``.help``.

View File

@ -1,12 +0,0 @@
Deprecated/obsolete documentation
=================================
These documents cover features that have been deprecated or that have been
replaced in newer versions of Django. They're preserved here for folks using old
versions of Django or those still using deprecated APIs. No new code based on
these APIs should be written.
.. toctree::
:maxdepth: 1
admin-css

View File

@ -126,8 +126,9 @@ provided for each widget will be rendered exactly the same::
On a real Web page, you probably don't want every widget to look the same. You
might want a larger input element for the comment, and you might want the
'name' widget to have some special CSS class. To do this, you use the
:attr:`Widget.attrs` argument when creating the widget:
'name' widget to have some special CSS class. It is also possible to specify
the 'type' attribute to take advantage of the new HTML5 input types. To do
this, you use the :attr:`Widget.attrs` argument when creating the widget:
For example::
@ -245,7 +246,7 @@ commonly used groups of widgets:
Date input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: DateInput.format
@ -262,7 +263,7 @@ commonly used groups of widgets:
Date/time input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: DateTimeInput.format
@ -279,7 +280,7 @@ commonly used groups of widgets:
Time input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
Takes same arguments as :class:`TextInput`, with one more optional argument:
.. attribute:: TimeInput.format

View File

@ -14,6 +14,7 @@ up to and including the new version.
Final releases
==============
.. _development_release_notes:
1.5 release
-----------

View File

@ -257,15 +257,14 @@ Installing the development version
If you decide to use the latest development version of Django,
you'll want to pay close attention to `the development timeline`_,
and you'll want to keep an eye on `the list of
backwards-incompatible changes`_. This will help you stay on top
of any new features you might want to use, as well as any changes
and you'll want to keep an eye on the :ref:`release notes for the
upcoming release <development_release_notes>`. This will help you stay
on top of any new features you might want to use, as well as any changes
you'll need to make to your code when updating your copy of Django.
(For stable releases, any necessary changes are documented in the
release notes.)
.. _the development timeline: https://code.djangoproject.com/timeline
.. _the list of backwards-incompatible changes: https://code.djangoproject.com/wiki/BackwardsIncompatibleChanges
If you'd like to be able to update your Django code occasionally with the
latest bug fixes and improvements, follow these instructions:

View File

@ -401,6 +401,19 @@ class BackendTestCase(TestCase):
self.assertEqual(list(cursor.fetchmany(2)), [('Jane', 'Doe'), ('John', 'Doe')])
self.assertEqual(list(cursor.fetchall()), [('Mary', 'Agnelline'), ('Peter', 'Parker')])
def test_unicode_password(self):
old_password = connection.settings_dict['PASSWORD']
connection.settings_dict['PASSWORD'] = "françois"
try:
cursor = connection.cursor()
except backend.Database.DatabaseError:
# As password is probably wrong, a database exception is expected
pass
except Exception as e:
self.fail("Unexpected error raised with unicode password: %s" % e)
finally:
connection.settings_dict['PASSWORD'] = old_password
def test_database_operations_helper_class(self):
# Ticket #13630
self.assertTrue(hasattr(connection, 'ops'))

View File

@ -31,9 +31,9 @@ class FormsWidgetTestCase(TestCase):
self.assertHTMLEqual(w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}), '<input type="text" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />')
# You can also pass 'attrs' to the constructor:
w = TextInput(attrs={'class': 'fun'})
self.assertHTMLEqual(w.render('email', ''), '<input type="text" class="fun" name="email" />')
self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="text" class="fun" value="foo@example.com" name="email" />')
w = TextInput(attrs={'class': 'fun', 'type': 'email'})
self.assertHTMLEqual(w.render('email', ''), '<input type="email" class="fun" name="email" />')
self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="email" class="fun" value="foo@example.com" name="email" />')
# 'attrs' passed to render() get precedence over those passed to the constructor:
w = TextInput(attrs={'class': 'pretty'})
@ -915,8 +915,8 @@ beatle J R Ringo False""")
self.assertHTMLEqual(w.render('date', datetime.datetime(2007, 9, 17, 12, 51)), '<input type="text" name="date" value="2007-09-17 12:51:00" />')
# Use 'format' to change the way a value is displayed.
w = DateTimeInput(format='%d/%m/%Y %H:%M')
self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17/09/2007 12:51" />')
w = DateTimeInput(format='%d/%m/%Y %H:%M', attrs={'type': 'datetime'})
self.assertHTMLEqual(w.render('date', d), '<input type="datetime" name="date" value="17/09/2007 12:51" />')
self.assertFalse(w._has_changed(d, '17/09/2007 12:51'))
# Make sure a custom format works with _has_changed. The hidden input will use
@ -938,8 +938,8 @@ beatle J R Ringo False""")
self.assertHTMLEqual(w.render('date', '2007-09-17'), '<input type="text" name="date" value="2007-09-17" />')
# Use 'format' to change the way a value is displayed.
w = DateInput(format='%d/%m/%Y')
self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17/09/2007" />')
w = DateInput(format='%d/%m/%Y', attrs={'type': 'date'})
self.assertHTMLEqual(w.render('date', d), '<input type="date" name="date" value="17/09/2007" />')
self.assertFalse(w._has_changed(d, '17/09/2007'))
# Make sure a custom format works with _has_changed. The hidden input will use
@ -963,8 +963,8 @@ beatle J R Ringo False""")
self.assertHTMLEqual(w.render('time', '13:12:11'), '<input type="text" name="time" value="13:12:11" />')
# Use 'format' to change the way a value is displayed.
w = TimeInput(format='%H:%M')
self.assertHTMLEqual(w.render('time', t), '<input type="text" name="time" value="12:51" />')
w = TimeInput(format='%H:%M', attrs={'type': 'time'})
self.assertHTMLEqual(w.render('time', t), '<input type="time" name="time" value="12:51" />')
self.assertFalse(w._has_changed(t, '12:51'))
# Make sure a custom format works with _has_changed. The hidden input will use

View File

@ -1,21 +1,26 @@
import os
from django.utils import unittest
from django.utils._os import safe_join
class SafeJoinTests(unittest.TestCase):
def test_base_path_ends_with_sep(self):
drive, path = os.path.splitdrive(safe_join("/abc/", "abc"))
self.assertEqual(
safe_join("/abc/", "abc"),
"/abc/abc",
path,
"{0}abc{0}abc".format(os.path.sep)
)
def test_root_path(self):
drive, path = os.path.splitdrive(safe_join("/", "path"))
self.assertEqual(
safe_join("/", "path"),
"/path",
path,
"{0}path".format(os.path.sep),
)
drive, path = os.path.splitdrive(safe_join("/", ""))
self.assertEqual(
safe_join("/", ""),
"/",
path,
os.path.sep,
)