Merged recent Django trunk changes.
This commit is contained in:
commit
0229209c84
1
AUTHORS
1
AUTHORS
|
@ -33,6 +33,7 @@ The PRIMARY AUTHORS are (and/or have been):
|
|||
* Florian Apolloner
|
||||
* Jeremy Dunck
|
||||
* Bryan Veloso
|
||||
* Preston Holmes
|
||||
|
||||
More information on the main contributors to Django can be found in
|
||||
docs/internals/committers.txt.
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
======================
|
||||
Contributing to Django
|
||||
======================
|
||||
|
||||
As an open source project, Django welcomes contributions of many forms.
|
||||
|
||||
Examples of contributions include:
|
||||
|
||||
* Code patches
|
||||
* Documentation improvements
|
||||
* Bug reports and patch reviews
|
||||
|
||||
Extensive contribution guidelines are available in the repository at
|
||||
``docs/internals/contributing/``, or online at:
|
||||
|
||||
https://docs.djangoproject.com/en/dev/internals/contributing/
|
|
@ -54,7 +54,7 @@ class UserAdmin(admin.ModelAdmin):
|
|||
add_form = UserCreationForm
|
||||
change_password_form = AdminPasswordChangeForm
|
||||
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
|
||||
list_filter = ('is_staff', 'is_superuser', 'is_active')
|
||||
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
|
||||
search_fields = ('username', 'first_name', 'last_name', 'email')
|
||||
ordering = ('username',)
|
||||
filter_horizontal = ('user_permissions',)
|
||||
|
|
|
@ -28,7 +28,7 @@ def update_last_login(sender, user, **kwargs):
|
|||
the user logging in.
|
||||
"""
|
||||
user.last_login = timezone.now()
|
||||
user.save()
|
||||
user.save(update_fields=['last_login'])
|
||||
user_logged_in.connect(update_last_login)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from django.contrib.auth import signals
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
|
||||
|
||||
|
@ -49,3 +51,16 @@ class SignalTestCase(TestCase):
|
|||
self.client.get('/logout/next_page/')
|
||||
self.assertEqual(len(self.logged_out), 1)
|
||||
self.assertEqual(self.logged_out[0].username, 'testclient')
|
||||
|
||||
def test_update_last_login(self):
|
||||
"""Ensure that only `last_login` is updated in `update_last_login`"""
|
||||
user = User.objects.get(pk=3)
|
||||
old_last_login = user.last_login
|
||||
|
||||
user.username = "This username shouldn't get saved"
|
||||
request = RequestFactory().get('/login')
|
||||
signals.user_logged_in.send(sender=user.__class__, request=request,
|
||||
user=user)
|
||||
user = User.objects.get(pk=3)
|
||||
self.assertEqual(user.username, 'staff')
|
||||
self.assertNotEqual(user.last_login, old_last_login)
|
||||
|
|
|
@ -109,10 +109,12 @@ OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:3857", OpenLayers.Layer.Sp
|
|||
{% autoescape off %}{% for item in map_options.items %} '{{ item.0 }}' : {{ item.1 }}{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}{% endautoescape %} };{% endblock %}
|
||||
// The admin map for this geometry field.
|
||||
{% block map_creation %}
|
||||
{{ module }}.map = new OpenLayers.Map('{{ id }}_map', options);
|
||||
// Base Layer
|
||||
{{ module }}.layers.base = {% block base_layer %}new OpenLayers.Layer.WMS("{{ wms_name }}", "{{ wms_url }}", {layers: '{{ wms_layer }}'{{ wms_options|safe }}});{% endblock %}
|
||||
{{ module }}.map.addLayer({{ module }}.layers.base);
|
||||
{% endblock %}
|
||||
{% block extra_layers %}{% endblock %}
|
||||
{% if is_linestring %}OpenLayers.Feature.Vector.style["default"]["strokeWidth"] = 3; // Default too thin for linestrings. {% endif %}
|
||||
{{ module }}.layers.vector = new OpenLayers.Layer.Vector(" {{ field_name }}");
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
from __future__ import absolute_import
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from django.utils.unittest import TestCase
|
||||
from django.contrib.gis.db.models import Union, Extent3D
|
||||
from django.contrib.gis.geos import GEOSGeometry, Point, Polygon
|
||||
from django.contrib.gis.utils import LayerMapping, LayerMapError
|
||||
from django.test import TestCase
|
||||
|
||||
from .models import (City3D, Interstate2D, Interstate3D, InterstateProj2D,
|
||||
InterstateProj3D, Point2D, Point3D, MultiPoint3D, Polygon2D, Polygon3D)
|
||||
|
||||
|
||||
data_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data'))
|
||||
city_file = os.path.join(data_path, 'cities', 'cities.shp')
|
||||
vrt_file = os.path.join(data_path, 'test_vrt', 'test_vrt.vrt')
|
||||
|
@ -46,12 +47,11 @@ interstate_data = (
|
|||
# Bounding box polygon for inner-loop of Houston (in projected coordinate
|
||||
# system 32140), with elevation values from the National Elevation Dataset
|
||||
# (see above).
|
||||
bbox_wkt = 'POLYGON((941527.97 4225693.20,962596.48 4226349.75,963152.57 4209023.95,942051.75 4208366.38,941527.97 4225693.20))'
|
||||
bbox_z = (21.71, 13.21, 9.12, 16.40, 21.71)
|
||||
def gen_bbox():
|
||||
bbox_2d = GEOSGeometry(bbox_wkt, srid=32140)
|
||||
bbox_3d = Polygon(tuple((x, y, z) for (x, y), z in zip(bbox_2d[0].coords, bbox_z)), srid=32140)
|
||||
return bbox_2d, bbox_3d
|
||||
bbox_data = (
|
||||
'POLYGON((941527.97 4225693.20,962596.48 4226349.75,963152.57 4209023.95,942051.75 4208366.38,941527.97 4225693.20))',
|
||||
(21.71, 13.21, 9.12, 16.40, 21.71)
|
||||
)
|
||||
|
||||
|
||||
class Geo3DTest(TestCase):
|
||||
"""
|
||||
|
@ -63,20 +63,7 @@ class Geo3DTest(TestCase):
|
|||
http://postgis.refractions.net/documentation/manual-1.4/ch08.html#PostGIS_3D_Functions
|
||||
"""
|
||||
|
||||
def test01_3d(self):
|
||||
"Test the creation of 3D models."
|
||||
# 3D models for the rest of the tests will be populated in here.
|
||||
# For each 3D data set create model (and 2D version if necessary),
|
||||
# retrieve, and assert geometry is in 3D and contains the expected
|
||||
# 3D values.
|
||||
for name, pnt_data in city_data:
|
||||
x, y, z = pnt_data
|
||||
pnt = Point(x, y, z, srid=4326)
|
||||
City3D.objects.create(name=name, point=pnt)
|
||||
city = City3D.objects.get(name=name)
|
||||
self.assertTrue(city.point.hasz)
|
||||
self.assertEqual(z, city.point.z)
|
||||
|
||||
def _load_interstate_data(self):
|
||||
# Interstate (2D / 3D and Geographic/Projected variants)
|
||||
for name, line, exp_z in interstate_data:
|
||||
line_3d = GEOSGeometry(line, srid=4269)
|
||||
|
@ -90,26 +77,51 @@ class Geo3DTest(TestCase):
|
|||
Interstate2D.objects.create(name=name, line=line_2d)
|
||||
InterstateProj2D.objects.create(name=name, line=line_2d)
|
||||
|
||||
# Retrieving and making sure it's 3D and has expected
|
||||
# Z values -- shouldn't change because of coordinate system.
|
||||
def _load_city_data(self):
|
||||
for name, pnt_data in city_data:
|
||||
City3D.objects.create(name=name, point=Point(*pnt_data, srid=4326))
|
||||
|
||||
def _load_polygon_data(self):
|
||||
bbox_wkt, bbox_z = bbox_data
|
||||
bbox_2d = GEOSGeometry(bbox_wkt, srid=32140)
|
||||
bbox_3d = Polygon(tuple((x, y, z) for (x, y), z in zip(bbox_2d[0].coords, bbox_z)), srid=32140)
|
||||
Polygon2D.objects.create(name='2D BBox', poly=bbox_2d)
|
||||
Polygon3D.objects.create(name='3D BBox', poly=bbox_3d)
|
||||
|
||||
def test_3d_hasz(self):
|
||||
"""
|
||||
Make sure data is 3D and has expected Z values -- shouldn't change
|
||||
because of coordinate system.
|
||||
"""
|
||||
self._load_interstate_data()
|
||||
for name, line, exp_z in interstate_data:
|
||||
interstate = Interstate3D.objects.get(name=name)
|
||||
interstate_proj = InterstateProj3D.objects.get(name=name)
|
||||
for i in [interstate, interstate_proj]:
|
||||
self.assertTrue(i.line.hasz)
|
||||
self.assertEqual(exp_z, tuple(i.line.z))
|
||||
|
||||
# Creating 3D Polygon.
|
||||
bbox2d, bbox3d = gen_bbox()
|
||||
Polygon2D.objects.create(name='2D BBox', poly=bbox2d)
|
||||
Polygon3D.objects.create(name='3D BBox', poly=bbox3d)
|
||||
self._load_city_data()
|
||||
for name, pnt_data in city_data:
|
||||
city = City3D.objects.get(name=name)
|
||||
z = pnt_data[2]
|
||||
self.assertTrue(city.point.hasz)
|
||||
self.assertEqual(z, city.point.z)
|
||||
|
||||
def test_3d_polygons(self):
|
||||
"""
|
||||
Test the creation of polygon 3D models.
|
||||
"""
|
||||
self._load_polygon_data()
|
||||
p3d = Polygon3D.objects.get(name='3D BBox')
|
||||
self.assertTrue(p3d.poly.hasz)
|
||||
self.assertEqual(bbox3d, p3d.poly)
|
||||
|
||||
def test01a_3d_layermapping(self):
|
||||
"Testing LayerMapping on 3D models."
|
||||
from .models import Point2D, Point3D
|
||||
self.assertIsInstance(p3d.poly, Polygon)
|
||||
self.assertEqual(p3d.poly.srid, 32140)
|
||||
|
||||
def test_3d_layermapping(self):
|
||||
"""
|
||||
Testing LayerMapping on 3D models.
|
||||
"""
|
||||
point_mapping = {'point' : 'POINT'}
|
||||
mpoint_mapping = {'mpoint' : 'MULTIPOINT'}
|
||||
|
||||
|
@ -134,34 +146,46 @@ class Geo3DTest(TestCase):
|
|||
lm.save()
|
||||
self.assertEqual(3, MultiPoint3D.objects.count())
|
||||
|
||||
def test02a_kml(self):
|
||||
"Test GeoQuerySet.kml() with Z values."
|
||||
def test_kml(self):
|
||||
"""
|
||||
Test GeoQuerySet.kml() with Z values.
|
||||
"""
|
||||
self._load_city_data()
|
||||
h = City3D.objects.kml(precision=6).get(name='Houston')
|
||||
# KML should be 3D.
|
||||
# `SELECT ST_AsKML(point, 6) FROM geo3d_city3d WHERE name = 'Houston';`
|
||||
ref_kml_regex = re.compile(r'^<Point><coordinates>-95.363\d+,29.763\d+,18</coordinates></Point>$')
|
||||
self.assertTrue(ref_kml_regex.match(h.kml))
|
||||
|
||||
def test02b_geojson(self):
|
||||
"Test GeoQuerySet.geojson() with Z values."
|
||||
def test_geojson(self):
|
||||
"""
|
||||
Test GeoQuerySet.geojson() with Z values.
|
||||
"""
|
||||
self._load_city_data()
|
||||
h = City3D.objects.geojson(precision=6).get(name='Houston')
|
||||
# GeoJSON should be 3D
|
||||
# `SELECT ST_AsGeoJSON(point, 6) FROM geo3d_city3d WHERE name='Houston';`
|
||||
ref_json_regex = re.compile(r'^{"type":"Point","coordinates":\[-95.363151,29.763374,18(\.0+)?\]}$')
|
||||
self.assertTrue(ref_json_regex.match(h.geojson))
|
||||
|
||||
def test03a_union(self):
|
||||
"Testing the Union aggregate of 3D models."
|
||||
def test_union(self):
|
||||
"""
|
||||
Testing the Union aggregate of 3D models.
|
||||
"""
|
||||
# PostGIS query that returned the reference EWKT for this test:
|
||||
# `SELECT ST_AsText(ST_Union(point)) FROM geo3d_city3d;`
|
||||
self._load_city_data()
|
||||
ref_ewkt = 'SRID=4326;MULTIPOINT(-123.305196 48.462611 15,-104.609252 38.255001 1433,-97.521157 34.464642 380,-96.801611 32.782057 147,-95.363151 29.763374 18,-95.23506 38.971823 251,-87.650175 41.850385 181,174.783117 -41.315268 14)'
|
||||
ref_union = GEOSGeometry(ref_ewkt)
|
||||
union = City3D.objects.aggregate(Union('point'))['point__union']
|
||||
self.assertTrue(union.hasz)
|
||||
self.assertEqual(ref_union, union)
|
||||
|
||||
def test03b_extent(self):
|
||||
"Testing the Extent3D aggregate for 3D models."
|
||||
def test_extent(self):
|
||||
"""
|
||||
Testing the Extent3D aggregate for 3D models.
|
||||
"""
|
||||
self._load_city_data()
|
||||
# `SELECT ST_Extent3D(point) FROM geo3d_city3d;`
|
||||
ref_extent3d = (-123.305196, -41.315268, 14,174.783117, 48.462611, 1433)
|
||||
extent1 = City3D.objects.aggregate(Extent3D('point'))['point__extent3d']
|
||||
|
@ -174,8 +198,11 @@ class Geo3DTest(TestCase):
|
|||
for e3d in [extent1, extent2]:
|
||||
check_extent3d(e3d)
|
||||
|
||||
def test04_perimeter(self):
|
||||
"Testing GeoQuerySet.perimeter() on 3D fields."
|
||||
def test_perimeter(self):
|
||||
"""
|
||||
Testing GeoQuerySet.perimeter() on 3D fields.
|
||||
"""
|
||||
self._load_polygon_data()
|
||||
# Reference query for values below:
|
||||
# `SELECT ST_Perimeter3D(poly), ST_Perimeter2D(poly) FROM geo3d_polygon3d;`
|
||||
ref_perim_3d = 76859.2620451
|
||||
|
@ -188,12 +215,15 @@ class Geo3DTest(TestCase):
|
|||
Polygon3D.objects.perimeter().get(name='3D BBox').perimeter.m,
|
||||
tol)
|
||||
|
||||
def test05_length(self):
|
||||
"Testing GeoQuerySet.length() on 3D fields."
|
||||
def test_length(self):
|
||||
"""
|
||||
Testing GeoQuerySet.length() on 3D fields.
|
||||
"""
|
||||
# ST_Length_Spheroid Z-aware, and thus does not need to use
|
||||
# a separate function internally.
|
||||
# `SELECT ST_Length_Spheroid(line, 'SPHEROID["GRS 1980",6378137,298.257222101]')
|
||||
# FROM geo3d_interstate[2d|3d];`
|
||||
self._load_interstate_data()
|
||||
tol = 3
|
||||
ref_length_2d = 4368.1721949481
|
||||
ref_length_3d = 4368.62547052088
|
||||
|
@ -217,16 +247,22 @@ class Geo3DTest(TestCase):
|
|||
InterstateProj3D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
|
||||
def test06_scale(self):
|
||||
"Testing GeoQuerySet.scale() on Z values."
|
||||
def test_scale(self):
|
||||
"""
|
||||
Testing GeoQuerySet.scale() on Z values.
|
||||
"""
|
||||
self._load_city_data()
|
||||
# Mapping of City name to reference Z values.
|
||||
zscales = (-3, 4, 23)
|
||||
for zscale in zscales:
|
||||
for city in City3D.objects.scale(1.0, 1.0, zscale):
|
||||
self.assertEqual(city_dict[city.name][2] * zscale, city.scale.z)
|
||||
|
||||
def test07_translate(self):
|
||||
"Testing GeoQuerySet.translate() on Z values."
|
||||
def test_translate(self):
|
||||
"""
|
||||
Testing GeoQuerySet.translate() on Z values.
|
||||
"""
|
||||
self._load_city_data()
|
||||
ztranslations = (5.23, 23, -17)
|
||||
for ztrans in ztranslations:
|
||||
for city in City3D.objects.translate(0, 0, ztrans):
|
||||
|
|
|
@ -520,8 +520,8 @@ class GeoQuerySetTest(TestCase):
|
|||
if oracle:
|
||||
# No precision parameter for Oracle :-/
|
||||
gml_regex = re.compile(r'^<gml:Point srsName="SDO:4326" xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="\." cs="," ts=" ">-104.60925\d+,38.25500\d+ </gml:coordinates></gml:Point>')
|
||||
elif spatialite:
|
||||
# Spatialite has extra colon in SrsName
|
||||
elif spatialite and connection.ops.spatial_version < (3, 0, 0):
|
||||
# Spatialite before 3.0 has extra colon in SrsName
|
||||
gml_regex = re.compile(r'^<gml:Point SrsName="EPSG::4326"><gml:coordinates decimal="\." cs="," ts=" ">-104.609251\d+,38.255001</gml:coordinates></gml:Point>')
|
||||
else:
|
||||
gml_regex = re.compile(r'^<gml:Point srsName="EPSG:4326"><gml:coordinates>-104\.60925\d+,38\.255001</gml:coordinates></gml:Point>')
|
||||
|
|
|
@ -8,6 +8,7 @@ from django.contrib.gis.gdal import DataSource
|
|||
from django.contrib.gis.tests.utils import mysql
|
||||
from django.contrib.gis.utils.layermapping import (LayerMapping, LayerMapError,
|
||||
InvalidDecimal, MissingForeignKey)
|
||||
from django.db import router
|
||||
from django.test import TestCase
|
||||
|
||||
from .models import (
|
||||
|
@ -26,6 +27,7 @@ NAMES = ['Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo']
|
|||
NUMS = [1, 2, 1, 19, 1] # Number of polygons for each.
|
||||
STATES = ['Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado']
|
||||
|
||||
|
||||
class LayerMapTest(TestCase):
|
||||
|
||||
def test_init(self):
|
||||
|
@ -281,3 +283,31 @@ class LayerMapTest(TestCase):
|
|||
lm.save(silent=True, strict=True)
|
||||
self.assertEqual(City.objects.count(), 3)
|
||||
self.assertEqual(City.objects.all().order_by('name_txt')[0].name_txt, "Houston")
|
||||
|
||||
|
||||
class OtherRouter(object):
|
||||
def db_for_read(self, model, **hints):
|
||||
return 'other'
|
||||
|
||||
def db_for_write(self, model, **hints):
|
||||
return self.db_for_read(model, **hints)
|
||||
|
||||
def allow_relation(self, obj1, obj2, **hints):
|
||||
return None
|
||||
|
||||
def allow_syncdb(self, db, model):
|
||||
return True
|
||||
|
||||
|
||||
class LayerMapRouterTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.old_routers = router.routers
|
||||
router.routers = [OtherRouter()]
|
||||
|
||||
def tearDown(self):
|
||||
router.routers = self.old_routers
|
||||
|
||||
def test_layermapping_default_db(self):
|
||||
lm = LayerMapping(City, city_shp, city_mapping)
|
||||
self.assertEqual(lm.using, 'other')
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import sys
|
||||
from decimal import Decimal
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import connections, DEFAULT_DB_ALIAS
|
||||
from django.db import connections, router
|
||||
from django.contrib.gis.db.models import GeometryField
|
||||
from django.contrib.gis.gdal import (CoordTransform, DataSource,
|
||||
OGRException, OGRGeometry, OGRGeomType, SpatialReference)
|
||||
|
@ -67,7 +67,7 @@ class LayerMapping(object):
|
|||
def __init__(self, model, data, mapping, layer=0,
|
||||
source_srs=None, encoding=None,
|
||||
transaction_mode='commit_on_success',
|
||||
transform=True, unique=None, using=DEFAULT_DB_ALIAS):
|
||||
transform=True, unique=None, using=None):
|
||||
"""
|
||||
A LayerMapping object is initialized using the given Model (not an instance),
|
||||
a DataSource (or string path to an OGR-supported data file), and a mapping
|
||||
|
@ -81,8 +81,8 @@ class LayerMapping(object):
|
|||
self.ds = data
|
||||
self.layer = self.ds[layer]
|
||||
|
||||
self.using = using
|
||||
self.spatial_backend = connections[using].ops
|
||||
self.using = using if using is not None else router.db_for_write(model)
|
||||
self.spatial_backend = connections[self.using].ops
|
||||
|
||||
# Setting the mapping & model attributes.
|
||||
self.mapping = mapping
|
||||
|
|
|
@ -12,6 +12,7 @@ from django.contrib.sessions.backends.file import SessionStore as FileSession
|
|||
from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.core.cache import DEFAULT_CACHE_ALIAS
|
||||
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
|
||||
from django.http import HttpResponse
|
||||
from django.test import TestCase, RequestFactory
|
||||
|
@ -133,6 +134,9 @@ class SessionTestsMixin(object):
|
|||
self.assertTrue(self.session.modified)
|
||||
|
||||
def test_save(self):
|
||||
if (hasattr(self.session, '_cache') and
|
||||
'DummyCache' in settings.CACHES[DEFAULT_CACHE_ALIAS]['BACKEND']):
|
||||
raise unittest.SkipTest("Session saving tests require a real cache backend")
|
||||
self.session.save()
|
||||
self.assertTrue(self.session.exists(self.session.session_key))
|
||||
|
||||
|
@ -296,6 +300,8 @@ class CacheDBSessionTests(SessionTestsMixin, TestCase):
|
|||
|
||||
backend = CacheDBSession
|
||||
|
||||
@unittest.skipIf('DummyCache' in settings.CACHES[DEFAULT_CACHE_ALIAS]['BACKEND'],
|
||||
"Session saving tests require a real cache backend")
|
||||
def test_exists_searches_cache_first(self):
|
||||
self.session.save()
|
||||
with self.assertNumQueries(0):
|
||||
|
|
|
@ -192,7 +192,10 @@ class FileSystemStorage(Storage):
|
|||
else:
|
||||
# This fun binary flag incantation makes os.open throw an
|
||||
# OSError if the file already exists before we open it.
|
||||
fd = os.open(full_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
|
||||
flags = (os.O_WRONLY | os.O_CREAT | os.O_EXCL |
|
||||
getattr(os, 'O_BINARY', 0))
|
||||
# The current umask value is masked out by os.open!
|
||||
fd = os.open(full_path, flags, 0o666)
|
||||
try:
|
||||
locks.lock(fd, locks.LOCK_EX)
|
||||
_file = None
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
@ -7,10 +8,9 @@ from django import http
|
|||
from django.core import signals
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.importlib import import_module
|
||||
from django.utils.log import getLogger
|
||||
from django.utils import six
|
||||
|
||||
logger = getLogger('django.request')
|
||||
logger = logging.getLogger('django.request')
|
||||
|
||||
|
||||
class BaseHandler(object):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from io import BytesIO
|
||||
from threading import Lock
|
||||
|
@ -10,9 +11,8 @@ from django.core.handlers import base
|
|||
from django.core.urlresolvers import set_script_prefix
|
||||
from django.utils import datastructures
|
||||
from django.utils.encoding import force_str, force_text, iri_to_uri
|
||||
from django.utils.log import getLogger
|
||||
|
||||
logger = getLogger('django.request')
|
||||
logger = logging.getLogger('django.request')
|
||||
|
||||
|
||||
# See http://www.iana.org/assignments/http-status-codes
|
||||
|
|
|
@ -20,5 +20,7 @@ class EmailBackend(BaseEmailBackend):
|
|||
|
||||
def send_messages(self, messages):
|
||||
"""Redirect messages to the dummy outbox"""
|
||||
for message in messages: # .message() triggers header validation
|
||||
message.message()
|
||||
mail.outbox.extend(messages)
|
||||
return len(messages)
|
||||
|
|
|
@ -3,6 +3,7 @@ PostgreSQL database backend for Django.
|
|||
|
||||
Requires psycopg 2: http://initd.org/projects/psycopg2
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from django.db import utils
|
||||
|
@ -14,7 +15,6 @@ 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
|
||||
from django.utils.timezone import utc
|
||||
|
@ -33,7 +33,7 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
|||
psycopg2.extensions.register_adapter(SafeBytes, psycopg2.extensions.QuotedString)
|
||||
psycopg2.extensions.register_adapter(SafeText, psycopg2.extensions.QuotedString)
|
||||
|
||||
logger = getLogger('django.db.backends')
|
||||
logger = logging.getLogger('django.db.backends')
|
||||
|
||||
def utc_tzinfo_factory(offset):
|
||||
if offset != 0:
|
||||
|
|
|
@ -3,15 +3,15 @@ from __future__ import unicode_literals
|
|||
import datetime
|
||||
import decimal
|
||||
import hashlib
|
||||
import logging
|
||||
from time import time
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.log import getLogger
|
||||
from django.utils.timezone import utc
|
||||
|
||||
|
||||
logger = getLogger('django.db.backends')
|
||||
logger = logging.getLogger('django.db.backends')
|
||||
|
||||
|
||||
class CursorWrapper(object):
|
||||
|
|
|
@ -609,8 +609,12 @@ class SQLCompiler(object):
|
|||
restricted = False
|
||||
|
||||
for f, model in opts.get_fields_with_model():
|
||||
# The get_fields_with_model() returns None for fields that live
|
||||
# in the field's local model. So, for those fields we want to use
|
||||
# the f.model - that is the field's local model.
|
||||
field_model = model or f.model
|
||||
if not select_related_descend(f, restricted, requested,
|
||||
only_load.get(model or self.query.model)):
|
||||
only_load.get(field_model)):
|
||||
continue
|
||||
# The "avoid" set is aliases we want to avoid just for this
|
||||
# particular branch of the recursion. They aren't permanently
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import hashlib
|
||||
import logging
|
||||
import re
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -6,9 +7,9 @@ from django import http
|
|||
from django.core.mail import mail_managers
|
||||
from django.utils.http import urlquote
|
||||
from django.core import urlresolvers
|
||||
from django.utils.log import getLogger
|
||||
|
||||
logger = getLogger('django.request')
|
||||
|
||||
logger = logging.getLogger('django.request')
|
||||
|
||||
|
||||
class CommonMiddleware(object):
|
||||
|
|
|
@ -7,6 +7,7 @@ against request forgeries from other sites.
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
import re
|
||||
import random
|
||||
|
||||
|
@ -15,10 +16,10 @@ from django.core.urlresolvers import get_callable
|
|||
from django.utils.cache import patch_vary_headers
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.http import same_origin
|
||||
from django.utils.log import getLogger
|
||||
from django.utils.crypto import constant_time_compare, get_random_string
|
||||
|
||||
logger = getLogger('django.request')
|
||||
|
||||
logger = logging.getLogger('django.request')
|
||||
|
||||
REASON_NO_REFERER = "Referer checking failed - no Referer."
|
||||
REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
|
||||
|
|
|
@ -649,6 +649,7 @@ class TransactionTestCase(SimpleTestCase):
|
|||
self.assertEqual(response.status_code, status_code,
|
||||
msg_prefix + "Couldn't retrieve content: Response code was %d"
|
||||
" (expected %d)" % (response.status_code, status_code))
|
||||
text = force_text(text, encoding=response._charset)
|
||||
content = response.content.decode(response._charset)
|
||||
if html:
|
||||
content = assert_and_parse_html(self, content, None,
|
||||
|
@ -684,6 +685,7 @@ class TransactionTestCase(SimpleTestCase):
|
|||
self.assertEqual(response.status_code, status_code,
|
||||
msg_prefix + "Couldn't retrieve content: Response code was %d"
|
||||
" (expected %d)" % (response.status_code, status_code))
|
||||
text = force_text(text, encoding=response._charset)
|
||||
content = response.content.decode(response._charset)
|
||||
if html:
|
||||
content = assert_and_parse_html(self, content, None,
|
||||
|
|
|
@ -110,8 +110,8 @@ class TimeFormat(Formatter):
|
|||
return '%02d' % self.data.second
|
||||
|
||||
def u(self):
|
||||
"Microseconds"
|
||||
return self.data.microsecond
|
||||
"Microseconds; i.e. '000000' to '999999'"
|
||||
return '%06d' %self.data.microsecond
|
||||
|
||||
|
||||
class DateFormat(TimeFormat):
|
||||
|
|
|
@ -42,29 +42,26 @@ def escape(text):
|
|||
return mark_safe(force_text(text).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", '''))
|
||||
escape = allow_lazy(escape, six.text_type)
|
||||
|
||||
_base_js_escapes = (
|
||||
('\\', '\\u005C'),
|
||||
('\'', '\\u0027'),
|
||||
('"', '\\u0022'),
|
||||
('>', '\\u003E'),
|
||||
('<', '\\u003C'),
|
||||
('&', '\\u0026'),
|
||||
('=', '\\u003D'),
|
||||
('-', '\\u002D'),
|
||||
(';', '\\u003B'),
|
||||
('\u2028', '\\u2028'),
|
||||
('\u2029', '\\u2029')
|
||||
)
|
||||
_js_escapes = {
|
||||
ord('\\'): '\\u005C',
|
||||
ord('\''): '\\u0027',
|
||||
ord('"'): '\\u0022',
|
||||
ord('>'): '\\u003E',
|
||||
ord('<'): '\\u003C',
|
||||
ord('&'): '\\u0026',
|
||||
ord('='): '\\u003D',
|
||||
ord('-'): '\\u002D',
|
||||
ord(';'): '\\u003B',
|
||||
ord('\u2028'): '\\u2028',
|
||||
ord('\u2029'): '\\u2029'
|
||||
}
|
||||
|
||||
# Escape every ASCII character with a value less than 32.
|
||||
_js_escapes = (_base_js_escapes +
|
||||
tuple([('%c' % z, '\\u%04X' % z) for z in range(32)]))
|
||||
_js_escapes.update((ord('%c' % z), '\\u%04X' % z) for z in range(32))
|
||||
|
||||
def escapejs(value):
|
||||
"""Hex encodes characters for use in JavaScript strings."""
|
||||
for bad, good in _js_escapes:
|
||||
value = mark_safe(force_text(value).replace(bad, good))
|
||||
return value
|
||||
return mark_safe(force_text(value).translate(_js_escapes))
|
||||
escapejs = allow_lazy(escapejs, six.text_type)
|
||||
|
||||
def conditional_escape(text):
|
||||
|
|
|
@ -21,12 +21,10 @@ def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
|
|||
if isinstance(number, int) and not use_grouping and not decimal_pos:
|
||||
return mark_safe(six.text_type(number))
|
||||
# sign
|
||||
if float(number) < 0:
|
||||
sign = '-'
|
||||
else:
|
||||
sign = ''
|
||||
sign = ''
|
||||
str_number = six.text_type(number)
|
||||
if str_number[0] == '-':
|
||||
sign = '-'
|
||||
str_number = str_number[1:]
|
||||
# decimal part
|
||||
if '.' in str_number:
|
||||
|
@ -48,4 +46,3 @@ def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
|
|||
int_part_gd += digit
|
||||
int_part = int_part_gd[::-1]
|
||||
return sign + int_part + dec_part
|
||||
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
Decorators for views based on HTTP headers.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from calendar import timegm
|
||||
from functools import wraps
|
||||
|
||||
from django.utils.decorators import decorator_from_middleware, available_attrs
|
||||
from django.utils.http import http_date, parse_http_date_safe, parse_etags, quote_etag
|
||||
from django.utils.log import getLogger
|
||||
from django.middleware.http import ConditionalGetMiddleware
|
||||
from django.http import HttpResponseNotAllowed, HttpResponseNotModified, HttpResponse
|
||||
|
||||
conditional_page = decorator_from_middleware(ConditionalGetMiddleware)
|
||||
|
||||
logger = getLogger('django.request')
|
||||
logger = logging.getLogger('django.request')
|
||||
|
||||
|
||||
def require_http_methods(request_method_list):
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
from functools import update_wrapper
|
||||
|
||||
from django import http
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.log import getLogger
|
||||
from django.utils.decorators import classonlymethod
|
||||
from django.utils import six
|
||||
|
||||
logger = getLogger('django.request')
|
||||
logger = logging.getLogger('django.request')
|
||||
|
||||
|
||||
class ContextMixin(object):
|
||||
|
|
|
@ -377,7 +377,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
|||
"""
|
||||
return self.date_list_period
|
||||
|
||||
def get_date_list(self, queryset, date_type=None):
|
||||
def get_date_list(self, queryset, date_type=None, ordering='ASC'):
|
||||
"""
|
||||
Get a date list by calling `queryset.dates()`, checking along the way
|
||||
for empty lists that aren't allowed.
|
||||
|
@ -387,7 +387,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
|||
if date_type is None:
|
||||
date_type = self.get_date_list_period()
|
||||
|
||||
date_list = queryset.dates(date_field, date_type)[::-1]
|
||||
date_list = queryset.dates(date_field, date_type, ordering)
|
||||
if date_list is not None and not date_list and not allow_empty:
|
||||
name = force_text(queryset.model._meta.verbose_name_plural)
|
||||
raise Http404(_("No %(verbose_name_plural)s available") %
|
||||
|
@ -409,7 +409,7 @@ class BaseArchiveIndexView(BaseDateListView):
|
|||
Return (date_list, items, extra_context) for this request.
|
||||
"""
|
||||
qs = self.get_dated_queryset(ordering='-%s' % self.get_date_field())
|
||||
date_list = self.get_date_list(qs)
|
||||
date_list = self.get_date_list(qs, ordering='DESC')
|
||||
|
||||
if not date_list:
|
||||
qs = qs.none()
|
||||
|
|
|
@ -760,8 +760,6 @@ A few things to note about the ``simple_tag`` helper function:
|
|||
* If the argument was a template variable, our function is passed the
|
||||
current value of the variable, not the variable itself.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
If your template tag needs to access the current context, you can use the
|
||||
``takes_context`` argument when registering your tag:
|
||||
|
||||
|
|
|
@ -44,8 +44,6 @@ setting.
|
|||
|
||||
.. seealso::
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Server error emails are sent using the logging framework, so you can
|
||||
customize this behavior by :doc:`customizing your logging configuration
|
||||
</topics/logging>`.
|
||||
|
@ -99,8 +97,6 @@ The best way to disable this behavior is to set
|
|||
|
||||
.. seealso::
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
404 errors are logged using the logging framework. By default, these log
|
||||
records are ignored, but you can use them for error reporting by writing a
|
||||
handler and :doc:`configuring logging </topics/logging>` appropriately.
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
Managing static files
|
||||
=====================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Django developers mostly concern themselves with the dynamic parts of web
|
||||
applications -- the views and templates that render anew for each request. But
|
||||
web applications have other parts: the static files (images, CSS,
|
||||
|
|
|
@ -407,6 +407,18 @@ Jeremy Dunck
|
|||
.. _vlogger: http://youtube.com/bryanveloso/
|
||||
.. _shoutcaster: http://twitch.tv/vlogalonstar/
|
||||
|
||||
`Preston Holmes`_
|
||||
Preston is a recovering neuroscientist who originally discovered Django as
|
||||
part of a sweeping move to Python from a grab bag of half a dozen
|
||||
languages. He was drawn to Django's balance of practical batteries included
|
||||
philosophy, care and thought in code design, and strong open source
|
||||
community. In addition to his current job in private progressive education,
|
||||
Preston contributes some developer time to local non-profits.
|
||||
|
||||
Preston lives with his family and animal menagerie in Santa Barbara, CA, USA.
|
||||
|
||||
.. _Preston Holmes: http://www.ptone.com/
|
||||
|
||||
Specialists
|
||||
-----------
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ these changes.
|
|||
|
||||
* The function-based generic view modules will be removed in favor of their
|
||||
class-based equivalents, outlined :doc:`here
|
||||
</topics/class-based-views/index>`:
|
||||
</topics/class-based-views/index>`.
|
||||
|
||||
* The :class:`~django.core.servers.basehttp.AdminMediaHandler` will be
|
||||
removed. In its place use
|
||||
|
|
|
@ -155,8 +155,6 @@ Certain APIs are explicitly marked as "internal" in a couple of ways:
|
|||
Local flavors
|
||||
-------------
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
:mod:`django.contrib.localflavor` contains assorted pieces of code
|
||||
that are useful for particular countries or cultures. This data is
|
||||
local in nature, and is subject to change on timelines that will
|
||||
|
|
|
@ -87,16 +87,24 @@ YearArchiveView
|
|||
* ``year``: A :class:`~datetime.date` object
|
||||
representing the given year.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
|
||||
Previously, this returned a string.
|
||||
|
||||
* ``next_year``: A :class:`~datetime.date` object
|
||||
representing the first day of the next year, according to
|
||||
:attr:`~BaseDateListView.allow_empty` and
|
||||
:attr:`~DateMixin.allow_future`.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
|
||||
* ``previous_year``: A :class:`~datetime.date` object
|
||||
representing the first day of the previous year, according to
|
||||
:attr:`~BaseDateListView.allow_empty` and
|
||||
:attr:`~DateMixin.allow_future`.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
|
||||
**Notes**
|
||||
|
||||
* Uses a default ``template_name_suffix`` of ``_archive_year``.
|
||||
|
|
|
@ -318,12 +318,16 @@ BaseDateListView
|
|||
Returns the aggregation period for ``date_list``. Returns
|
||||
:attr:`~BaseDateListView.date_list_period` by default.
|
||||
|
||||
.. method:: get_date_list(queryset, date_type=None)
|
||||
.. method:: get_date_list(queryset, date_type=None, ordering='ASC')
|
||||
|
||||
Returns the list of dates of type ``date_type`` for which ``queryset``
|
||||
contains entries. For example, ``get_date_list(qs, 'year')`` will
|
||||
return the list of years for which ``qs`` has entries. If
|
||||
``date_type`` isn't provided, the result of
|
||||
:meth:`BaseDateListView.get_date_list_period` is used. See
|
||||
:meth:`~django.db.models.query.QuerySet.dates()` for the ways that the
|
||||
``date_type`` argument can be used.
|
||||
:meth:`~BaseDateListView.get_date_list_period` is used. ``date_type``
|
||||
and ``ordering`` are simply passed to
|
||||
:meth:`QuerySet.dates()<django.db.models.query.QuerySet.dates>`.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
The ``ordering`` parameter was added, and the default order was
|
||||
changed to ascending.
|
||||
|
|
|
@ -129,8 +129,6 @@ subclass::
|
|||
|
||||
date_hierarchy = 'pub_date'
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
This will intelligently populate itself based on available data,
|
||||
e.g. if all the dates are in one month, it'll show the day-level
|
||||
drill-down only.
|
||||
|
@ -576,8 +574,6 @@ subclass::
|
|||
class PersonAdmin(ModelAdmin):
|
||||
list_filter = ('is_staff', 'company')
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Field names in ``list_filter`` can also span relations
|
||||
using the ``__`` lookup, for example::
|
||||
|
||||
|
@ -748,8 +744,6 @@ subclass::
|
|||
|
||||
.. attribute:: ModelAdmin.paginator
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The paginator class to be used for pagination. By default,
|
||||
:class:`django.core.paginator.Paginator` is used. If the custom paginator
|
||||
class doesn't have the same constructor interface as
|
||||
|
@ -966,8 +960,6 @@ templates used by the :class:`ModelAdmin` views:
|
|||
|
||||
.. method:: ModelAdmin.delete_model(self, request, obj)
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The ``delete_model`` method is given the ``HttpRequest`` and a model
|
||||
instance. Use this method to do pre- or post-delete operations.
|
||||
|
||||
|
@ -1213,8 +1205,6 @@ templates used by the :class:`ModelAdmin` views:
|
|||
|
||||
.. method:: ModelAdmin.get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Returns an instance of the paginator to use for this view. By default,
|
||||
instantiates an instance of :attr:`paginator`.
|
||||
|
||||
|
@ -1295,8 +1285,6 @@ on your ``ModelAdmin``::
|
|||
}
|
||||
js = ("my_code.js",)
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
The :doc:`staticfiles app </ref/contrib/staticfiles>` prepends
|
||||
:setting:`STATIC_URL` (or :setting:`MEDIA_URL` if :setting:`STATIC_URL` is
|
||||
``None``) to any media paths. The same rules apply as :ref:`regular media
|
||||
|
@ -1394,18 +1382,15 @@ adds some of its own (the shared features are actually defined in the
|
|||
- :attr:`~ModelAdmin.exclude`
|
||||
- :attr:`~ModelAdmin.filter_horizontal`
|
||||
- :attr:`~ModelAdmin.filter_vertical`
|
||||
- :attr:`~ModelAdmin.ordering`
|
||||
- :attr:`~ModelAdmin.prepopulated_fields`
|
||||
- :meth:`~ModelAdmin.queryset`
|
||||
- :attr:`~ModelAdmin.radio_fields`
|
||||
- :attr:`~ModelAdmin.readonly_fields`
|
||||
- :attr:`~InlineModelAdmin.raw_id_fields`
|
||||
- :meth:`~ModelAdmin.formfield_for_foreignkey`
|
||||
- :meth:`~ModelAdmin.formfield_for_manytomany`
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
- :attr:`~ModelAdmin.ordering`
|
||||
- :meth:`~ModelAdmin.queryset`
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
- :meth:`~ModelAdmin.has_add_permission`
|
||||
|
@ -1813,8 +1798,6 @@ Templates can override or extend base admin templates as described in
|
|||
|
||||
.. attribute:: AdminSite.login_form
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Subclass of :class:`~django.contrib.auth.forms.AuthenticationForm` that
|
||||
will be used by the admin site login view.
|
||||
|
||||
|
|
|
@ -152,27 +152,6 @@ enable it in your project's ``urls.py``:
|
|||
|
||||
Now you should have the latest comment feeds being served off ``/feeds/latest/``.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
Prior to Django 1.3, the LatestCommentFeed was deployed using the
|
||||
syndication feed view:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.conf.urls import patterns
|
||||
from django.contrib.comments.feeds import LatestCommentFeed
|
||||
|
||||
feeds = {
|
||||
'latest': LatestCommentFeed,
|
||||
}
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# ...
|
||||
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
|
||||
{'feed_dict': feeds}),
|
||||
# ...
|
||||
)
|
||||
|
||||
|
||||
Moderation
|
||||
==========
|
||||
|
|
|
@ -136,10 +136,6 @@ Simply subclassing :class:`CommentModerator` and changing the values of these
|
|||
options will automatically enable the various moderation methods for any
|
||||
models registered using the subclass.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
``moderate_after`` and ``close_after`` now accept 0 as a valid value.
|
||||
|
||||
Adding custom moderation methods
|
||||
--------------------------------
|
||||
|
||||
|
|
|
@ -423,8 +423,6 @@ pointing at it will be deleted as well. In the example above, this means that
|
|||
if a ``Bookmark`` object were deleted, any ``TaggedItem`` objects pointing at
|
||||
it would be deleted at the same time.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Unlike :class:`~django.db.models.ForeignKey`,
|
||||
:class:`~django.contrib.contenttypes.generic.GenericForeignKey` does not accept
|
||||
an :attr:`~django.db.models.ForeignKey.on_delete` argument to customize this
|
||||
|
|
|
@ -239,8 +239,6 @@ template.
|
|||
Getting a list of :class:`~django.contrib.flatpages.models.FlatPage` objects in your templates
|
||||
==============================================================================================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The flatpages app provides a template tag that allows you to iterate
|
||||
over all of the available flatpages on the :ref:`current site
|
||||
<hooking-into-current-site-from-views>`.
|
||||
|
|
|
@ -45,7 +45,7 @@ GeoDjango's admin site
|
|||
.. attribute:: openlayers_url
|
||||
|
||||
Link to the URL of the OpenLayers JavaScript. Defaults to
|
||||
``'http://openlayers.org/api/2.8/OpenLayers.js'``.
|
||||
``'http://openlayers.org/api/2.11/OpenLayers.js'``.
|
||||
|
||||
|
||||
.. attribute:: modifiable
|
||||
|
|
|
@ -163,6 +163,11 @@ WKB / EWKB ``buffer``
|
|||
GeoJSON ``str`` or ``unicode``
|
||||
============= ======================
|
||||
|
||||
.. note::
|
||||
|
||||
The new 3D/4D WKT notation with an intermediary Z or M (like
|
||||
``POINT Z (3, 4, 5)``) is only supported with GEOS 3.3.0 or later.
|
||||
|
||||
Properties
|
||||
~~~~~~~~~~
|
||||
|
||||
|
@ -232,8 +237,6 @@ Returns a boolean indicating whether the geometry is valid.
|
|||
|
||||
.. attribute:: GEOSGeometry.valid_reason
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Returns a string describing the reason why a geometry is invalid.
|
||||
|
||||
.. attribute:: GEOSGeometry.srid
|
||||
|
@ -530,8 +533,6 @@ corresponding to the SRID of the geometry or ``None``.
|
|||
|
||||
.. method:: GEOSGeometry.transform(ct, clone=False)
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
Transforms the geometry according to the given coordinate transformation paramter
|
||||
(``ct``), which may be an integer SRID, spatial reference WKT string,
|
||||
a PROJ.4 string, a :class:`~django.contrib.gis.gdal.SpatialReference` object, or a
|
||||
|
|
|
@ -134,8 +134,6 @@ your settings::
|
|||
GeoDjango tests
|
||||
===============
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
GeoDjango's test suite may be run in one of two ways, either by itself or
|
||||
with the rest of :ref:`Django's unit tests <running-unit-tests>`.
|
||||
|
||||
|
|
|
@ -267,8 +267,6 @@ Austria (``at``)
|
|||
Belgium (``be``)
|
||||
================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. class:: be.forms.BEPhoneNumberField
|
||||
|
||||
A form field that validates input as a Belgium phone number, with one of
|
||||
|
@ -658,11 +656,6 @@ Indonesia (``id``)
|
|||
|
||||
A ``Select`` widget that uses a list of Indonesian provinces as its choices.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
The province "Nanggroe Aceh Darussalam (NAD)" has been removed
|
||||
from the province list in favor of the new official designation
|
||||
"Aceh (ACE)".
|
||||
|
||||
.. class:: id.forms.IDPhoneNumberField
|
||||
|
||||
A form field that validates input as an Indonesian telephone number.
|
||||
|
|
|
@ -330,8 +330,6 @@ with a caching decorator -- you must name your sitemap view and pass
|
|||
Template customization
|
||||
======================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
If you wish to use a different template for each sitemap or sitemap index
|
||||
available on your site, you may specify it by passing a ``template_name``
|
||||
parameter to the ``sitemap`` and ``index`` views via the URLconf::
|
||||
|
|
|
@ -159,8 +159,6 @@ the :class:`~django.contrib.sites.models.Site` model's manager has a
|
|||
else:
|
||||
# Do something else.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
For code which relies on getting the current domain but cannot be certain
|
||||
that the sites framework will be installed for any given project, there is a
|
||||
utility function :func:`~django.contrib.sites.models.get_current_site` that
|
||||
|
@ -169,12 +167,10 @@ the sites framework is installed) or a RequestSite instance (if it is not).
|
|||
This allows loose coupling with the sites framework and provides a usable
|
||||
fallback for cases where it is not installed.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. function:: get_current_site(request)
|
||||
|
||||
Checks if contrib.sites is installed and returns either the current
|
||||
:class:`~django.contrib.sites.models.Site` object or a
|
||||
:class:`~django.contrib.sites.models.Site` object or a
|
||||
:class:`~django.contrib.sites.models.RequestSite` object based on
|
||||
the request.
|
||||
|
||||
|
@ -437,7 +433,7 @@ fallback when the database-backed sites framework is not available.
|
|||
|
||||
Sets the ``name`` and ``domain`` attributes to the value of
|
||||
:meth:`~django.http.HttpRequest.get_host`.
|
||||
|
||||
|
||||
|
||||
A :class:`~django.contrib.sites.models.RequestSite` object has a similar
|
||||
interface to a normal :class:`~django.contrib.sites.models.Site` object, except
|
||||
|
|
|
@ -5,8 +5,6 @@ The staticfiles app
|
|||
.. module:: django.contrib.staticfiles
|
||||
:synopsis: An app for handling static files.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
``django.contrib.staticfiles`` collects static files from each of your
|
||||
applications (and any other places you specify) into a single location that
|
||||
can easily be served in production.
|
||||
|
|
|
@ -176,8 +176,6 @@ records to dump. If you're using a :ref:`custom manager <custom-managers>` as
|
|||
the default manager and it filters some of the available records, not all of the
|
||||
objects will be dumped.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The :djadminopt:`--all` option may be provided to specify that
|
||||
``dumpdata`` should use Django's base manager, dumping records which
|
||||
might otherwise be filtered or modified by a custom manager.
|
||||
|
@ -195,18 +193,10 @@ easy for humans to read, so you can use the ``--indent`` option to
|
|||
pretty-print the output with a number of indentation spaces.
|
||||
|
||||
The :djadminopt:`--exclude` option may be provided to prevent specific
|
||||
applications from being dumped.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The :djadminopt:`--exclude` option may also be provided to prevent specific
|
||||
models (specified as in the form of ``appname.ModelName``) from being dumped.
|
||||
|
||||
In addition to specifying application names, you can provide a list of
|
||||
individual models, in the form of ``appname.Model``. If you specify a model
|
||||
name to ``dumpdata``, the dumped output will be restricted to that model,
|
||||
rather than the entire application. You can also mix application names and
|
||||
model names.
|
||||
applications or models (specified as in the form of ``appname.ModelName``) from
|
||||
being dumped. If you specify a model name to ``dumpdata``, the dumped output
|
||||
will be restricted to that model, rather than the entire application. You can
|
||||
also mix application names and model names.
|
||||
|
||||
The :djadminopt:`--database` option can be used to specify the database
|
||||
from which data will be dumped.
|
||||
|
@ -463,8 +453,6 @@ Use the ``--no-default-ignore`` option to disable the default values of
|
|||
|
||||
.. django-admin-option:: --no-wrap
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Use the ``--no-wrap`` option to disable breaking long message lines into
|
||||
several lines in language files.
|
||||
|
||||
|
@ -640,15 +628,11 @@ machines on your network. To make your development server viewable to other
|
|||
machines on the network, use its own IP address (e.g. ``192.168.2.1``) or
|
||||
``0.0.0.0`` or ``::`` (with IPv6 enabled).
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
You can provide an IPv6 address surrounded by brackets
|
||||
(e.g. ``[200a::1]:8000``). This will automatically enable IPv6 support.
|
||||
|
||||
A hostname containing ASCII-only characters can also be used.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
If the :doc:`staticfiles</ref/contrib/staticfiles>` contrib app is enabled
|
||||
(default in new projects) the :djadmin:`runserver` command will be overriden
|
||||
with an own :djadmin:`runserver<staticfiles-runserver>` command.
|
||||
|
@ -674,8 +658,6 @@ development server.
|
|||
|
||||
.. django-admin-option:: --ipv6, -6
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Use the ``--ipv6`` (or shorter ``-6``) option to tell Django to use IPv6 for
|
||||
the development server. This changes the default IP address from
|
||||
``127.0.0.1`` to ``::1``.
|
||||
|
@ -1113,8 +1095,6 @@ To run on 1.2.3.4:7000 with a ``test`` fixture::
|
|||
|
||||
django-admin.py testserver --addrport 1.2.3.4:7000 test
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The :djadminopt:`--noinput` option may be provided to suppress all user
|
||||
prompts.
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ Django provides two convenient ways to access the current storage class:
|
|||
.. function:: get_storage_class([import_path=None])
|
||||
|
||||
Returns a class or module which implements the storage API.
|
||||
|
||||
|
||||
When called without the ``import_path`` parameter ``get_storage_class``
|
||||
will return the current default storage system as defined by
|
||||
:setting:`DEFAULT_FILE_STORAGE`. If ``import_path`` is provided,
|
||||
|
@ -35,9 +35,9 @@ The FileSystemStorage Class
|
|||
basic file storage on a local filesystem. It inherits from
|
||||
:class:`~django.core.files.storage.Storage` and provides implementations
|
||||
for all the public methods thereof.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
The :class:`FileSystemStorage.delete` method will not raise
|
||||
raise an exception if the given file name does not exist.
|
||||
|
||||
|
@ -53,16 +53,12 @@ The Storage Class
|
|||
|
||||
.. method:: accessed_time(name)
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Returns a ``datetime`` object containing the last accessed time of the
|
||||
file. For storage systems that aren't able to return the last accessed
|
||||
time this will raise ``NotImplementedError`` instead.
|
||||
|
||||
.. method:: created_time(name)
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Returns a ``datetime`` object containing the creation time of the file.
|
||||
For storage systems that aren't able to return the creation time this
|
||||
will raise ``NotImplementedError`` instead.
|
||||
|
@ -100,8 +96,6 @@ The Storage Class
|
|||
|
||||
.. method:: modified_time(name)
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Returns a ``datetime`` object containing the last modified time. For
|
||||
storage systems that aren't able to return the last modified time, this
|
||||
will raise ``NotImplementedError`` instead.
|
||||
|
|
|
@ -658,8 +658,6 @@ those classes as an argument::
|
|||
|
||||
.. method:: BoundField.value()
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Use this method to render the raw value of this field as it would be rendered
|
||||
by a ``Widget``::
|
||||
|
||||
|
|
|
@ -704,8 +704,6 @@ For each field, we describe the default widget used if you don't specify
|
|||
``TypedMultipleChoiceField``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. class:: TypedMultipleChoiceField(**kwargs)
|
||||
|
||||
Just like a :class:`MultipleChoiceField`, except :class:`TypedMultipleChoiceField`
|
||||
|
|
|
@ -294,11 +294,6 @@ These widgets make use of the HTML elements ``input`` and ``textarea``.
|
|||
Determines whether the widget will have a value filled in when the
|
||||
form is re-displayed after a validation error (default is ``False``).
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
The default value for
|
||||
:attr:`~PasswordInput.render_value` was
|
||||
changed from ``True`` to ``False``
|
||||
|
||||
``HiddenInput``
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -532,8 +527,6 @@ File upload widgets
|
|||
|
||||
.. class:: ClearableFileInput
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
File upload input: ``<input type='file' ...>``, with an additional checkbox
|
||||
input to clear the field's value, if the field is not required and has
|
||||
initial data.
|
||||
|
|
|
@ -1023,8 +1023,6 @@ define the details of how the relation works.
|
|||
The field on the related object that the relation is to. By default, Django
|
||||
uses the primary key of the related object.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. attribute:: ForeignKey.on_delete
|
||||
|
||||
When an object referenced by a :class:`ForeignKey` is deleted, Django by
|
||||
|
@ -1081,6 +1079,9 @@ the model is related. This works exactly the same as it does for
|
|||
:class:`ForeignKey`, including all the options regarding :ref:`recursive
|
||||
<recursive-relationships>` and :ref:`lazy <lazy-relationships>` relationships.
|
||||
|
||||
Related objects can be added, removed, or created with the field's
|
||||
:class:`~django.db.models.fields.related.RelatedManager`.
|
||||
|
||||
Database Representation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -505,15 +505,8 @@ followed (optionally) by any output-affecting methods (such as ``values()``),
|
|||
but it doesn't really matter. This is your chance to really flaunt your
|
||||
individualism.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
The ``values()`` method previously did not return anything for
|
||||
:class:`~django.db.models.ManyToManyField` attributes and would raise an error
|
||||
if you tried to pass this type of field to it.
|
||||
|
||||
This restriction has been lifted, and you can now also refer to fields on
|
||||
related models with reverse relations through ``OneToOneField``, ``ForeignKey``
|
||||
and ``ManyToManyField`` attributes::
|
||||
You can also refer to fields on related models with reverse relations through
|
||||
``OneToOneField``, ``ForeignKey`` and ``ManyToManyField`` attributes::
|
||||
|
||||
Blog.objects.values('name', 'entry__headline')
|
||||
[{'name': 'My blog', 'entry__headline': 'An entry'},
|
||||
|
@ -1664,10 +1657,9 @@ For example::
|
|||
# This will delete all Blogs and all of their Entry objects.
|
||||
blogs.delete()
|
||||
|
||||
.. versionadded:: 1.3
|
||||
This cascade behavior is customizable via the
|
||||
:attr:`~django.db.models.ForeignKey.on_delete` argument to the
|
||||
:class:`~django.db.models.ForeignKey`.
|
||||
This cascade behavior is customizable via the
|
||||
:attr:`~django.db.models.ForeignKey.on_delete` argument to the
|
||||
:class:`~django.db.models.ForeignKey`.
|
||||
|
||||
The ``delete()`` method does a bulk delete and does not call any ``delete()``
|
||||
methods on your models. It does, however, emit the
|
||||
|
|
|
@ -42,8 +42,6 @@ All attributes should be considered read-only, unless stated otherwise below.
|
|||
data in different ways than conventional HTML forms: binary images,
|
||||
XML payload etc. For processing conventional form data, use ``HttpRequest.POST``.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
You can also read from an HttpRequest using a file-like interface. See
|
||||
:meth:`HttpRequest.read()`.
|
||||
|
||||
|
@ -305,8 +303,6 @@ Methods
|
|||
.. method:: HttpRequest.xreadlines()
|
||||
.. method:: HttpRequest.__iter__()
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Methods implementing a file-like interface for reading from an
|
||||
HttpRequest instance. This makes it possible to consume an incoming
|
||||
request in a streaming fashion. A common use-case would be to process a
|
||||
|
@ -509,9 +505,6 @@ In addition, ``QueryDict`` has the following methods:
|
|||
>>> q.urlencode()
|
||||
'a=2&b=3&b=5'
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
The ``safe`` parameter was added.
|
||||
|
||||
Optionally, urlencode can be passed characters which
|
||||
do not require encoding. For example::
|
||||
|
||||
|
@ -648,12 +641,6 @@ Methods
|
|||
|
||||
.. method:: HttpResponse.set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=True)
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
The possibility of specifying a ``datetime.datetime`` object in
|
||||
``expires``, and the auto-calculation of ``max_age`` in such case
|
||||
was added. The ``httponly`` argument was also added.
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
|
||||
The default value for httponly was changed from ``False`` to ``True``.
|
||||
|
|
|
@ -124,8 +124,6 @@ The model to use to represent a User. See :ref:`auth-custom-user`.
|
|||
CACHES
|
||||
------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Default::
|
||||
|
||||
{
|
||||
|
@ -166,12 +164,6 @@ backend class (i.e. ``mypackage.backends.whatever.WhateverCache``).
|
|||
Writing a whole new cache backend from scratch is left as an exercise
|
||||
to the reader; see the other backends for examples.
|
||||
|
||||
.. note::
|
||||
Prior to Django 1.3, you could use a URI based version of the backend
|
||||
name to reference the built-in cache backends (e.g., you could use
|
||||
``'db://tablename'`` to refer to the database backend). This format has
|
||||
been deprecated, and will be removed in Django 1.5.
|
||||
|
||||
.. setting:: CACHES-KEY_FUNCTION
|
||||
|
||||
KEY_FUNCTION
|
||||
|
@ -533,8 +525,6 @@ Only supported for the ``mysql`` backend (see the `MySQL manual`_ for details).
|
|||
TEST_DEPENDENCIES
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Default: ``['default']``, for all databases other than ``default``,
|
||||
which has no dependencies.
|
||||
|
||||
|
@ -1261,8 +1251,6 @@ the ``locale`` directory (i.e. ``'/path/to/locale'``).
|
|||
LOGGING
|
||||
-------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Default: A logging configuration dictionary.
|
||||
|
||||
A data structure containing configuration information. The contents of
|
||||
|
@ -1277,8 +1265,6 @@ email log handler; all other log messages are given to a NullHandler.
|
|||
LOGGING_CONFIG
|
||||
--------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Default: ``'django.utils.log.dictConfig'``
|
||||
|
||||
A path to a callable that will be used to configure logging in the
|
||||
|
@ -1370,13 +1356,11 @@ MEDIA_URL
|
|||
Default: ``''`` (Empty string)
|
||||
|
||||
URL that handles the media served from :setting:`MEDIA_ROOT`, used
|
||||
for :doc:`managing stored files </topics/files>`.
|
||||
for :doc:`managing stored files </topics/files>`. It must end in a slash if set
|
||||
to a non-empty value.
|
||||
|
||||
Example: ``"http://media.example.com/"``
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
It must end in a slash if set to a non-empty value.
|
||||
|
||||
MESSAGE_LEVEL
|
||||
-------------
|
||||
|
||||
|
@ -1895,10 +1879,6 @@ A tuple of callables that are used to populate the context in ``RequestContext``
|
|||
These callables take a request object as their argument and return a dictionary
|
||||
of items to be merged into the context.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
The ``django.core.context_processors.static`` context processor
|
||||
was added in this release.
|
||||
|
||||
.. versionadded:: 1.4
|
||||
The ``django.core.context_processors.tz`` context processor
|
||||
was added in this release.
|
||||
|
@ -2159,8 +2139,6 @@ See also :setting:`TIME_ZONE`, :setting:`USE_I18N` and :setting:`USE_L10N`.
|
|||
USE_X_FORWARDED_HOST
|
||||
--------------------
|
||||
|
||||
.. versionadded:: 1.3.1
|
||||
|
||||
Default: ``False``
|
||||
|
||||
A boolean that specifies whether to use the X-Forwarded-Host header in
|
||||
|
|
|
@ -46,7 +46,7 @@ pre_init
|
|||
|
||||
.. ^^^^^^^ this :module: hack keeps Sphinx from prepending the module.
|
||||
|
||||
Whenever you instantiate a Django model,, this signal is sent at the beginning
|
||||
Whenever you instantiate a Django model, this signal is sent at the beginning
|
||||
of the model's :meth:`~django.db.models.Model.__init__` method.
|
||||
|
||||
Arguments sent with this signal:
|
||||
|
@ -118,8 +118,6 @@ Arguments sent with this signal:
|
|||
records in the database as the database might not be in a
|
||||
consistent state yet.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
``using``
|
||||
The database alias being used.
|
||||
|
||||
|
@ -155,8 +153,6 @@ Arguments sent with this signal:
|
|||
records in the database as the database might not be in a
|
||||
consistent state yet.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
``using``
|
||||
The database alias being used.
|
||||
|
||||
|
@ -183,8 +179,6 @@ Arguments sent with this signal:
|
|||
``instance``
|
||||
The actual instance being deleted.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
``using``
|
||||
The database alias being used.
|
||||
|
||||
|
@ -209,8 +203,6 @@ Arguments sent with this signal:
|
|||
Note that the object will no longer be in the database, so be very
|
||||
careful what you do with this instance.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
``using``
|
||||
The database alias being used.
|
||||
|
||||
|
@ -271,8 +263,6 @@ Arguments sent with this signal:
|
|||
|
||||
For the ``pre_clear`` and ``post_clear`` actions, this is ``None``.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
``using``
|
||||
The database alias being used.
|
||||
|
||||
|
@ -287,13 +277,22 @@ like this::
|
|||
# ...
|
||||
toppings = models.ManyToManyField(Topping)
|
||||
|
||||
If we would do something like this:
|
||||
If we connected a handler like this::
|
||||
|
||||
def toppings_changed(sender, **kwargs):
|
||||
# Do something
|
||||
pass
|
||||
|
||||
m2m_changed.connect(toppings_changed, sender=Pizza.toppings.through)
|
||||
|
||||
and then did something like this::
|
||||
|
||||
>>> p = Pizza.object.create(...)
|
||||
>>> t = Topping.objects.create(...)
|
||||
>>> p.toppings.add(t)
|
||||
|
||||
the arguments sent to a :data:`m2m_changed` handler would be:
|
||||
the arguments sent to a :data:`m2m_changed` handler (``topppings_changed`` in
|
||||
the example above) would be:
|
||||
|
||||
============== ============================================================
|
||||
Argument Value
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
TemplateResponse and SimpleTemplateResponse
|
||||
===========================================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. module:: django.template.response
|
||||
:synopsis: Classes dealing with lazy-rendered HTTP responses.
|
||||
|
||||
|
|
|
@ -160,11 +160,6 @@ it. Example::
|
|||
>>> t.render(Context({"person": PersonClass2}))
|
||||
"My name is Samantha."
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Previously, only variables that originated with an attribute lookup would
|
||||
be called by the template system. This change was made for consistency
|
||||
across lookup types.
|
||||
|
||||
Callable variables are slightly more complex than variables which only require
|
||||
straight lookups. Here are some things to keep in mind:
|
||||
|
||||
|
@ -448,11 +443,6 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every
|
|||
``django.contrib.auth.context_processors.PermWrapper``, representing the
|
||||
permissions that the currently logged-in user has.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Prior to version 1.3, ``PermWrapper`` was located in
|
||||
``django.contrib.auth.context_processors``.
|
||||
|
||||
|
||||
django.core.context_processors.debug
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -491,8 +481,6 @@ django.core.context_processors.static
|
|||
|
||||
.. function:: django.core.context_processors.static
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every
|
||||
``RequestContext`` will contain a variable ``STATIC_URL``, providing the
|
||||
value of the :setting:`STATIC_URL` setting.
|
||||
|
|
|
@ -156,8 +156,6 @@ In this syntax, each value gets interpreted as a literal string, and there's no
|
|||
way to specify variable values. Or literal commas. Or spaces. Did we mention
|
||||
you shouldn't use this syntax in any new projects?
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
By default, when you use the ``as`` keyword with the cycle tag, the
|
||||
usage of ``{% cycle %}`` that declares the cycle will itself output
|
||||
the first value in the cycle. This could be a problem if you want to
|
||||
|
@ -676,9 +674,6 @@ including it. This example produces the output ``"Hello, John"``:
|
|||
|
||||
{{ greeting }}, {{ person|default:"friend" }}!
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Additional context and exclusive context.
|
||||
|
||||
You can pass additional context to the template using keyword arguments::
|
||||
|
||||
{% include "name_snippet.html" with person="Jane" greeting="Hello" %}
|
||||
|
@ -710,8 +705,6 @@ registered in ``somelibrary`` and ``otherlibrary`` located in package
|
|||
|
||||
{% load somelibrary package.otherlibrary %}
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
You can also selectively load individual filters or tags from a library, using
|
||||
the ``from`` argument. In this example, the template tags/filters named ``foo``
|
||||
and ``bar`` will be loaded from ``somelibrary``::
|
||||
|
@ -1076,9 +1069,6 @@ which is rounded up to 88).
|
|||
with
|
||||
^^^^
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
New keyword argument format and multiple variable assignments.
|
||||
|
||||
Caches a complex variable under a simpler name. This is useful when accessing
|
||||
an "expensive" method (e.g., one that hits the database) multiple times.
|
||||
|
||||
|
@ -1261,7 +1251,7 @@ S English ordinal suffix for day of the ``'st'``, ``'nd'``,
|
|||
month, 2 characters.
|
||||
t Number of days in the given month. ``28`` to ``31``
|
||||
T Time zone of this machine. ``'EST'``, ``'MDT'``
|
||||
u Microseconds. ``0`` to ``999999``
|
||||
u Microseconds. ``000000`` to ``999999``
|
||||
U Seconds since the Unix Epoch
|
||||
(January 1 1970 00:00:00 UTC).
|
||||
w Day of the week, digits without ``'0'`` (Sunday) to ``'6'`` (Saturday)
|
||||
|
@ -2126,8 +2116,6 @@ For example::
|
|||
If ``value`` is ``"http://www.example.org/foo?a=b&c=d"``, the output will be
|
||||
``"http%3A//www.example.org/foo%3Fa%3Db%26c%3Dd"``.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
An optional argument containing the characters which should not be escaped can
|
||||
be provided.
|
||||
|
||||
|
|
|
@ -133,6 +133,8 @@ Django 1.5 also includes several smaller improvements worth noting:
|
|||
* The :ref:`receiver <connecting-receiver-functions>` decorator is now able to
|
||||
connect to more than one signal by supplying a list of signals.
|
||||
|
||||
* In the admin, you can now filter users by groups which they are members of.
|
||||
|
||||
* :meth:`QuerySet.bulk_create()
|
||||
<django.db.models.query.QuerySet.bulk_create>` now has a batch_size
|
||||
argument. By default the batch_size is unlimited except for SQLite where
|
||||
|
@ -167,6 +169,21 @@ year|date:"Y" }}``.
|
|||
``next_year`` and ``previous_year`` were also added in the context. They are
|
||||
calculated according to ``allow_empty`` and ``allow_future``.
|
||||
|
||||
Context in year and month archive class-based views
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:class:`~django.views.generic.dates.YearArchiveView` and
|
||||
:class:`~django.views.generic.dates.MonthArchiveView` were documented to
|
||||
provide a ``date_list`` sorted in ascending order in the context, like their
|
||||
function-based predecessors, but it actually was in descending order. In 1.5,
|
||||
the documented order was restored. You may want to add (or remove) the
|
||||
``reversed`` keyword when you're iterating on ``date_list`` in a template::
|
||||
|
||||
{% for date in date_list reversed %}
|
||||
|
||||
:class:`~django.views.generic.dates.ArchiveIndexView` still provides a
|
||||
``date_list`` in descending order.
|
||||
|
||||
Context in TemplateView
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -362,6 +379,11 @@ Miscellaneous
|
|||
function at :func:`django.utils.text.slugify`. Similarly, ``remove_tags`` is
|
||||
available at :func:`django.utils.html.remove_tags`.
|
||||
|
||||
* Uploaded files are no longer created as executable by default. If you need
|
||||
them to be executeable change :setting:`FILE_UPLOAD_PERMISSIONS` to your
|
||||
needs. The new default value is `0666` (octal) and the current umask value
|
||||
is first masked out.
|
||||
|
||||
Features deprecated in 1.5
|
||||
==========================
|
||||
|
||||
|
|
|
@ -872,8 +872,6 @@ How to log a user out
|
|||
Login and logout signals
|
||||
------------------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The auth framework uses two :doc:`signals </topics/signals>` that can be used
|
||||
for notification when a user logs in or out.
|
||||
|
||||
|
@ -972,8 +970,6 @@ The login_required decorator
|
|||
context variable which stores the redirect path will use the value of
|
||||
``redirect_field_name`` as its key rather than ``"next"`` (the default).
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
:func:`~django.contrib.auth.decorators.login_required` also takes an
|
||||
optional ``login_url`` parameter. Example::
|
||||
|
||||
|
@ -1201,9 +1197,6 @@ includes a few other useful built-in views located in
|
|||
that can be used to reset the password, and sending that link to the
|
||||
user's registered email address.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
The ``from_email`` argument was added.
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
Users flagged with an unusable password (see
|
||||
:meth:`~django.contrib.auth.models.User.set_unusable_password()`
|
||||
|
@ -1684,10 +1677,6 @@ The currently logged-in user's permissions are stored in the template variable
|
|||
:class:`django.contrib.auth.context_processors.PermWrapper`, which is a
|
||||
template-friendly proxy of permissions.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Prior to version 1.3, ``PermWrapper`` was located in
|
||||
``django.core.context_processors``.
|
||||
|
||||
In the ``{{ perms }}`` object, single-attribute lookup is a proxy to
|
||||
:meth:`User.has_module_perms <django.contrib.auth.models.User.has_module_perms>`.
|
||||
This example would display ``True`` if the logged-in user had any permissions
|
||||
|
@ -2263,8 +2252,6 @@ for example, to control anonymous access.
|
|||
Authorization for inactive users
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
An inactive user is a one that is authenticated but has its attribute
|
||||
``is_active`` set to ``False``. However this does not mean they are not
|
||||
authorized to do anything. For example they are allowed to activate their
|
||||
|
|
|
@ -51,13 +51,6 @@ Your cache preference goes in the :setting:`CACHES` setting in your
|
|||
settings file. Here's an explanation of all available values for
|
||||
:setting:`CACHES`.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
The settings used to configure caching changed in Django 1.3. In
|
||||
Django 1.2 and earlier, you used a single string-based
|
||||
:setting:`CACHE_BACKEND` setting to configure caches. This has
|
||||
been replaced with the new dictionary-based :setting:`CACHES`
|
||||
setting.
|
||||
|
||||
.. _memcached:
|
||||
|
||||
Memcached
|
||||
|
@ -83,9 +76,6 @@ two most common are `python-memcached`_ and `pylibmc`_.
|
|||
.. _`python-memcached`: ftp://ftp.tummy.com/pub/python-memcached/
|
||||
.. _`pylibmc`: http://sendapatch.se/projects/pylibmc/
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Support for ``pylibmc`` was added.
|
||||
|
||||
To use Memcached with Django:
|
||||
|
||||
* Set :setting:`BACKEND <CACHES-BACKEND>` to
|
||||
|
@ -673,12 +663,27 @@ dictionaries, lists of model objects, and so forth. (Most common Python objects
|
|||
can be pickled; refer to the Python documentation for more information about
|
||||
pickling.)
|
||||
|
||||
Accessing the cache
|
||||
-------------------
|
||||
|
||||
The cache module, ``django.core.cache``, has a ``cache`` object that's
|
||||
automatically created from the ``'default'`` entry in the :setting:`CACHES`
|
||||
setting::
|
||||
|
||||
>>> from django.core.cache import cache
|
||||
|
||||
If you have multiple caches defined in :setting:`CACHES`, then you can use
|
||||
:func:`django.core.cache.get_cache` to retrieve a cache object for any key::
|
||||
|
||||
>>> from django.core.cache import get_cache
|
||||
>>> cache = get_cache('alternate')
|
||||
|
||||
If the named key does not exist, :exc:`InvalidCacheBackendError` will be raised.
|
||||
|
||||
|
||||
Basic usage
|
||||
-----------
|
||||
|
||||
The basic interface is ``set(key, value, timeout)`` and ``get(key)``::
|
||||
|
||||
>>> cache.set('my_key', 'hello, world!', 30)
|
||||
|
@ -686,7 +691,7 @@ The basic interface is ``set(key, value, timeout)`` and ``get(key)``::
|
|||
'hello, world!'
|
||||
|
||||
The ``timeout`` argument is optional and defaults to the ``timeout``
|
||||
argument of the ``'default'`` backend in :setting:`CACHES` setting
|
||||
argument of the appropriate backend in the :setting:`CACHES` setting
|
||||
(explained above). It's the number of seconds the value should be stored
|
||||
in the cache.
|
||||
|
||||
|
@ -785,8 +790,6 @@ nonexistent cache key.::
|
|||
Cache key prefixing
|
||||
-------------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
If you are sharing a cache instance between servers, or between your
|
||||
production and development environments, it's possible for data cached
|
||||
by one server to be used by another server. If the format of cached
|
||||
|
@ -807,8 +810,6 @@ collisions in cache values.
|
|||
Cache versioning
|
||||
----------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
When you change running code that uses cached values, you may need to
|
||||
purge any existing cached values. The easiest way to do this is to
|
||||
flush the entire cache, but this can lead to the loss of cache values
|
||||
|
@ -856,8 +857,6 @@ keys unaffected. Continuing our previous example::
|
|||
Cache key transformation
|
||||
------------------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
As described in the previous two sections, the cache key provided by a
|
||||
user is not used verbatim -- it is combined with the cache prefix and
|
||||
key version to provide a final cache key. By default, the three parts
|
||||
|
@ -878,8 +877,6 @@ be used instead of the default key combining function.
|
|||
Cache key warnings
|
||||
------------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Memcached, the most commonly-used production cache backend, does not allow
|
||||
cache keys longer than 250 characters or containing whitespace or control
|
||||
characters, and using such keys will cause an exception. To encourage
|
||||
|
@ -966,10 +963,6 @@ mechanism should take into account when building its cache key. For example, if
|
|||
the contents of a Web page depend on a user's language preference, the page is
|
||||
said to "vary on language."
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
In Django 1.3 the full request path -- including the query -- is used
|
||||
to create the cache keys, instead of only the path component in Django 1.2.
|
||||
|
||||
By default, Django's cache system creates its cache keys using the requested
|
||||
path and query -- e.g., ``"/stories/2005/?order_by=author"``. This means every
|
||||
request to that URL will use the same cached version, regardless of user-agent
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
Class-based generic views
|
||||
=========================
|
||||
|
||||
.. note::
|
||||
Prior to Django 1.3, generic views were implemented as functions. The
|
||||
function-based implementation has been removed in favor of the
|
||||
class-based approach described here.
|
||||
|
||||
Writing Web applications can be monotonous, because we repeat certain patterns
|
||||
again and again. Django tries to take away some of that monotony at the model
|
||||
and template layers, but Web developers also experience this boredom at the view
|
||||
|
|
|
@ -203,3 +203,43 @@ Note that you'll need to :ref:`decorate this
|
|||
view<decorating-class-based-views>` using
|
||||
:func:`~django.contrib.auth.decorators.login_required`, or
|
||||
alternatively handle unauthorised users in the :meth:`form_valid()`.
|
||||
|
||||
AJAX example
|
||||
------------
|
||||
|
||||
Here is a simple example showing how you might go about implementing a form that
|
||||
works for AJAX requests as well as 'normal' form POSTs::
|
||||
|
||||
import json
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.views.generic.edit import CreateView
|
||||
from django.views.generic.detail import SingleObjectTemplateResponseMixin
|
||||
|
||||
class AjaxableResponseMixin(object):
|
||||
"""
|
||||
Mixin to add AJAX support to a form.
|
||||
Must be used with an object-based FormView (e.g. CreateView)
|
||||
"""
|
||||
def render_to_json_response(self, context, **response_kwargs):
|
||||
data = json.dumps(context)
|
||||
response_kwargs['content_type'] = 'application/json'
|
||||
return HttpResponse(data, **response_kwargs)
|
||||
|
||||
def form_invalid(self, form):
|
||||
if self.request.is_ajax():
|
||||
return self.render_to_json_response(form.errors, status=400)
|
||||
else:
|
||||
return super(AjaxableResponseMixin, self).form_invalid(form)
|
||||
|
||||
def form_valid(self, form):
|
||||
if self.request.is_ajax():
|
||||
data = {
|
||||
'pk': form.instance.pk,
|
||||
}
|
||||
return self.render_to_json_response(data)
|
||||
else:
|
||||
return super(AjaxableResponseMixin, self).form_valid(form)
|
||||
|
||||
class AuthorCreate(AjaxableResponseMixin, CreateView):
|
||||
model = Author
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
Class-based views
|
||||
=================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
A view is a callable which takes a request and returns a
|
||||
response. This can be more than just a function, and Django provides
|
||||
an example of some classes which can be used as views. These allow you
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
Using mixins with class-based views
|
||||
===================================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. caution::
|
||||
|
||||
This is an advanced topic. A working knowledge of :doc:`Django's
|
||||
|
|
|
@ -633,8 +633,6 @@ issue the query::
|
|||
|
||||
>>> Entry.objects.filter(authors__name=F('blog__name'))
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
For date and date/time fields, you can add or subtract a
|
||||
:class:`~datetime.timedelta` object. The following would return all entries
|
||||
that were modified more than 3 days after they were published::
|
||||
|
@ -876,7 +874,6 @@ it. For example::
|
|||
# This will delete the Blog and all of its Entry objects.
|
||||
b.delete()
|
||||
|
||||
.. versionadded:: 1.3
|
||||
This cascade behavior is customizable via the
|
||||
:attr:`~django.db.models.ForeignKey.on_delete` argument to the
|
||||
:class:`~django.db.models.ForeignKey`.
|
||||
|
|
|
@ -242,7 +242,7 @@ By default, the Python DB API will return results without their field
|
|||
names, which means you end up with a ``list`` of values, rather than a
|
||||
``dict``. At a small performance cost, you can return results as a
|
||||
``dict`` by using something like this::
|
||||
|
||||
|
||||
def dictfetchall(cursor):
|
||||
"Returns all rows from a cursor as a dict"
|
||||
desc = cursor.description
|
||||
|
@ -256,7 +256,7 @@ Here is an example of the difference between the two::
|
|||
>>> cursor.execute("SELECT id, parent_id from test LIMIT 2");
|
||||
>>> cursor.fetchall()
|
||||
((54360982L, None), (54360880L, None))
|
||||
|
||||
|
||||
>>> cursor.execute("SELECT id, parent_id from test LIMIT 2");
|
||||
>>> dictfetchall(cursor)
|
||||
[{'parent_id': None, 'id': 54360982L}, {'parent_id': None, 'id': 54360880L}]
|
||||
|
@ -273,11 +273,6 @@ transaction containing those calls is closed correctly. See :ref:`the
|
|||
notes on the requirements of Django's transaction handling
|
||||
<topics-db-transactions-requirements>` for more details.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
Prior to Django 1.3, it was necessary to manually mark a transaction
|
||||
as dirty using ``transaction.set_dirty()`` when using raw SQL calls.
|
||||
|
||||
Connections and cursors
|
||||
-----------------------
|
||||
|
||||
|
|
|
@ -66,9 +66,6 @@ database cursor (which is mapped to its own database connection internally).
|
|||
Controlling transaction management in views
|
||||
===========================================
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Transaction management context managers are new in Django 1.3.
|
||||
|
||||
For most people, implicit request-based transactions work wonderfully. However,
|
||||
if you need more fine-grained control over how transactions are managed, you can
|
||||
use a set of functions in ``django.db.transaction`` to control transactions on a
|
||||
|
@ -195,8 +192,6 @@ managers, too.
|
|||
Requirements for transaction handling
|
||||
=====================================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Django requires that every transaction that is opened is closed before
|
||||
the completion of a request. If you are using :func:`autocommit` (the
|
||||
default commit mode) or :func:`commit_on_success`, this will be done
|
||||
|
|
|
@ -119,8 +119,6 @@ The "From:" header of the email will be the value of the
|
|||
|
||||
This method exists for convenience and readability.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
If ``html_message`` is provided, the resulting email will be a
|
||||
:mimetype:`multipart/alternative` email with ``message`` as the
|
||||
:mimetype:`text/plain` content type and ``html_message`` as the
|
||||
|
@ -236,9 +234,6 @@ following parameters (in the given order, if positional arguments are used).
|
|||
All parameters are optional and can be set at any time prior to calling the
|
||||
``send()`` method.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
The ``cc`` argument was added.
|
||||
|
||||
* ``subject``: The subject line of the email.
|
||||
|
||||
* ``body``: The body text. This should be a plain text message.
|
||||
|
|
|
@ -35,19 +35,9 @@ display two blank forms::
|
|||
|
||||
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
Prior to Django 1.3, formset instances were not iterable. To render
|
||||
the formset you iterated over the ``forms`` attribute::
|
||||
|
||||
>>> formset = ArticleFormSet()
|
||||
>>> for form in formset.forms:
|
||||
... print(form.as_table())
|
||||
|
||||
Iterating over ``formset.forms`` will render the forms in the order
|
||||
they were created. The default formset iterator also renders the forms
|
||||
in this order, but you can change this order by providing an alternate
|
||||
implementation for the :meth:`__iter__()` method.
|
||||
Iterating over the ``formset`` will render the forms in the order they were
|
||||
created. You can change this order by providing an alternate implementation for
|
||||
the :meth:`__iter__()` method.
|
||||
|
||||
Formsets can also be indexed into, which returns the corresponding form. If you
|
||||
override ``__iter__``, you will need to also override ``__getitem__`` to have
|
||||
|
|
|
@ -195,8 +195,6 @@ return values for dynamic media properties.
|
|||
Paths in media definitions
|
||||
--------------------------
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
Paths used to specify media can be either relative or absolute. If a path
|
||||
starts with ``/``, ``http://`` or ``https://``, it will be interpreted as an
|
||||
absolute path, and left as-is. All other paths will be prepended with the value
|
||||
|
|
|
@ -117,8 +117,6 @@ middleware is always called on every response.
|
|||
``process_template_response``
|
||||
-----------------------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. method:: process_template_response(self, request, response)
|
||||
|
||||
``request`` is an :class:`~django.http.HttpRequest` object. ``response`` is a
|
||||
|
|
|
@ -17,8 +17,6 @@ introduce controlled coupling for convenience's sake.
|
|||
|
||||
.. function:: render(request, template_name[, dictionary][, context_instance][, content_type][, status][, current_app])
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Combines a given template with a given context dictionary and returns an
|
||||
:class:`~django.http.HttpResponse` object with that rendered text.
|
||||
|
||||
|
|
|
@ -55,7 +55,8 @@ algorithm the system follows to determine which Python code to execute:
|
|||
one that matches the requested URL.
|
||||
|
||||
4. Once one of the regexes matches, Django imports and calls the given
|
||||
view, which is a simple Python function. The view gets passed an
|
||||
view, which is a simple Python function (or a :doc:`class based view
|
||||
</topics/class-based-views/index>`). The view gets passed an
|
||||
:class:`~django.http.HttpRequest` as its first argument and any values
|
||||
captured in the regex as remaining arguments.
|
||||
|
||||
|
@ -673,6 +674,15 @@ The style you use is up to you.
|
|||
Note that if you use this technique -- passing objects rather than strings --
|
||||
the view prefix (as explained in "The view prefix" above) will have no effect.
|
||||
|
||||
Note that :doc:`class based views</topics/class-based-views/index>` must be
|
||||
imported::
|
||||
|
||||
from mysite.views import ClassBasedView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^myview/$', ClassBasedView.as_view()),
|
||||
)
|
||||
|
||||
.. _naming-url-patterns:
|
||||
|
||||
Naming URL patterns
|
||||
|
@ -970,13 +980,6 @@ A :class:`ResolverMatch` object can also be assigned to a triple::
|
|||
|
||||
func, args, kwargs = resolve('/some/path/')
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Triple-assignment exists for backwards-compatibility. Prior to
|
||||
Django 1.3, :func:`~django.core.urlresolvers.resolve` returned a
|
||||
triple containing (view function, arguments, keyword arguments);
|
||||
the :class:`ResolverMatch` object (as well as the namespace and pattern
|
||||
information it provides) is not available in earlier Django releases.
|
||||
|
||||
One possible use of :func:`~django.core.urlresolvers.resolve` would be to test
|
||||
whether a view would raise a ``Http404`` error before redirecting to it::
|
||||
|
||||
|
|
|
@ -80,8 +80,6 @@ Template tags
|
|||
localize
|
||||
~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Enables or disables localization of template variables in the
|
||||
contained block.
|
||||
|
||||
|
@ -116,8 +114,6 @@ Template filters
|
|||
localize
|
||||
~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Forces localization of a single value.
|
||||
|
||||
For example::
|
||||
|
@ -136,8 +132,6 @@ tag.
|
|||
unlocalize
|
||||
~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Forces a single value to be printed without localization.
|
||||
|
||||
For example::
|
||||
|
|
|
@ -134,8 +134,6 @@ translations wouldn't be able to reorder placeholder text.
|
|||
Comments for translators
|
||||
------------------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
If you would like to give translators hints about a translatable string, you
|
||||
can add a comment prefixed with the ``Translators`` keyword on the line
|
||||
preceding the string, e.g.::
|
||||
|
@ -255,8 +253,6 @@ cardinality of the elements at play.
|
|||
Contextual markers
|
||||
------------------
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Sometimes words have several meanings, such as ``"May"`` in English, which
|
||||
refers to a month name and to a verb. To enable translators to translate
|
||||
these words correctly in different contexts, you can use the
|
||||
|
@ -436,8 +432,6 @@ Localized names of languages
|
|||
|
||||
.. function:: get_language_info
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The ``get_language_info()`` function provides detailed information about
|
||||
languages::
|
||||
|
||||
|
@ -535,9 +529,6 @@ using the ``context`` keyword:
|
|||
``blocktrans`` template tag
|
||||
---------------------------
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
New keyword argument format.
|
||||
|
||||
Contrarily to the :ttag:`trans` tag, the ``blocktrans`` tag allows you to mark
|
||||
complex sentences consisting of literals and variable content for translation
|
||||
by making use of placeholders::
|
||||
|
@ -664,8 +655,6 @@ string, so they don't need to be aware of translations.
|
|||
translator might translate the string ``"yes,no"`` as ``"ja,nein"``
|
||||
(keeping the comma intact).
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
You can also retrieve information about any of the available languages using
|
||||
provided template tags and filters. To get information about a single language,
|
||||
use the ``{% get_language_info %}`` tag::
|
||||
|
@ -787,10 +776,6 @@ directories listed in :setting:`LOCALE_PATHS` have the highest precedence with
|
|||
the ones appearing first having higher precedence than the ones appearing
|
||||
later.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Directories listed in :setting:`LOCALE_PATHS` weren't included in the
|
||||
lookup algorithm until version 1.3.
|
||||
|
||||
Using the JavaScript translation catalog
|
||||
----------------------------------------
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
Logging
|
||||
=======
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. module:: django.utils.log
|
||||
:synopsis: Logging tools for Django applications
|
||||
|
||||
|
|
|
@ -132,10 +132,6 @@ Now, our ``my_callback`` function will be called each time a request finishes.
|
|||
Note that ``receiver`` can also take a list of signals to connect a function
|
||||
to.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The ``receiver`` decorator was added in Django 1.3.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
|
||||
The ability to pass a list of signals was added.
|
||||
|
|
|
@ -73,8 +73,6 @@ module defines tests in class-based approach.
|
|||
|
||||
.. admonition:: unittest2
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
Python 2.7 introduced some major changes to the unittest library,
|
||||
adding some extremely useful features. To ensure that every Django
|
||||
project can benefit from these new features, Django ships with a
|
||||
|
@ -436,8 +434,6 @@ two databases.
|
|||
Controlling creation order for test databases
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
By default, Django will always create the ``default`` database first.
|
||||
However, no guarantees are made on the creation order of any other
|
||||
databases in your test setup.
|
||||
|
@ -512,6 +508,13 @@ file, all Django tests run with :setting:`DEBUG`\=False. This is to ensure that
|
|||
the observed output of your code matches what will be seen in a production
|
||||
setting.
|
||||
|
||||
Caches are not cleared after each test, and running "manage.py test fooapp" can
|
||||
insert data from the tests into the cache of a live system if you run your
|
||||
tests in production because, unlike databases, a separate "test cache" is not
|
||||
used. This behavior `may change`_ in the future.
|
||||
|
||||
.. _may change: https://code.djangoproject.com/ticket/11505
|
||||
|
||||
Understanding the test output
|
||||
-----------------------------
|
||||
|
||||
|
@ -1001,8 +1004,6 @@ Specifically, a ``Response`` object has the following attributes:
|
|||
The HTTP status of the response, as an integer. See
|
||||
:rfc:`2616#section-10` for a full list of HTTP status codes.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. attribute:: templates
|
||||
|
||||
A list of ``Template`` instances used to render the final content, in
|
||||
|
@ -1089,8 +1090,6 @@ The request factory
|
|||
|
||||
.. class:: RequestFactory
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The :class:`~django.test.client.RequestFactory` shares the same API as
|
||||
the test client. However, instead of behaving like a browser, the
|
||||
RequestFactory provides a way to generate a request instance that can
|
||||
|
@ -1327,8 +1326,6 @@ This means, instead of instantiating a ``Client`` in each test::
|
|||
Customizing the test client
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. attribute:: TestCase.client_class
|
||||
|
||||
If you want to use a different ``Client`` class (for example, a subclass
|
||||
|
@ -1708,8 +1705,6 @@ your test suite.
|
|||
|
||||
.. method:: TestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True)
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Asserts that a queryset ``qs`` returns a particular list of values ``values``.
|
||||
|
||||
The comparison of the contents of ``qs`` and ``values`` is performed using
|
||||
|
@ -1730,8 +1725,6 @@ your test suite.
|
|||
|
||||
.. method:: TestCase.assertNumQueries(num, func, *args, **kwargs)
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Asserts that when ``func`` is called with ``*args`` and ``**kwargs`` that
|
||||
``num`` database queries are executed.
|
||||
|
||||
|
@ -1854,8 +1847,6 @@ Skipping tests
|
|||
|
||||
.. currentmodule:: django.test
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The unittest library provides the :func:`@skipIf <unittest.skipIf>` and
|
||||
:func:`@skipUnless <unittest.skipUnless>` decorators to allow you to skip tests
|
||||
if you know ahead of time that those tests are going to fail under certain
|
||||
|
|
|
@ -52,6 +52,9 @@ class SimpleItem(models.Model):
|
|||
class Feature(models.Model):
|
||||
item = models.ForeignKey(SimpleItem)
|
||||
|
||||
class SpecialFeature(models.Model):
|
||||
feature = models.ForeignKey(Feature)
|
||||
|
||||
class ItemAndSimpleItem(models.Model):
|
||||
item = models.ForeignKey(Item)
|
||||
simple = models.ForeignKey(SimpleItem)
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.db.models.loading import cache
|
|||
from django.test import TestCase
|
||||
|
||||
from .models import (ResolveThis, Item, RelatedItem, Child, Leaf, Proxy,
|
||||
SimpleItem, Feature, ItemAndSimpleItem)
|
||||
SimpleItem, Feature, ItemAndSimpleItem, SpecialFeature)
|
||||
|
||||
|
||||
class DeferRegressionTest(TestCase):
|
||||
|
@ -115,6 +115,7 @@ class DeferRegressionTest(TestCase):
|
|||
RelatedItem,
|
||||
ResolveThis,
|
||||
SimpleItem,
|
||||
SpecialFeature,
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -152,6 +153,7 @@ class DeferRegressionTest(TestCase):
|
|||
"RelatedItem_Deferred_item_id",
|
||||
"ResolveThis",
|
||||
"SimpleItem",
|
||||
"SpecialFeature",
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -197,6 +199,18 @@ class DeferRegressionTest(TestCase):
|
|||
self.assertEqual(obj.item, item2)
|
||||
self.assertEqual(obj.item_id, item2.id)
|
||||
|
||||
def test_only_with_select_related(self):
|
||||
# Test for #17485.
|
||||
item = SimpleItem.objects.create(name='first', value=47)
|
||||
feature = Feature.objects.create(item=item)
|
||||
SpecialFeature.objects.create(feature=feature)
|
||||
|
||||
qs = Feature.objects.only('item__name').select_related('item')
|
||||
self.assertEqual(len(qs), 1)
|
||||
|
||||
qs = SpecialFeature.objects.only('feature__item__name').select_related('feature__item')
|
||||
self.assertEqual(len(qs), 1)
|
||||
|
||||
def test_deferred_class_factory(self):
|
||||
from django.db.models.query_utils import deferred_class_factory
|
||||
new_class = deferred_class_factory(Item,
|
||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import absolute_import, unicode_literals
|
|||
import errno
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
@ -23,6 +24,7 @@ from django.core.files.uploadedfile import UploadedFile
|
|||
from django.test import SimpleTestCase
|
||||
from django.utils import six
|
||||
from django.utils import unittest
|
||||
from django.test.utils import override_settings
|
||||
from ..servers.tests import LiveServerBase
|
||||
|
||||
# Try to import PIL in either of the two ways it can end up installed.
|
||||
|
@ -433,22 +435,29 @@ class FileSaveRaceConditionTest(unittest.TestCase):
|
|||
self.storage.delete('conflict')
|
||||
self.storage.delete('conflict_1')
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith('win'), "Windows only partially supports umasks and chmod.")
|
||||
class FileStoragePermissions(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.old_perms = settings.FILE_UPLOAD_PERMISSIONS
|
||||
settings.FILE_UPLOAD_PERMISSIONS = 0o666
|
||||
self.umask = 0o027
|
||||
self.old_umask = os.umask(self.umask)
|
||||
self.storage_dir = tempfile.mkdtemp()
|
||||
self.storage = FileSystemStorage(self.storage_dir)
|
||||
|
||||
def tearDown(self):
|
||||
settings.FILE_UPLOAD_PERMISSIONS = self.old_perms
|
||||
shutil.rmtree(self.storage_dir)
|
||||
os.umask(self.old_umask)
|
||||
|
||||
@override_settings(FILE_UPLOAD_PERMISSIONS=0o654)
|
||||
def test_file_upload_permissions(self):
|
||||
name = self.storage.save("the_file", ContentFile("data"))
|
||||
actual_mode = os.stat(self.storage.path(name))[0] & 0o777
|
||||
self.assertEqual(actual_mode, 0o666)
|
||||
self.assertEqual(actual_mode, 0o654)
|
||||
|
||||
@override_settings(FILE_UPLOAD_PERMISSIONS=None)
|
||||
def test_file_upload_default_permissions(self):
|
||||
fname = self.storage.save("some_file", ContentFile("data"))
|
||||
mode = os.stat(self.storage.path(fname))[0] & 0o777
|
||||
self.assertEqual(mode, 0o666 & ~self.umask)
|
||||
|
||||
class FileStoragePathParsing(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -23,29 +23,30 @@ requires_tz_support = skipUnless(TZ_SUPPORT,
|
|||
"time zone, but your operating system isn't able to do that.")
|
||||
|
||||
|
||||
def _make_books(n, base_date):
|
||||
for i in range(n):
|
||||
b = Book.objects.create(
|
||||
name='Book %d' % i,
|
||||
slug='book-%d' % i,
|
||||
pages=100+i,
|
||||
pubdate=base_date - datetime.timedelta(days=i))
|
||||
|
||||
class ArchiveIndexViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
urls = 'regressiontests.generic_views.urls'
|
||||
|
||||
def _make_books(self, n, base_date):
|
||||
for i in range(n):
|
||||
b = Book.objects.create(
|
||||
name='Book %d' % i,
|
||||
slug='book-%d' % i,
|
||||
pages=100+i,
|
||||
pubdate=base_date - datetime.timedelta(days=1))
|
||||
|
||||
def test_archive_view(self):
|
||||
res = self.client.get('/dates/books/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
||||
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
||||
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
||||
|
||||
def test_archive_view_context_object_name(self):
|
||||
res = self.client.get('/dates/books/context_object_name/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
||||
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||
self.assertEqual(list(res.context['thingies']), list(Book.objects.all()))
|
||||
self.assertFalse('latest' in res.context)
|
||||
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
||||
|
@ -65,14 +66,14 @@ class ArchiveIndexViewTests(TestCase):
|
|||
def test_archive_view_template(self):
|
||||
res = self.client.get('/dates/books/template_name/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
||||
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
||||
self.assertTemplateUsed(res, 'generic_views/list.html')
|
||||
|
||||
def test_archive_view_template_suffix(self):
|
||||
res = self.client.get('/dates/books/template_name_suffix/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
||||
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||
self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
|
||||
self.assertTemplateUsed(res, 'generic_views/book_detail.html')
|
||||
|
||||
|
@ -82,13 +83,13 @@ class ArchiveIndexViewTests(TestCase):
|
|||
def test_archive_view_by_month(self):
|
||||
res = self.client.get('/dates/books/by_month/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'month')[::-1])
|
||||
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'month', 'DESC')))
|
||||
|
||||
def test_paginated_archive_view(self):
|
||||
self._make_books(20, base_date=datetime.date.today())
|
||||
_make_books(20, base_date=datetime.date.today())
|
||||
res = self.client.get('/dates/books/paginated/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'year')[::-1])
|
||||
self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
|
||||
self.assertEqual(list(res.context['latest']), list(Book.objects.all()[0:10]))
|
||||
self.assertTemplateUsed(res, 'generic_views/book_archive.html')
|
||||
|
||||
|
@ -99,7 +100,7 @@ class ArchiveIndexViewTests(TestCase):
|
|||
|
||||
def test_paginated_archive_view_does_not_load_entire_table(self):
|
||||
# Regression test for #18087
|
||||
self._make_books(20, base_date=datetime.date.today())
|
||||
_make_books(20, base_date=datetime.date.today())
|
||||
# 1 query for years list + 1 query for books
|
||||
with self.assertNumQueries(2):
|
||||
self.client.get('/dates/books/')
|
||||
|
@ -124,6 +125,13 @@ class ArchiveIndexViewTests(TestCase):
|
|||
res = self.client.get('/dates/booksignings/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_date_list_order(self):
|
||||
"""date_list should be sorted descending in index"""
|
||||
_make_books(5, base_date=datetime.date(2011, 12, 25))
|
||||
res = self.client.get('/dates/books/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(list(res.context['date_list']), list(reversed(sorted(res.context['date_list']))))
|
||||
|
||||
|
||||
class YearArchiveViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
|
@ -202,6 +210,12 @@ class YearArchiveViewTests(TestCase):
|
|||
res = self.client.get('/dates/booksignings/2008/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_date_list_order(self):
|
||||
"""date_list should be sorted ascending in year view"""
|
||||
_make_books(10, base_date=datetime.date(2011, 12, 25))
|
||||
res = self.client.get('/dates/books/2011/')
|
||||
self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list'])))
|
||||
|
||||
|
||||
class MonthArchiveViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
|
@ -322,6 +336,12 @@ class MonthArchiveViewTests(TestCase):
|
|||
res = self.client.get('/dates/booksignings/2008/apr/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_date_list_order(self):
|
||||
"""date_list should be sorted ascending in month view"""
|
||||
_make_books(10, base_date=datetime.date(2011, 12, 25))
|
||||
res = self.client.get('/dates/books/2011/dec/')
|
||||
self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list'])))
|
||||
|
||||
|
||||
class WeekArchiveViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
from django.conf import compat_patch_logging_config
|
||||
from django.core import mail
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.utils.log import CallbackFilter, RequireDebugFalse, getLogger
|
||||
from django.utils.log import CallbackFilter, RequireDebugFalse
|
||||
|
||||
|
||||
# logging config prior to using filter with mail_admins
|
||||
|
@ -153,7 +154,7 @@ class AdminEmailHandlerTest(TestCase):
|
|||
token1 = 'ping'
|
||||
token2 = 'pong'
|
||||
|
||||
logger = getLogger('django.request')
|
||||
logger = logging.getLogger('django.request')
|
||||
admin_email_handler = self.get_admin_email_handler(logger)
|
||||
# Backup then override original filters
|
||||
orig_filters = admin_email_handler.filters
|
||||
|
@ -184,7 +185,7 @@ class AdminEmailHandlerTest(TestCase):
|
|||
token1 = 'ping'
|
||||
token2 = 'pong'
|
||||
|
||||
logger = getLogger('django.request')
|
||||
logger = logging.getLogger('django.request')
|
||||
admin_email_handler = self.get_admin_email_handler(logger)
|
||||
# Backup then override original filters
|
||||
orig_filters = admin_email_handler.filters
|
||||
|
@ -222,7 +223,7 @@ class AdminEmailHandlerTest(TestCase):
|
|||
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
|
||||
logger = getLogger('django.request')
|
||||
logger = logging.getLogger('django.request')
|
||||
logger.error(message)
|
||||
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
|
@ -247,7 +248,7 @@ class AdminEmailHandlerTest(TestCase):
|
|||
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
|
||||
logger = getLogger('django.request')
|
||||
logger = logging.getLogger('django.request')
|
||||
logger.error(message)
|
||||
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
|
|
|
@ -498,6 +498,11 @@ class LocmemBackendTests(BaseEmailBackendTests, TestCase):
|
|||
connection2.send_messages([email])
|
||||
self.assertEqual(len(mail.outbox), 2)
|
||||
|
||||
def test_validate_multiline_headers(self):
|
||||
# Ticket #18861 - Validate emails when using the locmem backend
|
||||
with self.assertRaises(BadHeaderError):
|
||||
send_mail('Subject\nMultiline', 'Content', 'from@example.com', ['to@example.com'])
|
||||
|
||||
|
||||
class FileBackendTests(BaseEmailBackendTests, TestCase):
|
||||
email_backend = 'django.core.mail.backends.filebased.EmailBackend'
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
def set_attr(name, value):
|
||||
def wrapper(function):
|
||||
setattr(function, name, value)
|
||||
return function
|
||||
return wrapper
|
||||
|
||||
|
||||
class Guitarist(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
slug = models.CharField(max_length=50)
|
||||
|
@ -9,3 +16,9 @@ class Guitarist(models.Model):
|
|||
def url(self):
|
||||
"Returns the URL for this guitarist."
|
||||
return ('guitarist_detail', [self.slug])
|
||||
|
||||
@models.permalink
|
||||
@set_attr('attribute', 'value')
|
||||
def url_with_attribute(self):
|
||||
"Returns the URL for this guitarist and holds an attribute"
|
||||
return ('guitarist_detail', [self.slug])
|
||||
|
|
|
@ -16,3 +16,12 @@ class PermalinkTests(TestCase):
|
|||
"Methods using the @permalink decorator retain their docstring."
|
||||
g = Guitarist(name='Adrien Moignard', slug='adrienmoignard')
|
||||
self.assertEqual(g.url.__doc__, "Returns the URL for this guitarist.")
|
||||
|
||||
def test_wrapped_attribute(self):
|
||||
"""
|
||||
Methods using the @permalink decorator can have attached attributes
|
||||
from other decorators
|
||||
"""
|
||||
g = Guitarist(name='Adrien Moignard', slug='adrienmoignard')
|
||||
self.assertTrue(hasattr(g.url_with_attribute, 'attribute'))
|
||||
self.assertEqual(g.url_with_attribute.attribute, 'value')
|
||||
|
|
|
@ -16,6 +16,7 @@ from django.test import Client, TestCase
|
|||
from django.test.client import encode_file, RequestFactory
|
||||
from django.test.utils import ContextList, override_settings, str_prefix
|
||||
from django.template.response import SimpleTemplateResponse
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.http import HttpResponse
|
||||
|
||||
|
||||
|
@ -129,6 +130,14 @@ class AssertContainsTests(TestCase):
|
|||
self.assertNotContains(r, 'はたけ')
|
||||
self.assertNotContains(r, b'\xe3\x81\xaf\xe3\x81\x9f\xe3\x81\x91'.decode('utf-8'))
|
||||
|
||||
def test_nontext_contains(self):
|
||||
r = self.client.get('/test_client_regress/no_template_view/')
|
||||
self.assertContains(r, ugettext_lazy('once'))
|
||||
|
||||
def test_nontext_not_contains(self):
|
||||
r = self.client.get('/test_client_regress/no_template_view/')
|
||||
self.assertNotContains(r, ugettext_lazy('never'))
|
||||
|
||||
def test_assert_contains_renders_template_response(self):
|
||||
""" Test that we can pass in an unrendered SimpleTemplateReponse
|
||||
without throwing an error.
|
||||
|
|
|
@ -72,6 +72,11 @@ class DateFormatTests(unittest.TestCase):
|
|||
|
||||
self.assertEqual(dateformat.format(my_birthday, 'a'), 'p.m.')
|
||||
|
||||
def test_microsecond(self):
|
||||
# Regression test for #18951
|
||||
dt = datetime(2009, 5, 16, microsecond=123)
|
||||
self.assertEqual(dateformat.format(dt, 'u'), '000123')
|
||||
|
||||
def test_date_formats(self):
|
||||
my_birthday = datetime(1979, 7, 8, 22, 00)
|
||||
timestamp = datetime(2008, 5, 19, 11, 45, 23, 123456)
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
from unittest import TestCase
|
||||
from django.utils.numberformat import format as nformat
|
||||
from sys import float_info
|
||||
|
||||
|
||||
class TestNumberFormat(TestCase):
|
||||
|
||||
def test_format_number(self):
|
||||
self.assertEqual(nformat(1234, '.'), '1234')
|
||||
self.assertEqual(nformat(1234.2, '.'), '1234.2')
|
||||
self.assertEqual(nformat(1234, '.', decimal_pos=2), '1234.00')
|
||||
self.assertEqual(nformat(1234, '.', grouping=2, thousand_sep=','),
|
||||
'1234')
|
||||
self.assertEqual(nformat(1234, '.', grouping=2, thousand_sep=',',
|
||||
force_grouping=True), '12,34')
|
||||
self.assertEqual(nformat(-1234.33, '.', decimal_pos=1), '-1234.3')
|
||||
|
||||
def test_format_string(self):
|
||||
self.assertEqual(nformat('1234', '.'), '1234')
|
||||
self.assertEqual(nformat('1234.2', '.'), '1234.2')
|
||||
self.assertEqual(nformat('1234', '.', decimal_pos=2), '1234.00')
|
||||
self.assertEqual(nformat('1234', '.', grouping=2, thousand_sep=','),
|
||||
'1234')
|
||||
self.assertEqual(nformat('1234', '.', grouping=2, thousand_sep=',',
|
||||
force_grouping=True), '12,34')
|
||||
self.assertEqual(nformat('-1234.33', '.', decimal_pos=1), '-1234.3')
|
||||
|
||||
def test_large_number(self):
|
||||
most_max = ('{0}179769313486231570814527423731704356798070567525844996'
|
||||
'598917476803157260780028538760589558632766878171540458953'
|
||||
'514382464234321326889464182768467546703537516986049910576'
|
||||
'551282076245490090389328944075868508455133942304583236903'
|
||||
'222948165808559332123348274797826204144723168738177180919'
|
||||
'29988125040402618412485836{1}')
|
||||
most_max2 = ('{0}35953862697246314162905484746340871359614113505168999'
|
||||
'31978349536063145215600570775211791172655337563430809179'
|
||||
'07028764928468642653778928365536935093407075033972099821'
|
||||
'15310256415249098018077865788815173701691026788460916647'
|
||||
'38064458963316171186642466965495956524082894463374763543'
|
||||
'61838599762500808052368249716736')
|
||||
int_max = int(float_info.max)
|
||||
self.assertEqual(nformat(int_max, '.'), most_max.format('', '8'))
|
||||
self.assertEqual(nformat(int_max + 1, '.'), most_max.format('', '9'))
|
||||
self.assertEqual(nformat(int_max * 2, '.'), most_max2.format(''))
|
||||
self.assertEqual(nformat(0 - int_max, '.'), most_max.format('-', '8'))
|
||||
self.assertEqual(nformat(-1 - int_max, '.'), most_max.format('-', '9'))
|
||||
self.assertEqual(nformat(-2 * int_max, '.'), most_max2.format('-'))
|
|
@ -21,6 +21,7 @@ from .http import TestUtilsHttp
|
|||
from .ipv6 import TestUtilsIPv6
|
||||
from .jslex import JsToCForGettextTest, JsTokensTest
|
||||
from .module_loading import CustomLoader, DefaultLoader, EggLoader
|
||||
from .numberformat import TestNumberFormat
|
||||
from .os_utils import SafeJoinTests
|
||||
from .regex_helper import NormalizeTests
|
||||
from .simplelazyobject import TestUtilsSimpleLazyObject
|
||||
|
|
Loading…
Reference in New Issue