Merge remote-tracking branch 'django/master' into t3011
This commit is contained in:
commit
57ac6e3d32
3
AUTHORS
3
AUTHORS
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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} ",
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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 ###
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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']:
|
||||
|
|
|
@ -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']:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
"""
|
||||
|
|
|
@ -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))
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
============
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 |
|
@ -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``.
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ up to and including the new version.
|
|||
Final releases
|
||||
==============
|
||||
|
||||
.. _development_release_notes:
|
||||
|
||||
1.5 release
|
||||
-----------
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue