Merged master changes.

This commit is contained in:
Russell Keith-Magee 2012-09-07 23:45:03 +08:00
commit 7e82e83d67
146 changed files with 2278 additions and 875 deletions

View File

@ -1,4 +1,4 @@
include README
include README.rst
include AUTHORS
include INSTALL
include LICENSE

View File

@ -152,17 +152,25 @@ class UserSettingsHolder(BaseSettings):
Requests for configuration variables not in this class are satisfied
from the module specified in default_settings (if possible).
"""
self.__dict__['_deleted'] = set()
self.default_settings = default_settings
def __getattr__(self, name):
if name in self._deleted:
raise AttributeError
return getattr(self.default_settings, name)
def __setattr__(self, name, value):
self._deleted.discard(name)
return super(UserSettingsHolder, self).__setattr__(name, value)
def __delattr__(self, name):
self._deleted.add(name)
return super(UserSettingsHolder, self).__delattr__(name)
def __dir__(self):
return list(self.__dict__) + dir(self.default_settings)
# For Python < 2.6:
__members__ = property(lambda self: self.__dir__())
settings = LazySettings()

View File

@ -13,10 +13,11 @@ DATABASES = {
'default': {
'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
'NAME': '', # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
# The following settings are not used with sqlite3:
'USER': '',
'PASSWORD': '',
'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
'PORT': '', # Set to empty string for default.
}
}

View File

@ -29,7 +29,7 @@
{% if change %}{% if not is_popup %}
<ul class="object-tools">
{% block object-tools-items %}
<li><a href="history/" class="historylink">{% trans "History" %}</a></li>
<li><a href="{% url opts|admin_urlname:'history' original.pk %}" class="historylink">{% trans "History" %}</a></li>
{% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
{% endblock %}
</ul>

View File

@ -12,7 +12,7 @@ from django.utils import formats
from django.utils.html import format_html
from django.utils.text import capfirst
from django.utils import timezone
from django.utils.encoding import force_text, smart_text, smart_bytes
from django.utils.encoding import force_str, force_text, smart_text
from django.utils import six
from django.utils.translation import ungettext
from django.core.urlresolvers import reverse
@ -277,7 +277,7 @@ def label_for_field(name, model, model_admin=None, return_attr=False):
label = force_text(model._meta.verbose_name)
attr = six.text_type
elif name == "__str__":
label = smart_bytes(model._meta.verbose_name)
label = force_str(model._meta.verbose_name)
attr = bytes
else:
if callable(name):

View File

@ -6,7 +6,7 @@ from django.core.paginator import InvalidPage
from django.db import models
from django.db.models.fields import FieldDoesNotExist
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_text, smart_bytes
from django.utils.encoding import force_str, force_text
from django.utils.translation import ugettext, ugettext_lazy
from django.utils.http import urlencode
@ -94,7 +94,7 @@ class ChangeList(object):
# 'key' will be used as a keyword argument later, so Python
# requires it to be a string.
del lookup_params[key]
lookup_params[smart_bytes(key)] = value
lookup_params[force_str(key)] = value
if not self.model_admin.lookup_allowed(key, value):
raise SuspiciousOperation("Filtering by %s not allowed" % key)

View File

@ -6,7 +6,7 @@ from email.errors import HeaderParseError
from django.utils.safestring import mark_safe
from django.core.urlresolvers import reverse
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
try:
import docutils.core
import docutils.nodes
@ -66,7 +66,7 @@ def parse_rst(text, default_reference_context, thing_being_parsed=None):
"link_base" : reverse('django-admindocs-docroot').rstrip('/')
}
if thing_being_parsed:
thing_being_parsed = smart_bytes("<%s>" % thing_being_parsed)
thing_being_parsed = force_bytes("<%s>" % thing_being_parsed)
parts = docutils.core.publish_parts(text, source_path=thing_being_parsed,
destination_path=None, writer_name='html',
settings_overrides=overrides)

View File

@ -8,7 +8,7 @@ from django.conf import settings
from django.test.signals import setting_changed
from django.utils import importlib
from django.utils.datastructures import SortedDict
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
from django.core.exceptions import ImproperlyConfigured
from django.utils.crypto import (
pbkdf2, constant_time_compare, get_random_string)
@ -299,7 +299,7 @@ class SHA1PasswordHasher(BasePasswordHasher):
def encode(self, password, salt):
assert password
assert salt and '$' not in salt
hash = hashlib.sha1(smart_bytes(salt + password)).hexdigest()
hash = hashlib.sha1(force_bytes(salt + password)).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash)
def verify(self, password, encoded):
@ -327,7 +327,7 @@ class MD5PasswordHasher(BasePasswordHasher):
def encode(self, password, salt):
assert password
assert salt and '$' not in salt
hash = hashlib.md5(smart_bytes(salt + password)).hexdigest()
hash = hashlib.md5(force_bytes(salt + password)).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash)
def verify(self, password, encoded):
@ -361,7 +361,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
return ''
def encode(self, password, salt):
return hashlib.md5(smart_bytes(password)).hexdigest()
return hashlib.md5(force_bytes(password)).hexdigest()
def verify(self, password, encoded):
encoded_2 = self.encode(password, '')

View File

@ -10,6 +10,7 @@ import unicodedata
from django.contrib.auth import models as auth_app, get_user_model
from django.core import exceptions
from django.db.models import get_models, signals
from django.utils import six
from django.utils.six.moves import input
@ -87,17 +88,23 @@ def get_system_username():
:returns: The username as a unicode string, or an empty string if the
username could not be determined.
"""
default_locale = locale.getdefaultlocale()[1]
if default_locale:
try:
result = getpass.getuser()
except (ImportError, KeyError):
# KeyError will be raised by os.getpwuid() (called by getuser())
# if there is no corresponding entry in the /etc/passwd file
# (a very restricted chroot environment, for example).
return ''
if not six.PY3:
default_locale = locale.getdefaultlocale()[1]
if not default_locale:
return ''
try:
return getpass.getuser().decode(default_locale)
except (ImportError, KeyError, UnicodeDecodeError):
# KeyError will be raised by os.getpwuid() (called by getuser())
# if there is no corresponding entry in the /etc/passwd file
# (a very restricted chroot environment, for example).
result = result.decode(default_locale)
except UnicodeDecodeError:
# UnicodeDecodeError - preventive treatment for non-latin Windows.
pass
return ''
return ''
return result
def get_default_username(check_db=True):

View File

@ -9,16 +9,20 @@ from django.core.management import call_command
from django.core.management.base import CommandError
from django.test import TestCase
from django.test.utils import override_settings
from django.utils import six
from django.utils.six import StringIO
class GetDefaultUsernameTestCase(TestCase):
def setUp(self):
self._getpass_getuser = management.get_system_username
self.old_get_system_username = management.get_system_username
def tearDown(self):
management.get_system_username = self._getpass_getuser
management.get_system_username = self.old_get_system_username
def test_actual_implementation(self):
self.assertIsInstance(management.get_system_username(), six.text_type)
def test_simple(self):
management.get_system_username = lambda: 'joe'

View File

@ -7,7 +7,7 @@ from __future__ import unicode_literals
from django.db import models
from django.utils import formats
from django.utils.text import capfirst
from django.utils.encoding import smart_text, smart_str, iri_to_uri
from django.utils.encoding import smart_text, force_str, iri_to_uri
from django.db.models.query import QuerySet
from django.utils.encoding import python_2_unicode_compatible
@ -23,7 +23,7 @@ class EasyModel(object):
self.verbose_name_plural = model._meta.verbose_name_plural
def __repr__(self):
return smart_str('<EasyModel for %s>' % self.model._meta.object_name)
return force_str('<EasyModel for %s>' % self.model._meta.object_name)
def model_databrowse(self):
"Returns the ModelDatabrowse class for this model."
@ -62,7 +62,7 @@ class EasyField(object):
self.model, self.field = easy_model, field
def __repr__(self):
return smart_str('<EasyField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
return force_str('<EasyField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
def choices(self):
for value, label in self.field.choices:
@ -80,7 +80,7 @@ class EasyChoice(object):
self.value, self.label = value, label
def __repr__(self):
return smart_str('<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
return force_str('<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
def url(self):
return '%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, iri_to_uri(self.value))
@ -91,7 +91,7 @@ class EasyInstance(object):
self.model, self.instance = easy_model, instance
def __repr__(self):
return smart_str('<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val()))
return force_str('<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val()))
def __str__(self):
val = smart_text(self.instance)
@ -135,7 +135,7 @@ class EasyInstanceField(object):
self.raw_value = getattr(instance.instance, field.name)
def __repr__(self):
return smart_str('<EasyInstanceField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
return force_str('<EasyInstanceField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
def values(self):
"""

View File

@ -1,16 +1,15 @@
from django.db import connections
from django.db.models.query import QuerySet, ValuesQuerySet, ValuesListQuerySet
from django.utils import six
from django.contrib.gis.db.models import aggregates
from django.contrib.gis.db.models.fields import get_srid_info, PointField, LineStringField
from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery
from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.measure import Area, Distance
from django.utils import six
from django.utils import six
class GeoQuerySet(QuerySet):
"The Geographic QuerySet."

View File

@ -8,7 +8,6 @@ from django.core.paginator import EmptyPage, PageNotAnInteger
from django.contrib.gis.db.models.fields import GeometryField
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.models import get_model
from django.utils.encoding import smart_bytes
from django.utils import six
from django.utils.translation import ugettext as _
@ -61,7 +60,7 @@ def sitemap(request, sitemaps, section=None):
raise Http404(_("Page %s empty") % page)
except PageNotAnInteger:
raise Http404(_("No page '%s'") % page)
xml = smart_bytes(loader.render_to_string('gis/sitemaps/geo_sitemap.xml', {'urlset': urls}))
xml = loader.render_to_string('gis/sitemaps/geo_sitemap.xml', {'urlset': urls})
return HttpResponse(xml, content_type='application/xml')
def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB_ALIAS):

View File

@ -186,6 +186,15 @@ class GeoModelTest(TestCase):
self.assertEqual(1, qs.count())
for pc in qs: self.assertEqual(32128, pc.point.srid)
def test_raw_sql_query(self):
"Testing raw SQL query."
cities1 = City.objects.all()
# Only PostGIS would support a 'select *' query because of its recognized
# HEXEWKB format for geometry fields
cities2 = City.objects.raw('select id, name, asText(point) from geoapp_city')
self.assertEqual(len(cities1), len(list(cities2)))
self.assertTrue(isinstance(cities2[0].point, Point))
class GeoLookupTest(TestCase):

View File

@ -13,7 +13,7 @@ markup syntaxes to HTML; currently there is support for:
from django import template
from django.conf import settings
from django.utils.encoding import smart_bytes, force_text
from django.utils.encoding import force_bytes, force_text
from django.utils.safestring import mark_safe
register = template.Library()
@ -27,7 +27,7 @@ def textile(value):
raise template.TemplateSyntaxError("Error in 'textile' filter: The Python textile library isn't installed.")
return force_text(value)
else:
return mark_safe(force_text(textile.textile(smart_bytes(value), encoding='utf-8', output='utf-8')))
return mark_safe(force_text(textile.textile(force_bytes(value), encoding='utf-8', output='utf-8')))
@register.filter(is_safe=True)
def markdown(value, arg=''):
@ -80,5 +80,5 @@ def restructuredtext(value):
return force_text(value)
else:
docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {})
parts = publish_parts(source=smart_bytes(value), writer_name="html4css1", settings_overrides=docutils_settings)
parts = publish_parts(source=force_bytes(value), writer_name="html4css1", settings_overrides=docutils_settings)
return mark_safe(force_text(parts["fragment"]))

View File

@ -14,7 +14,7 @@ from django.utils.crypto import constant_time_compare
from django.utils.crypto import get_random_string
from django.utils.crypto import salted_hmac
from django.utils import timezone
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
class CreateError(Exception):
"""
@ -84,7 +84,7 @@ class SessionBase(object):
return base64.b64encode(hash.encode() + b":" + pickled).decode('ascii')
def decode(self, session_data):
encoded_data = base64.b64decode(smart_bytes(session_data))
encoded_data = base64.b64decode(force_bytes(session_data))
try:
# could produce ValueError if there is no ':'
hash, pickled = encoded_data.split(b':', 1)

View File

@ -192,7 +192,7 @@ Type 'yes' to continue, or 'no' to cancel: """
def clear_dir(self, path):
"""
Deletes the given relative path using the destinatin storage backend.
Deletes the given relative path using the destination storage backend.
"""
dirs, files = self.storage.listdir(path)
for f in files:

View File

@ -16,7 +16,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage, get_storage_class
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_text, smart_bytes
from django.utils.encoding import force_bytes, force_text
from django.utils.functional import LazyObject
from django.utils.importlib import import_module
@ -118,7 +118,7 @@ class CachedFilesMixin(object):
return urlunsplit(unparsed_name)
def cache_key(self, name):
return 'staticfiles:%s' % hashlib.md5(smart_bytes(name)).hexdigest()
return 'staticfiles:%s' % hashlib.md5(force_bytes(name)).hexdigest()
def url(self, name, force=False):
"""
@ -254,7 +254,7 @@ class CachedFilesMixin(object):
if hashed_file_exists:
self.delete(hashed_name)
# then save the processed result
content_file = ContentFile(smart_bytes(content))
content_file = ContentFile(force_bytes(content))
saved_name = self._save(hashed_name, content_file)
hashed_name = force_text(saved_name.replace('\\', '/'))
processed = True

View File

@ -12,7 +12,7 @@ from django.conf import settings
from django.core.cache.backends.base import BaseCache
from django.db import connections, router, transaction, DatabaseError
from django.utils import timezone
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
class Options(object):
@ -73,7 +73,7 @@ class DatabaseCache(BaseDatabaseCache):
transaction.commit_unless_managed(using=db)
return default
value = connections[db].ops.process_clob(row[1])
return pickle.loads(base64.b64decode(smart_bytes(value)))
return pickle.loads(base64.b64decode(force_bytes(value)))
def set(self, key, value, timeout=None, version=None):
key = self.make_key(key, version=version)

View File

@ -10,7 +10,7 @@ except ImportError:
import pickle
from django.core.cache.backends.base import BaseCache
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
class FileBasedCache(BaseCache):
def __init__(self, dir, params):
@ -137,7 +137,7 @@ class FileBasedCache(BaseCache):
Thus, a cache key of "foo" gets turnned into a file named
``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
"""
path = hashlib.md5(smart_bytes(key)).hexdigest()
path = hashlib.md5(force_bytes(key)).hexdigest()
path = os.path.join(path[:2], path[2:4], path[4:])
return os.path.join(self._dir, path)

View File

@ -6,7 +6,7 @@ from threading import local
from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError
from django.utils import six
from django.utils.encoding import smart_str
from django.utils.encoding import force_str
class BaseMemcachedCache(BaseCache):
def __init__(self, server, params, library, value_not_found_exception):
@ -53,7 +53,7 @@ class BaseMemcachedCache(BaseCache):
def make_key(self, key, version=None):
# Python 2 memcache requires the key to be a byte string.
return smart_str(super(BaseMemcachedCache, self).make_key(key, version))
return force_str(super(BaseMemcachedCache, self).make_key(key, version))
def add(self, key, value, timeout=0, version=None):
key = self.make_key(key, version=version)

View File

@ -1,10 +1,11 @@
from __future__ import unicode_literals
import os
from io import BytesIO, UnsupportedOperation
from io import BytesIO, StringIO, UnsupportedOperation
from django.utils.encoding import smart_bytes, smart_text
from django.utils.encoding import smart_text
from django.core.files.utils import FileProxyMixin
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
@ -132,7 +133,8 @@ class ContentFile(File):
"""
def __init__(self, content, name=None):
content = content or b''
super(ContentFile, self).__init__(BytesIO(content), name=name)
stream_class = StringIO if isinstance(content, six.text_type) else BytesIO
super(ContentFile, self).__init__(stream_class(content), name=name)
self.size = len(content)
def __str__(self):

View File

@ -195,11 +195,18 @@ class FileSystemStorage(Storage):
fd = os.open(full_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
try:
locks.lock(fd, locks.LOCK_EX)
_file = None
for chunk in content.chunks():
os.write(fd, chunk)
if _file is None:
mode = 'wb' if isinstance(chunk, bytes) else 'wt'
_file = os.fdopen(fd, mode)
_file.write(chunk)
finally:
locks.unlock(fd)
os.close(fd)
if _file is not None:
_file.close()
else:
os.close(fd)
except OSError as e:
if e.errno == errno.EEXIST:
# Ooops, the file exists. We need a new file name.

View File

@ -8,7 +8,7 @@ from io import BytesIO
from django.conf import settings
from django.core.files.base import File
from django.core.files import temp as tempfile
from django.utils.encoding import smart_str
from django.utils.encoding import force_str
__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile',
'SimpleUploadedFile')
@ -30,7 +30,7 @@ class UploadedFile(File):
self.charset = charset
def __repr__(self):
return smart_str("<%s: %s (%s)>" % (
return force_str("<%s: %s (%s)>" % (
self.__class__.__name__, self.name, self.content_type))
def _get_name(self):

View File

@ -9,7 +9,7 @@ from django.core import signals
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_text, smart_str, iri_to_uri
from django.utils.encoding import force_str, force_text, iri_to_uri
from django.utils.log import getLogger
logger = getLogger('django.request')
@ -246,5 +246,5 @@ class WSGIHandler(base.BaseHandler):
response_headers = [(str(k), str(v)) for k, v in response.items()]
for c in response.cookies.values():
response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
start_response(smart_str(status), response_headers)
start_response(force_str(status), response_headers)
return response

View File

@ -12,7 +12,7 @@ import traceback
import django
from django.core.exceptions import ImproperlyConfigured
from django.core.management.color import color_style
from django.utils.encoding import smart_str
from django.utils.encoding import force_str
from django.utils.six import StringIO
@ -65,7 +65,7 @@ class OutputWrapper(object):
msg += ending
style_func = [f for f in (style_func, self.style_func, lambda x:x)
if f is not None][0]
self._out.write(smart_str(style_func(msg)))
self._out.write(force_str(style_func(msg)))
class BaseCommand(object):

View File

@ -1,4 +1,7 @@
from __future__ import unicode_literals
import keyword
import re
from optparse import make_option
from django.core.management.base import NoArgsCommand, CommandError
@ -31,6 +34,7 @@ class Command(NoArgsCommand):
table_name_filter = options.get('table_name_filter')
table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '')
strip_prefix = lambda s: s.startswith("u'") and s[1:] or s
cursor = connection.cursor()
yield "# This is an auto-generated Django model module."
@ -41,6 +45,7 @@ class Command(NoArgsCommand):
yield "#"
yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'"
yield "# into your database."
yield "from __future__ import unicode_literals"
yield ''
yield 'from %s import models' % self.db_module
yield ''
@ -59,16 +64,19 @@ class Command(NoArgsCommand):
indexes = connection.introspection.get_indexes(cursor, table_name)
except NotImplementedError:
indexes = {}
used_column_names = [] # Holds column names used in the table so far
for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
column_name = row[0]
att_name = column_name.lower()
comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
extra_params = {} # Holds Field parameters such as 'db_column'.
column_name = row[0]
is_relation = i in relations
# If the column name can't be used verbatim as a Python
# attribute, set the "db_column" for this Field.
if ' ' in att_name or '-' in att_name or keyword.iskeyword(att_name) or column_name != att_name:
extra_params['db_column'] = column_name
att_name, params, notes = self.normalize_col_name(
column_name, used_column_names, is_relation)
extra_params.update(params)
comment_notes.extend(notes)
used_column_names.append(att_name)
# Add primary_key and unique, if necessary.
if column_name in indexes:
@ -77,30 +85,12 @@ class Command(NoArgsCommand):
elif indexes[column_name]['unique']:
extra_params['unique'] = True
# Modify the field name to make it Python-compatible.
if ' ' in att_name:
att_name = att_name.replace(' ', '_')
comment_notes.append('Field renamed to remove spaces.')
if '-' in att_name:
att_name = att_name.replace('-', '_')
comment_notes.append('Field renamed to remove dashes.')
if column_name != att_name:
comment_notes.append('Field name made lowercase.')
if i in relations:
if is_relation:
rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1])
if rel_to in known_models:
field_type = 'ForeignKey(%s' % rel_to
else:
field_type = "ForeignKey('%s'" % rel_to
if att_name.endswith('_id'):
att_name = att_name[:-3]
else:
extra_params['db_column'] = column_name
else:
# Calling `get_field_type` to get the field type string and any
# additional paramters and notes.
@ -110,16 +100,6 @@ class Command(NoArgsCommand):
field_type += '('
if keyword.iskeyword(att_name):
att_name += '_field'
comment_notes.append('Field renamed because it was a Python reserved word.')
if att_name[0].isdigit():
att_name = 'number_%s' % att_name
extra_params['db_column'] = six.text_type(column_name)
comment_notes.append("Field renamed because it wasn't a "
"valid Python identifier.")
# Don't output 'id = meta.AutoField(primary_key=True)', because
# that's assumed if it doesn't exist.
if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}:
@ -136,7 +116,9 @@ class Command(NoArgsCommand):
if extra_params:
if not field_desc.endswith('('):
field_desc += ', '
field_desc += ', '.join(['%s=%r' % (k, v) for k, v in extra_params.items()])
field_desc += ', '.join([
'%s=%s' % (k, strip_prefix(repr(v)))
for k, v in extra_params.items()])
field_desc += ')'
if comment_notes:
field_desc += ' # ' + ' '.join(comment_notes)
@ -144,6 +126,62 @@ class Command(NoArgsCommand):
for meta_line in self.get_meta(table_name):
yield meta_line
def normalize_col_name(self, col_name, used_column_names, is_relation):
"""
Modify the column name to make it Python-compatible as a field name
"""
field_params = {}
field_notes = []
new_name = col_name.lower()
if new_name != col_name:
field_notes.append('Field name made lowercase.')
if is_relation:
if new_name.endswith('_id'):
new_name = new_name[:-3]
else:
field_params['db_column'] = col_name
new_name, num_repl = re.subn(r'\W', '_', new_name)
if num_repl > 0:
field_notes.append('Field renamed to remove unsuitable characters.')
if new_name.find('__') >= 0:
while new_name.find('__') >= 0:
new_name = new_name.replace('__', '_')
if col_name.lower().find('__') >= 0:
# Only add the comment if the double underscore was in the original name
field_notes.append("Field renamed because it contained more than one '_' in a row.")
if new_name.startswith('_'):
new_name = 'field%s' % new_name
field_notes.append("Field renamed because it started with '_'.")
if new_name.endswith('_'):
new_name = '%sfield' % new_name
field_notes.append("Field renamed because it ended with '_'.")
if keyword.iskeyword(new_name):
new_name += '_field'
field_notes.append('Field renamed because it was a Python reserved word.')
if new_name[0].isdigit():
new_name = 'number_%s' % new_name
field_notes.append("Field renamed because it wasn't a valid Python identifier.")
if new_name in used_column_names:
num = 0
while '%s_%d' % (new_name, num) in used_column_names:
num += 1
new_name = '%s_%d' % (new_name, num)
field_notes.append('Field renamed because of name conflict.')
if col_name != new_name and field_notes:
field_params['db_column'] = col_name
return new_name, field_params, field_notes
def get_field_type(self, connection, table_name, row):
"""
Given the database connection, the table name, and the cursor row
@ -181,6 +219,6 @@ class Command(NoArgsCommand):
to construct the inner Meta class for the model corresponding
to the given database table name.
"""
return [' class Meta:',
' db_table = %r' % table_name,
'']
return [" class Meta:",
" db_table = '%s'" % table_name,
""]

View File

@ -196,6 +196,10 @@ class Command(BaseCommand):
loaded_object_count += loaded_objects_in_fixture
fixture_object_count += objects_in_fixture
label_found = True
except Exception as e:
if not isinstance(e, CommandError):
e.args = ("Problem installing fixture '%s': %s" % (full_path, e),)
raise
finally:
fixture.close()
@ -209,7 +213,11 @@ class Command(BaseCommand):
# Since we disabled constraint checks, we must manually check for
# any invalid keys that might have been added
table_names = [model._meta.db_table for model in models]
connection.check_constraints(table_names=table_names)
try:
connection.check_constraints(table_names=table_names)
except Exception as e:
e.args = ("Problem installing fixtures: %s" % e,)
raise
except (SystemExit, KeyboardInterrupt):
raise
@ -217,8 +225,6 @@ class Command(BaseCommand):
if commit:
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
if not isinstance(e, CommandError):
e.args = ("Problem installing fixture '%s': %s" % (full_path, e),)
raise
# If we found even one object in a fixture, we need to reset the

View File

@ -47,31 +47,27 @@ def _popen(cmd):
output, errors = p.communicate()
return output, errors, p.returncode
def walk(root, topdown=True, onerror=None, followlinks=False,
ignore_patterns=None, verbosity=0, stdout=sys.stdout):
def find_files(root, ignore_patterns, verbosity, stdout=sys.stdout, symlinks=False):
"""
A version of os.walk that can follow symlinks for Python < 2.6
Helper function to get all files in the given root.
"""
if ignore_patterns is None:
ignore_patterns = []
dir_suffix = '%s*' % os.sep
norm_patterns = [p[:-len(dir_suffix)] if p.endswith(dir_suffix) else p for p in ignore_patterns]
for dirpath, dirnames, filenames in os.walk(root, topdown, onerror):
remove_dirs = []
for dirname in dirnames:
all_files = []
for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=symlinks):
for dirname in dirnames[:]:
if is_ignored(os.path.normpath(os.path.join(dirpath, dirname)), norm_patterns):
remove_dirs.append(dirname)
for dirname in remove_dirs:
dirnames.remove(dirname)
if verbosity > 1:
stdout.write('ignoring directory %s\n' % dirname)
yield (dirpath, dirnames, filenames)
if followlinks:
for d in dirnames:
p = os.path.join(dirpath, d)
if os.path.islink(p):
for link_dirpath, link_dirnames, link_filenames in walk(p):
yield (link_dirpath, link_dirnames, link_filenames)
dirnames.remove(dirname)
if verbosity > 1:
stdout.write('ignoring directory %s\n' % dirname)
for filename in filenames:
if is_ignored(os.path.normpath(os.path.join(dirpath, filename)), ignore_patterns):
if verbosity > 1:
stdout.write('ignoring file %s in %s\n' % (filename, dirpath))
else:
all_files.extend([(dirpath, filename)])
all_files.sort()
return all_files
def is_ignored(path, ignore_patterns):
"""
@ -82,23 +78,6 @@ def is_ignored(path, ignore_patterns):
return True
return False
def find_files(root, ignore_patterns, verbosity, stdout=sys.stdout, symlinks=False):
"""
Helper function to get all files in the given root.
"""
all_files = []
for (dirpath, dirnames, filenames) in walk(root, followlinks=symlinks,
ignore_patterns=ignore_patterns, verbosity=verbosity, stdout=stdout):
for filename in filenames:
norm_filepath = os.path.normpath(os.path.join(dirpath, filename))
if is_ignored(norm_filepath, ignore_patterns):
if verbosity > 1:
stdout.write('ignoring file %s in %s\n' % (filename, dirpath))
else:
all_files.extend([(dirpath, filename)])
all_files.sort()
return all_files
def copy_plural_forms(msgs, locale, domain, verbosity, stdout=sys.stdout):
"""
Copies plural forms header contents from a Django catalog of locale to

View File

@ -80,14 +80,14 @@ class Command(NoArgsCommand):
readline.parse_and_bind("tab:complete")
# We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
# conventions and get $PYTHONSTARTUP first then import user.
# conventions and get $PYTHONSTARTUP first then .pythonrc.py.
if not use_plain:
pythonrc = os.environ.get("PYTHONSTARTUP")
if pythonrc and os.path.isfile(pythonrc):
try:
execfile(pythonrc)
except NameError:
pass
# This will import .pythonrc.py as a side-effect
import user
for pythonrc in (os.environ.get("PYTHONSTARTUP"),
os.path.expanduser('~/.pythonrc.py')):
if pythonrc and os.path.isfile(pythonrc):
try:
with open(pythonrc) as handle:
exec(compile(handle.read(), pythonrc, 'exec'))
except NameError:
pass
code.interact(local=imported_objects)

View File

@ -8,6 +8,8 @@ import shutil
import stat
import sys
import tempfile
import codecs
try:
from urllib.request import urlretrieve
except ImportError: # Python 2
@ -154,12 +156,12 @@ class TemplateCommand(BaseCommand):
# Only render the Python files, as we don't want to
# accidentally render Django templates files
with open(old_path, 'r') as template_file:
with codecs.open(old_path, 'r', 'utf-8') as template_file:
content = template_file.read()
if filename.endswith(extensions) or filename in extra_files:
template = Template(content)
content = template.render(context)
with open(new_path, 'w') as new_file:
with codecs.open(new_path, 'w', 'utf-8') as new_file:
new_file.write(content)
if self.verbosity >= 2:

View File

@ -1,7 +1,7 @@
import sys
from django.core.management.color import color_style
from django.utils.encoding import smart_str
from django.utils.encoding import force_str
from django.utils.itercompat import is_iterable
from django.utils import six
@ -14,7 +14,7 @@ class ModelErrorCollection:
def add(self, context, error):
self.errors.append((context, error))
self.outfile.write(self.style.ERROR(smart_str("%s: %s\n" % (context, error))))
self.outfile.write(self.style.ERROR(force_str("%s: %s\n" % (context, error))))
def get_validation_errors(outfile, app=None):

View File

@ -12,7 +12,6 @@ import json
from django.core.serializers.base import DeserializationError
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer
from django.utils.encoding import smart_bytes
from django.utils import six
from django.utils.timezone import is_aware

View File

@ -12,7 +12,6 @@ from django.db import models
from django.core.serializers.base import DeserializationError
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer
from django.utils.encoding import smart_bytes
from django.utils import six

View File

@ -32,6 +32,7 @@ start of the base64 JSON.
There are 65 url-safe characters: the 64 used by url-safe base64 and the ':'.
These functions make use of all of them.
"""
from __future__ import unicode_literals
import base64
@ -43,7 +44,7 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils import baseconv
from django.utils.crypto import constant_time_compare, salted_hmac
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes, force_str, force_text
from django.utils.importlib import import_module
@ -62,12 +63,12 @@ class SignatureExpired(BadSignature):
def b64_encode(s):
return base64.urlsafe_b64encode(smart_bytes(s)).decode('ascii').strip('=')
return base64.urlsafe_b64encode(s).strip(b'=')
def b64_decode(s):
pad = '=' * (-len(s) % 4)
return base64.urlsafe_b64decode(smart_bytes(s + pad)).decode('ascii')
pad = b'=' * (-len(s) % 4)
return base64.urlsafe_b64decode(s + pad)
def base64_hmac(salt, value, key):
@ -116,20 +117,20 @@ def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer,
value or re-using a salt value across different parts of your
application without good cause is a security risk.
"""
data = serializer().dumps(obj)
data = force_bytes(serializer().dumps(obj))
# Flag for if it's been compressed or not
is_compressed = False
if compress:
# Avoid zlib dependency unless compress is being used
compressed = zlib.compress(smart_bytes(data))
compressed = zlib.compress(data)
if len(compressed) < (len(data) - 1):
data = compressed
is_compressed = True
base64d = b64_encode(data)
if is_compressed:
base64d = '.' + base64d
base64d = b'.' + base64d
return TimestampSigner(key, salt=salt).sign(base64d)
@ -137,37 +138,45 @@ def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, ma
"""
Reverse of dumps(), raises BadSignature if signature fails
"""
base64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age)
# TimestampSigner.unsign always returns unicode but base64 and zlib
# compression operate on bytes.
base64d = force_bytes(TimestampSigner(key, salt=salt).unsign(s, max_age=max_age))
decompress = False
if base64d[0] == '.':
if base64d[0] == b'.':
# It's compressed; uncompress it first
base64d = base64d[1:]
decompress = True
data = b64_decode(base64d)
if decompress:
data = zlib.decompress(data)
return serializer().loads(data)
return serializer().loads(force_str(data))
class Signer(object):
def __init__(self, key=None, sep=':', salt=None):
self.sep = sep
self.key = key or settings.SECRET_KEY
self.salt = salt or ('%s.%s' %
(self.__class__.__module__, self.__class__.__name__))
# Use of native strings in all versions of Python
self.sep = str(sep)
self.key = str(key or settings.SECRET_KEY)
self.salt = str(salt or
'%s.%s' % (self.__class__.__module__, self.__class__.__name__))
def signature(self, value):
return base64_hmac(self.salt + 'signer', value, self.key)
signature = base64_hmac(self.salt + 'signer', value, self.key)
# Convert the signature from bytes to str only on Python 3
return force_str(signature)
def sign(self, value):
return '%s%s%s' % (value, self.sep, self.signature(value))
value = force_str(value)
return str('%s%s%s') % (value, self.sep, self.signature(value))
def unsign(self, signed_value):
signed_value = force_str(signed_value)
if not self.sep in signed_value:
raise BadSignature('No "%s" found in value' % self.sep)
value, sig = signed_value.rsplit(self.sep, 1)
if constant_time_compare(sig, self.signature(value)):
return value
return force_text(value)
raise BadSignature('Signature "%s" does not match' % sig)
@ -177,8 +186,9 @@ class TimestampSigner(Signer):
return baseconv.base62.encode(int(time.time()))
def sign(self, value):
value = '%s%s%s' % (value, self.sep, self.timestamp())
return '%s%s%s' % (value, self.sep, self.signature(value))
value = force_str(value)
value = str('%s%s%s') % (value, self.sep, self.timestamp())
return super(TimestampSigner, self).sign(value)
def unsign(self, value, max_age=None):
result = super(TimestampSigner, self).unsign(value)

View File

@ -14,7 +14,7 @@ from threading import local
from django.http import Http404
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
from django.utils.datastructures import MultiValueDict
from django.utils.encoding import iri_to_uri, force_text, smart_str
from django.utils.encoding import force_str, force_text, iri_to_uri
from django.utils.functional import memoize, lazy
from django.utils.importlib import import_module
from django.utils.module_loading import module_has_submodule
@ -195,7 +195,7 @@ class RegexURLPattern(LocaleRegexProvider):
self.name = name
def __repr__(self):
return smart_str('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
return force_str('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern))
def add_prefix(self, prefix):
"""
@ -245,8 +245,13 @@ class RegexURLResolver(LocaleRegexProvider):
self._app_dict = {}
def __repr__(self):
return smart_str('<%s %s (%s:%s) %s>' % (
self.__class__.__name__, self.urlconf_name, self.app_name,
if isinstance(self.urlconf_name, list) and len(self.urlconf_name):
# Don't bother to output the whole list, it can be huge
urlconf_repr = '<%s list>' % self.urlconf_name[0].__class__.__name__
else:
urlconf_repr = repr(self.urlconf_name)
return force_str('<%s %s (%s:%s) %s>' % (
self.__class__.__name__, urlconf_repr, self.app_name,
self.namespace, self.regex.pattern))
def _populate(self):

View File

@ -8,7 +8,7 @@ except ImportError: # Python 2
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_text
from django.utils.encoding import force_text
from django.utils.ipv6 import is_valid_ipv6_address
from django.utils import six
@ -37,7 +37,7 @@ class RegexValidator(object):
"""
Validates that the input matches the regular expression.
"""
if not self.regex.search(smart_text(value)):
if not self.regex.search(force_text(value)):
raise ValidationError(self.message, code=self.code)
@ -57,7 +57,7 @@ class URLValidator(RegexValidator):
except ValidationError as e:
# Trivial case failed. Try for possible IDN domain
if value:
value = smart_text(value)
value = force_text(value)
scheme, netloc, path, query, fragment = urlsplit(value)
try:
netloc = netloc.encode('idna').decode('ascii') # IDN -> ACE

View File

@ -609,7 +609,7 @@ class BaseDatabaseOperations(object):
exists for database backends to provide a better implementation
according to their own quoting schemes.
"""
from django.utils.encoding import smart_text, force_text
from django.utils.encoding import force_text
# Convert params to contain Unicode values.
to_unicode = lambda s: force_text(s, strings_only=True, errors='replace')
@ -618,7 +618,7 @@ class BaseDatabaseOperations(object):
else:
u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()])
return smart_text(sql) % u_params
return force_text(sql) % u_params
def last_insert_id(self, cursor, table_name, pk_name):
"""
@ -802,8 +802,8 @@ class BaseDatabaseOperations(object):
def prep_for_like_query(self, x):
"""Prepares a value for use in a LIKE query."""
from django.utils.encoding import smart_text
return smart_text(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_")
from django.utils.encoding import force_text
return force_text(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_")
# Same as prep_for_like_query(), but called for "iexact" matches, which
# need not necessarily be implemented using "LIKE" in the backend.

View File

@ -1,10 +1,16 @@
try:
from itertools import zip_longest
except ImportError:
from itertools import izip_longest as zip_longest
from django.db.models.sql import compiler
class SQLCompiler(compiler.SQLCompiler):
def resolve_columns(self, row, fields=()):
values = []
index_extra_select = len(self.query.extra_select)
for value, field in map(None, row[index_extra_select:], fields):
for value, field in zip_longest(row[index_extra_select:], fields):
if (field and field.get_internal_type() in ("BooleanField", "NullBooleanField") and
value in (0, 1)):
value = bool(value)

View File

@ -1,8 +1,9 @@
import re
from .base import FIELD_TYPE
from django.db.backends import BaseDatabaseIntrospection
from django.utils import six
from MySQLdb import ProgrammingError, OperationalError
from MySQLdb.constants import FIELD_TYPE
import re
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
@ -35,9 +36,20 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
return [row[0] for row in cursor.fetchall()]
def get_table_description(self, cursor, table_name):
"Returns a description of the table, with the DB-API cursor.description interface."
"""
Returns a description of the table, with the DB-API cursor.description interface."
"""
# varchar length returned by cursor.description is an internal length,
# not visible length (#5725), use information_schema database to fix this
cursor.execute("""
SELECT column_name, character_maximum_length FROM information_schema.columns
WHERE table_name = %s AND table_schema = DATABASE()
AND character_maximum_length IS NOT NULL""", [table_name])
length_map = dict(cursor.fetchall())
cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
return cursor.description
return [line[:3] + (length_map.get(line[0], line[3]),) + line[4:]
for line in cursor.description]
def _name_to_index(self, cursor, table_name):
"""

View File

@ -10,8 +10,6 @@ import decimal
import sys
import warnings
from django.utils import six
def _setup_environment(environ):
import platform
# Cygwin requires some special voodoo to set the environment variables
@ -53,7 +51,7 @@ from django.db.backends.signals import connection_created
from django.db.backends.oracle.client import DatabaseClient
from django.db.backends.oracle.creation import DatabaseCreation
from django.db.backends.oracle.introspection import DatabaseIntrospection
from django.utils.encoding import smart_bytes, force_text
from django.utils.encoding import force_bytes, force_text
from django.utils import six
from django.utils import timezone
@ -66,7 +64,7 @@ IntegrityError = Database.IntegrityError
if int(Database.version.split('.', 1)[0]) >= 5 and not hasattr(Database, 'UNICODE'):
convert_unicode = force_text
else:
convert_unicode = smart_bytes
convert_unicode = force_bytes
class DatabaseFeatures(BaseDatabaseFeatures):
@ -604,9 +602,9 @@ class OracleParam(object):
elif param is False:
param = "0"
if hasattr(param, 'bind_parameter'):
self.smart_bytes = param.bind_parameter(cursor)
self.force_bytes = param.bind_parameter(cursor)
else:
self.smart_bytes = convert_unicode(param, cursor.charset,
self.force_bytes = convert_unicode(param, cursor.charset,
strings_only)
if hasattr(param, 'input_size'):
# If parameter has `input_size` attribute, use that.
@ -685,7 +683,7 @@ class FormatStylePlaceholderCursor(object):
self.setinputsizes(*sizes)
def _param_generator(self, params):
return [p.smart_bytes for p in params]
return [p.force_bytes for p in params]
def execute(self, query, params=None):
if params is None:

View File

@ -45,8 +45,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
WHERE table_name = %s""", [table_name])
null_map = dict(cursor.fetchall())
cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
return [tuple([item for item in line[:6]] + [null_map[line[0]]=='YES'])
for line in cursor.description]
return [line[:6] + (null_map[line[0]]=='YES',)
for line in cursor.description]
def get_relations(self, cursor, table_name):
"""

View File

@ -1,6 +1,14 @@
import re
from django.db.backends import BaseDatabaseIntrospection
field_size_re = re.compile(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$')
def get_field_size(name):
""" Extract the size number from a "varchar(11)" type name """
m = field_size_re.search(name)
return int(m.group(1)) if m else None
# This light wrapper "fakes" a dictionary interface, because some SQLite data
# types include variables in them -- e.g. "varchar(30)" -- and can't be matched
# as a simple dictionary lookup.
@ -32,10 +40,9 @@ class FlexibleFieldLookupDict(object):
try:
return self.base_data_types_reverse[key]
except KeyError:
import re
m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key)
if m:
return ('CharField', {'max_length': int(m.group(1))})
size = get_field_size(key)
if size is not None:
return ('CharField', {'max_length': size})
raise KeyError
class DatabaseIntrospection(BaseDatabaseIntrospection):
@ -53,7 +60,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
def get_table_description(self, cursor, table_name):
"Returns a description of the table, with the DB-API cursor.description interface."
return [(info['name'], info['type'], None, None, None, None,
return [(info['name'], info['type'], None, info['size'], None, None,
info['null_ok']) for info in self._table_info(cursor, table_name)]
def get_relations(self, cursor, table_name):
@ -171,6 +178,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
# cid, name, type, notnull, dflt_value, pk
return [{'name': field[1],
'type': field[2],
'size': get_field_size(field[2]),
'null_ok': not field[3],
'pk': field[5] # undocumented
} for field in cursor.fetchall()]

View File

@ -6,7 +6,7 @@ import hashlib
from time import time
from django.conf import settings
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
from django.utils.log import getLogger
from django.utils.timezone import utc
@ -138,7 +138,7 @@ def truncate_name(name, length=None, hash_len=4):
if length is None or len(name) <= length:
return name
hsh = hashlib.md5(smart_bytes(name)).hexdigest()[:hash_len]
hsh = hashlib.md5(force_bytes(name)).hexdigest()[:hash_len]
return '%s%s' % (name[:length-hash_len], hsh)
def format_number(value, max_digits, decimal_places):

View File

@ -23,7 +23,7 @@ from django.db.models import signals
from django.db.models.loading import register_models, get_model
from django.utils.translation import ugettext_lazy as _
from django.utils.functional import curry
from django.utils.encoding import smart_str, force_text
from django.utils.encoding import force_str, force_text
from django.utils import six
from django.utils.text import get_text_list, capfirst
@ -407,7 +407,7 @@ class Model(six.with_metaclass(ModelBase, object)):
u = six.text_type(self)
except (UnicodeEncodeError, UnicodeDecodeError):
u = '[Bad Unicode data]'
return smart_str('<%s: %s>' % (self.__class__.__name__, u))
return force_str('<%s: %s>' % (self.__class__.__name__, u))
def __str__(self):
if not six.PY3 and hasattr(self, '__unicode__'):

View File

@ -1047,13 +1047,14 @@ class GenericIPAddressField(Field):
description = _("IP address")
default_error_messages = {}
def __init__(self, protocol='both', unpack_ipv4=False, *args, **kwargs):
def __init__(self, verbose_name=None, name=None, protocol='both',
unpack_ipv4=False, *args, **kwargs):
self.unpack_ipv4 = unpack_ipv4
self.default_validators, invalid_error_message = \
validators.ip_address_validators(protocol, unpack_ipv4)
self.default_error_messages['invalid'] = invalid_error_message
kwargs['max_length'] = 39
Field.__init__(self, *args, **kwargs)
Field.__init__(self, verbose_name, name, *args, **kwargs)
def get_internal_type(self):
return "GenericIPAddressField"

View File

@ -8,7 +8,7 @@ from django.core.files.base import File
from django.core.files.storage import default_storage
from django.core.files.images import ImageFile
from django.db.models import signals
from django.utils.encoding import force_text, smart_str
from django.utils.encoding import force_str, force_text
from django.utils import six
from django.utils.translation import ugettext_lazy as _
@ -280,7 +280,7 @@ class FileField(Field):
setattr(cls, self.name, self.descriptor_class(self))
def get_directory_name(self):
return os.path.normpath(force_text(datetime.datetime.now().strftime(smart_str(self.upload_to))))
return os.path.normpath(force_text(datetime.datetime.now().strftime(force_str(self.upload_to))))
def get_filename(self, filename):
return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))

View File

@ -498,9 +498,7 @@ class QuerySet(object):
"Cannot use 'limit' or 'offset' with in_bulk"
if not id_list:
return {}
qs = self._clone()
qs.query.add_filter(('pk__in', id_list))
qs.query.clear_ordering(force_empty=True)
qs = self.filter(pk__in=id_list).order_by()
return dict([(obj._get_pk_val(), obj) for obj in qs])
def delete(self):

View File

@ -470,9 +470,7 @@ class SQLCompiler(object):
# Must use left outer joins for nullable fields and their relations.
# Ordering or distinct must not affect the returned set, and INNER
# JOINS for nullable fields could do this.
if joins_to_promote:
self.query.promote_alias_chain(joins_to_promote,
self.query.alias_map[joins_to_promote[0]].join_type == self.query.LOUTER)
self.query.promote_joins(joins_to_promote)
return field, col, alias, joins, opts
def _final_join_removal(self, col, alias):
@ -645,8 +643,6 @@ class SQLCompiler(object):
alias_chain.append(alias)
for (dupe_opts, dupe_col) in dupe_set:
self.query.update_dupe_avoidance(dupe_opts, dupe_col, alias)
if self.query.alias_map[root_alias].join_type == self.query.LOUTER:
self.query.promote_alias_chain(alias_chain, True)
else:
alias = root_alias
@ -663,8 +659,6 @@ class SQLCompiler(object):
columns, aliases = self.get_default_columns(start_alias=alias,
opts=f.rel.to._meta, as_pairs=True)
self.query.related_select_cols.extend(columns)
if self.query.alias_map[alias].join_type == self.query.LOUTER:
self.query.promote_alias_chain(aliases, True)
self.query.related_select_fields.extend(f.rel.to._meta.fields)
if restricted:
next = requested.get(f.name, {})
@ -738,7 +732,9 @@ class SQLCompiler(object):
self.query.related_select_fields.extend(model._meta.fields)
next = requested.get(f.related_query_name(), {})
new_nullable = f.null or None
# Use True here because we are looking at the _reverse_ side of
# the relation, which is always nullable.
new_nullable = True
self.fill_related_selections(model._meta, table, cur_depth+1,
used, next, restricted, new_nullable)

View File

@ -1,7 +1,7 @@
from collections import namedtuple
import re
# Valid query types (a dictionary is used for speedy lookups).
# Valid query types (a set is used for speedy lookups).
QUERY_TERMS = set([
'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in',
'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year',

View File

@ -505,7 +505,7 @@ class Query(object):
# Again, some of the tables won't have aliases due to
# the trimming of unnecessary tables.
if self.alias_refcount.get(alias) or rhs.alias_refcount.get(alias):
self.promote_alias(alias, True)
self.promote_joins([alias], True)
# Now relabel a copy of the rhs where-clause and add it to the current
# one.
@ -682,32 +682,38 @@ class Query(object):
""" Decreases the reference count for this alias. """
self.alias_refcount[alias] -= amount
def promote_alias(self, alias, unconditional=False):
def promote_joins(self, aliases, unconditional=False):
"""
Promotes the join type of an alias to an outer join if it's possible
for the join to contain NULL values on the left. If 'unconditional' is
False, the join is only promoted if it is nullable, otherwise it is
always promoted.
Promotes recursively the join type of given aliases and its children to
an outer join. If 'unconditional' is False, the join is only promoted if
it is nullable or the parent join is an outer join.
Returns True if the join was promoted by this call.
Note about join promotion: When promoting any alias, we make sure all
joins which start from that alias are promoted, too. When adding a join
in join(), we make sure any join added to already existing LOUTER join
is generated as LOUTER. This ensures we don't ever have broken join
chains which contain first a LOUTER join, then an INNER JOIN, that is
this kind of join should never be generated: a LOUTER b INNER c. The
reason for avoiding this type of join chain is that the INNER after
the LOUTER will effectively remove any effect the LOUTER had.
"""
if ((unconditional or self.alias_map[alias].nullable) and
self.alias_map[alias].join_type != self.LOUTER):
data = self.alias_map[alias]
data = data._replace(join_type=self.LOUTER)
self.alias_map[alias] = data
return True
return False
def promote_alias_chain(self, chain, must_promote=False):
"""
Walks along a chain of aliases, promoting the first nullable join and
any joins following that. If 'must_promote' is True, all the aliases in
the chain are promoted.
"""
for alias in chain:
if self.promote_alias(alias, must_promote):
must_promote = True
aliases = list(aliases)
while aliases:
alias = aliases.pop(0)
parent_alias = self.alias_map[alias].lhs_alias
parent_louter = (parent_alias
and self.alias_map[parent_alias].join_type == self.LOUTER)
already_louter = self.alias_map[alias].join_type == self.LOUTER
if ((unconditional or self.alias_map[alias].nullable
or parent_louter) and not already_louter):
data = self.alias_map[alias]._replace(join_type=self.LOUTER)
self.alias_map[alias] = data
# Join type of 'alias' changed, so re-examine all aliases that
# refer to this one.
aliases.extend(
join for join in self.alias_map.keys()
if (self.alias_map[join].lhs_alias == alias
and join not in aliases))
def reset_refcounts(self, to_counts):
"""
@ -726,19 +732,10 @@ class Query(object):
then and which ones haven't been used and promotes all of those
aliases, plus any children of theirs in the alias tree, to outer joins.
"""
# FIXME: There's some (a lot of!) overlap with the similar OR promotion
# in add_filter(). It's not quite identical, but is very similar. So
# pulling out the common bits is something for later.
considered = {}
for alias in self.tables:
if alias not in used_aliases:
continue
if (alias not in initial_refcounts or
if alias in used_aliases and (alias not in initial_refcounts or
self.alias_refcount[alias] == initial_refcounts[alias]):
parent = self.alias_map[alias].lhs_alias
must_promote = considered.get(parent, False)
promoted = self.promote_alias(alias, must_promote)
considered[alias] = must_promote or promoted
self.promote_joins([alias])
def change_aliases(self, change_map):
"""
@ -875,6 +872,9 @@ class Query(object):
LOUTER join type. This is used when joining certain types of querysets
and Q-objects together.
A join is always created as LOUTER if the lhs alias is LOUTER to make
sure we do not generate chains like a LOUTER b INNER c.
If 'nullable' is True, the join can potentially involve NULL values and
is a candidate for promotion (to "left outer") when combining querysets.
"""
@ -900,8 +900,8 @@ class Query(object):
if self.alias_map[alias].lhs_alias != lhs:
continue
self.ref_alias(alias)
if promote:
self.promote_alias(alias)
if promote or (lhs and self.alias_map[lhs].join_type == self.LOUTER):
self.promote_joins([alias])
return alias
# No reuse is possible, so we need a new alias.
@ -910,7 +910,12 @@ class Query(object):
# Not all tables need to be joined to anything. No join type
# means the later columns are ignored.
join_type = None
elif promote or outer_if_first:
elif (promote or outer_if_first
or self.alias_map[lhs].join_type == self.LOUTER):
# We need to use LOUTER join if asked by promote or outer_if_first,
# or if the LHS table is left-joined in the query. Adding inner join
# to an existing outer join effectively cancels the effect of the
# outer join.
join_type = self.LOUTER
else:
join_type = self.INNER
@ -1004,8 +1009,7 @@ class Query(object):
# If the aggregate references a model or field that requires a join,
# those joins must be LEFT OUTER - empty join rows must be returned
# in order for zeros to be returned for those aggregates.
for column_alias in join_list:
self.promote_alias(column_alias, unconditional=True)
self.promote_joins(join_list, True)
col = (join_list[-1], col)
else:
@ -1124,7 +1128,7 @@ class Query(object):
# If the comparison is against NULL, we may need to use some left
# outer joins when creating the join chain. This is only done when
# needed, as it's less efficient at the database level.
self.promote_alias_chain(join_list)
self.promote_joins(join_list)
join_promote = True
# Process the join list to see if we can remove any inner joins from
@ -1155,16 +1159,16 @@ class Query(object):
# This means that we are dealing with two different query
# subtrees, so we don't need to do any join promotion.
continue
join_promote = join_promote or self.promote_alias(join, unconditional)
join_promote = join_promote or self.promote_joins([join], unconditional)
if table != join:
table_promote = self.promote_alias(table)
table_promote = self.promote_joins([table])
# We only get here if we have found a table that exists
# in the join list, but isn't on the original tables list.
# This means we've reached the point where we only have
# new tables, so we can break out of this promotion loop.
break
self.promote_alias_chain(join_it, join_promote)
self.promote_alias_chain(table_it, table_promote or join_promote)
self.promote_joins(join_it, join_promote)
self.promote_joins(table_it, table_promote or join_promote)
if having_clause or force_having:
if (alias, col) not in self.group_by:
@ -1176,7 +1180,7 @@ class Query(object):
connector)
if negate:
self.promote_alias_chain(join_list)
self.promote_joins(join_list)
if lookup_type != 'isnull':
if len(join_list) > 1:
for alias in join_list:
@ -1650,7 +1654,7 @@ class Query(object):
final_alias = join.lhs_alias
col = join.lhs_join_col
joins = joins[:-1]
self.promote_alias_chain(joins[1:])
self.promote_joins(joins[1:])
self.select.append((final_alias, col))
self.select_fields.append(field)
except MultiJoin:

View File

@ -71,7 +71,8 @@ class BaseFormSet(object):
return True
__nonzero__ = __bool__ # Python 2
def _management_form(self):
@property
def management_form(self):
"""Returns the ManagementForm instance for this FormSet."""
if self.is_bound:
form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
@ -84,7 +85,6 @@ class BaseFormSet(object):
MAX_NUM_FORM_COUNT: self.max_num
})
return form
management_form = property(_management_form)
def total_form_count(self):
"""Returns the total number of forms in this FormSet."""
@ -140,17 +140,18 @@ class BaseFormSet(object):
self.add_fields(form, i)
return form
def _get_initial_forms(self):
@property
def initial_forms(self):
"""Return a list of all the initial forms in this formset."""
return self.forms[:self.initial_form_count()]
initial_forms = property(_get_initial_forms)
def _get_extra_forms(self):
@property
def extra_forms(self):
"""Return a list of all the extra forms in this formset."""
return self.forms[self.initial_form_count():]
extra_forms = property(_get_extra_forms)
def _get_empty_form(self, **kwargs):
@property
def empty_form(self, **kwargs):
defaults = {
'auto_id': self.auto_id,
'prefix': self.add_prefix('__prefix__'),
@ -160,19 +161,19 @@ class BaseFormSet(object):
form = self.form(**defaults)
self.add_fields(form, None)
return form
empty_form = property(_get_empty_form)
# Maybe this should just go away?
def _get_cleaned_data(self):
@property
def cleaned_data(self):
"""
Returns a list of form.cleaned_data dicts for every form in self.forms.
"""
if not self.is_valid():
raise AttributeError("'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__)
return [form.cleaned_data for form in self.forms]
cleaned_data = property(_get_cleaned_data)
def _get_deleted_forms(self):
@property
def deleted_forms(self):
"""
Returns a list of forms that have been marked for deletion. Raises an
AttributeError if deletion is not allowed.
@ -191,9 +192,9 @@ class BaseFormSet(object):
if self._should_delete_form(form):
self._deleted_form_indexes.append(i)
return [self.forms[i] for i in self._deleted_form_indexes]
deleted_forms = property(_get_deleted_forms)
def _get_ordered_forms(self):
@property
def ordered_forms(self):
"""
Returns a list of form in the order specified by the incoming data.
Raises an AttributeError if ordering is not allowed.
@ -228,7 +229,6 @@ class BaseFormSet(object):
# Return a list of form.cleaned_data dicts in the order specified by
# the form data.
return [self.forms[i[0]] for i in self._ordering]
ordered_forms = property(_get_ordered_forms)
@classmethod
def get_default_prefix(cls):
@ -244,23 +244,20 @@ class BaseFormSet(object):
return self._non_form_errors
return self.error_class()
def _get_errors(self):
@property
def errors(self):
"""
Returns a list of form.errors for every form in self.forms.
"""
if self._errors is None:
self.full_clean()
return self._errors
errors = property(_get_errors)
def _should_delete_form(self, form):
# The way we lookup the value of the deletion field here takes
# more code than we'd like, but the form's cleaned_data will
# not exist if the form is invalid.
field = form.fields[DELETION_FIELD_NAME]
raw_value = form._raw_value(DELETION_FIELD_NAME)
should_delete = field.clean(raw_value)
return should_delete
"""
Returns whether or not the form was marked for deletion.
"""
return form.cleaned_data.get(DELETION_FIELD_NAME, False)
def is_valid(self):
"""
@ -335,14 +332,14 @@ class BaseFormSet(object):
"""
return self.forms and self.forms[0].is_multipart()
def _get_media(self):
@property
def media(self):
# All the forms on a FormSet are the same, so you only need to
# interrogate the first form for media.
if self.forms:
return self.forms[0].media
else:
return Media()
media = property(_get_media)
def as_table(self):
"Returns this formset rendered as HTML <tr>s -- excluding the <table></table>."

View File

@ -18,7 +18,6 @@ from django.utils.datastructures import SortedDict
from django.utils import six
from django.utils.text import get_text_list, capfirst
from django.utils.translation import ugettext_lazy as _, ugettext
from django.utils import six
__all__ = (
@ -592,6 +591,10 @@ class BaseModelFormSet(BaseFormSet):
return []
saved_instances = []
try:
forms_to_delete = self.deleted_forms
except AttributeError:
forms_to_delete = []
for form in self.initial_forms:
pk_name = self._pk_field.name
raw_pk_value = form._raw_value(pk_name)
@ -602,7 +605,7 @@ class BaseModelFormSet(BaseFormSet):
pk_value = getattr(pk_value, 'pk', pk_value)
obj = self._existing_object(pk_value)
if self.can_delete and self._should_delete_form(form):
if form in forms_to_delete:
self.deleted_objects.append(obj)
obj.delete()
continue

View File

@ -61,14 +61,14 @@ else:
if not _cookie_allows_colon_in_names:
def load(self, rawdata):
self.bad_cookies = set()
super(SimpleCookie, self).load(smart_str(rawdata))
super(SimpleCookie, self).load(force_str(rawdata))
for key in self.bad_cookies:
del self[key]
# override private __set() method:
# (needed for using our Morsel, and for laxness with CookieError
def _BaseCookie__set(self, key, real_value, coded_value):
key = smart_str(key)
key = force_str(key)
try:
M = self.get(key, Morsel())
M.set(key, real_value, coded_value)
@ -85,7 +85,7 @@ from django.core.files import uploadhandler
from django.http.multipartparser import MultiPartParser
from django.http.utils import *
from django.utils.datastructures import MultiValueDict, ImmutableList
from django.utils.encoding import smart_bytes, smart_str, iri_to_uri, force_text
from django.utils.encoding import force_bytes, force_str, force_text, iri_to_uri
from django.utils.http import cookie_date
from django.utils import six
from django.utils import timezone
@ -137,7 +137,7 @@ def build_request_repr(request, path_override=None, GET_override=None,
except Exception:
meta = '<could not parse>'
path = path_override if path_override is not None else request.path
return smart_str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' %
return force_str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' %
(request.__class__.__name__,
path,
six.text_type(get),
@ -243,7 +243,12 @@ class HttpRequest(object):
def is_ajax(self):
return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
def _set_encoding(self, val):
@property
def encoding(self):
return self._encoding
@encoding.setter
def encoding(self, val):
"""
Sets the encoding used for GET/POST accesses. If the GET or POST
dictionary has already been created, it is removed and recreated on the
@ -255,27 +260,22 @@ class HttpRequest(object):
if hasattr(self, '_post'):
del self._post
def _get_encoding(self):
return self._encoding
encoding = property(_get_encoding, _set_encoding)
def _initialize_handlers(self):
self._upload_handlers = [uploadhandler.load_handler(handler, self)
for handler in settings.FILE_UPLOAD_HANDLERS]
def _set_upload_handlers(self, upload_handlers):
if hasattr(self, '_files'):
raise AttributeError("You cannot set the upload handlers after the upload has been processed.")
self._upload_handlers = upload_handlers
def _get_upload_handlers(self):
@property
def upload_handlers(self):
if not self._upload_handlers:
# If there are no upload handlers defined, initialize them from settings.
self._initialize_handlers()
return self._upload_handlers
upload_handlers = property(_get_upload_handlers, _set_upload_handlers)
@upload_handlers.setter
def upload_handlers(self, upload_handlers):
if hasattr(self, '_files'):
raise AttributeError("You cannot set the upload handlers after the upload has been processed.")
self._upload_handlers = upload_handlers
def parse_file_upload(self, META, post_data):
"""Returns a tuple of (POST QueryDict, FILES MultiValueDict)."""
@ -397,16 +397,16 @@ class QueryDict(MultiValueDict):
force_text(value, encoding, errors='replace'))
self._mutable = mutable
def _get_encoding(self):
@property
def encoding(self):
if self._encoding is None:
self._encoding = settings.DEFAULT_CHARSET
return self._encoding
def _set_encoding(self, value):
@encoding.setter
def encoding(self, value):
self._encoding = value
encoding = property(_get_encoding, _set_encoding)
def _assert_mutable(self):
if not self._mutable:
raise AttributeError("This QueryDict instance is immutable")
@ -489,13 +489,13 @@ class QueryDict(MultiValueDict):
"""
output = []
if safe:
safe = smart_bytes(safe, self.encoding)
safe = force_bytes(safe, self.encoding)
encode = lambda k, v: '%s=%s' % ((quote(k, safe), quote(v, safe)))
else:
encode = lambda k, v: urlencode({k: v})
for k, list_ in self.lists():
k = smart_bytes(k, self.encoding)
output.extend([encode(k, smart_bytes(v, self.encoding))
k = force_bytes(k, self.encoding)
output.extend([encode(k, force_bytes(v, self.encoding))
for v in list_])
return '&'.join(output)
@ -539,7 +539,7 @@ class HttpResponse(object):
if not content_type:
content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE,
self._charset)
# content is a bytestring. See _get_content / _set_content.
# content is a bytestring. See the content property methods.
self.content = content
self.cookies = SimpleCookie()
if status:
@ -669,7 +669,8 @@ class HttpResponse(object):
self.set_cookie(key, max_age=0, path=path, domain=domain,
expires='Thu, 01-Jan-1970 00:00:00 GMT')
def _get_content(self):
@property
def content(self):
if self.has_header('Content-Encoding'):
def make_bytes(value):
if isinstance(value, int):
@ -679,9 +680,10 @@ class HttpResponse(object):
# force conversion to bytes in case chunk is a subclass
return bytes(value)
return b''.join(make_bytes(e) for e in self._container)
return b''.join(smart_bytes(e, self._charset) for e in self._container)
return b''.join(force_bytes(e, self._charset) for e in self._container)
def _set_content(self, value):
@content.setter
def content(self, value):
if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)):
self._container = value
self._base_content_is_iter = True
@ -689,8 +691,6 @@ class HttpResponse(object):
self._container = [value]
self._base_content_is_iter = False
content = property(_get_content, _set_content)
def __iter__(self):
self._iterator = iter(self._container)
return self
@ -728,11 +728,11 @@ class HttpResponse(object):
class HttpResponseRedirectBase(HttpResponse):
allowed_schemes = ['http', 'https', 'ftp']
def __init__(self, redirect_to):
def __init__(self, redirect_to, *args, **kwargs):
parsed = urlparse(redirect_to)
if parsed.scheme and parsed.scheme not in self.allowed_schemes:
raise SuspiciousOperation("Unsafe redirect to URL with protocol '%s'" % parsed.scheme)
super(HttpResponseRedirectBase, self).__init__()
super(HttpResponseRedirectBase, self).__init__(*args, **kwargs)
self['Location'] = iri_to_uri(redirect_to)
class HttpResponseRedirect(HttpResponseRedirectBase):
@ -744,6 +744,16 @@ class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
class HttpResponseNotModified(HttpResponse):
status_code = 304
def __init__(self, *args, **kwargs):
super(HttpResponseNotModified, self).__init__(*args, **kwargs)
del self['content-type']
@HttpResponse.content.setter
def content(self, value):
if value:
raise AttributeError("You cannot set content to a 304 (Not Modified) response")
self._container = []
class HttpResponseBadRequest(HttpResponse):
status_code = 400
@ -756,8 +766,8 @@ class HttpResponseForbidden(HttpResponse):
class HttpResponseNotAllowed(HttpResponse):
status_code = 405
def __init__(self, permitted_methods):
super(HttpResponseNotAllowed, self).__init__()
def __init__(self, permitted_methods, *args, **kwargs):
super(HttpResponseNotAllowed, self).__init__(*args, **kwargs)
self['Allow'] = ', '.join(permitted_methods)
class HttpResponseGone(HttpResponse):

View File

@ -11,7 +11,7 @@ from django.utils.importlib import import_module
from django.utils.itercompat import is_iterable
from django.utils.text import (smart_split, unescape_string_literal,
get_text_list)
from django.utils.encoding import smart_text, force_text, smart_str
from django.utils.encoding import force_str, force_text
from django.utils.translation import ugettext_lazy, pgettext_lazy
from django.utils.safestring import (SafeData, EscapeData, mark_safe,
mark_for_escaping)
@ -116,7 +116,7 @@ class Template(object):
def __init__(self, template_string, origin=None,
name='<Unknown Template>'):
try:
template_string = smart_text(template_string)
template_string = force_text(template_string)
except UnicodeDecodeError:
raise TemplateEncodingError("Templates can only be constructed "
"from unicode or UTF-8 strings.")
@ -848,7 +848,7 @@ class TextNode(Node):
self.s = s
def __repr__(self):
return "<Text Node: '%s'>" % smart_str(self.s[:25], 'ascii',
return force_str("<Text Node: '%s'>" % self.s[:25], 'ascii',
errors='replace')
def render(self, context):

View File

@ -102,7 +102,7 @@ class SimpleTemplateResponse(HttpResponse):
"""
retval = self
if not self._is_rendered:
self._set_content(self.rendered_content)
self.content = self.rendered_content
for post_callback in self._post_render_callbacks:
newretval = post_callback(retval)
if newretval is not None:
@ -119,20 +119,20 @@ class SimpleTemplateResponse(HttpResponse):
'rendered before it can be iterated over.')
return super(SimpleTemplateResponse, self).__iter__()
def _get_content(self):
@property
def content(self):
if not self._is_rendered:
raise ContentNotRenderedError('The response content must be '
'rendered before it can be accessed.')
return super(SimpleTemplateResponse, self)._get_content()
return super(SimpleTemplateResponse, self).content
def _set_content(self, value):
@content.setter
def content(self, value):
"""Sets the content for the response
"""
super(SimpleTemplateResponse, self)._set_content(value)
HttpResponse.content.fset(self, value)
self._is_rendered = True
content = property(_get_content, _set_content)
class TemplateResponse(SimpleTemplateResponse):
rendering_attrs = SimpleTemplateResponse.rendering_attrs + \

View File

@ -4,7 +4,7 @@ import hashlib
from django.template import Library, Node, TemplateSyntaxError, Variable, VariableDoesNotExist
from django.template import resolve_variable
from django.core.cache import cache
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
from django.utils.http import urlquote
register = Library()
@ -26,8 +26,8 @@ class CacheNode(Node):
except (ValueError, TypeError):
raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time)
# Build a key for this fragment and all vary-on's.
key = smart_bytes(':'.join([urlquote(resolve_variable(var, context)) for var in self.vary_on]))
args = hashlib.md5(key)
key = ':'.join([urlquote(resolve_variable(var, context)) for var in self.vary_on])
args = hashlib.md5(force_bytes(key))
cache_key = 'template.cache.%s.%s' % (self.fragment_name, args.hexdigest())
value = cache.get(cache_key)
if value is None:

View File

@ -21,7 +21,7 @@ from django.http import SimpleCookie, HttpRequest, QueryDict
from django.template import TemplateDoesNotExist
from django.test import signals
from django.utils.functional import curry
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
from django.utils.http import urlencode
from django.utils.importlib import import_module
from django.utils.itercompat import is_iterable
@ -110,7 +110,7 @@ def encode_multipart(boundary, data):
as an application/octet-stream; otherwise, str(value) will be sent.
"""
lines = []
to_bytes = lambda s: smart_bytes(s, settings.DEFAULT_CHARSET)
to_bytes = lambda s: force_bytes(s, settings.DEFAULT_CHARSET)
# Not by any means perfect, but good enough for our purposes.
is_file = lambda thing: hasattr(thing, "read") and callable(thing.read)
@ -147,7 +147,7 @@ def encode_multipart(boundary, data):
return b'\r\n'.join(lines)
def encode_file(boundary, key, file):
to_bytes = lambda s: smart_bytes(s, settings.DEFAULT_CHARSET)
to_bytes = lambda s: force_bytes(s, settings.DEFAULT_CHARSET)
content_type = mimetypes.guess_type(file.name)[0]
if content_type is None:
content_type = 'application/octet-stream'
@ -222,7 +222,7 @@ class RequestFactory(object):
charset = match.group(1)
else:
charset = settings.DEFAULT_CHARSET
return smart_bytes(data, encoding=charset)
return force_bytes(data, encoding=charset)
def _get_path(self, parsed):
# If there are parameters, add them
@ -293,7 +293,7 @@ class RequestFactory(object):
def generic(self, method, path,
data='', content_type='application/octet-stream', **extra):
parsed = urlparse(path)
data = smart_bytes(data, settings.DEFAULT_CHARSET)
data = force_bytes(data, settings.DEFAULT_CHARSET)
r = {
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': parsed[4],

View File

@ -4,7 +4,6 @@ import time
from django.conf import settings
from django.db import connections
from django.dispatch import receiver, Signal
from django.template import context
from django.utils import timezone
template_rendered = Signal(providing_args=["template", "context"])
@ -48,9 +47,17 @@ def update_connections_time_zone(**kwargs):
@receiver(setting_changed)
def clear_context_processors_cache(**kwargs):
if kwargs['setting'] == 'TEMPLATE_CONTEXT_PROCESSORS':
from django.template import context
context._standard_context_processors = None
@receiver(setting_changed)
def clear_serializers_cache(**kwargs):
if kwargs['setting'] == 'SERIALIZATION_MODULES':
from django.core import serializers
serializers._serializers = {}
@receiver(setting_changed)
def language_changed(**kwargs):
if kwargs['setting'] in ('LOCALE_PATHS', 'LANGUAGE_CODE'):

View File

@ -41,7 +41,7 @@ from django.test.utils import (get_warnings_state, restore_warnings_state,
override_settings)
from django.test.utils import ContextList
from django.utils import unittest as ut2
from django.utils.encoding import smart_bytes, force_text
from django.utils.encoding import force_text
from django.utils import six
from django.utils.unittest.util import safe_repr
from django.views.static import serve

View File

@ -24,7 +24,7 @@ import time
from django.conf import settings
from django.core.cache import get_cache
from django.utils.encoding import iri_to_uri, force_text, smart_bytes
from django.utils.encoding import iri_to_uri, force_bytes, force_text
from django.utils.http import http_date
from django.utils.timezone import get_current_timezone_name
from django.utils.translation import get_language
@ -181,14 +181,14 @@ def _generate_cache_key(request, method, headerlist, key_prefix):
value = request.META.get(header, None)
if value is not None:
ctx.update(value)
path = hashlib.md5(smart_bytes(iri_to_uri(request.get_full_path())))
path = hashlib.md5(force_bytes(iri_to_uri(request.get_full_path())))
cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
key_prefix, method, path.hexdigest(), ctx.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)
def _generate_cache_header_key(key_prefix, request):
"""Returns a cache key for the header cache."""
path = hashlib.md5(smart_bytes(iri_to_uri(request.get_full_path())))
path = hashlib.md5(force_bytes(iri_to_uri(request.get_full_path())))
cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
key_prefix, path.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)

View File

@ -23,7 +23,8 @@ except NotImplementedError:
using_sysrandom = False
from django.conf import settings
from django.utils.encoding import smart_bytes
from django.utils.encoding import force_bytes
from django.utils import six
from django.utils.six.moves import xrange
@ -50,7 +51,7 @@ def salted_hmac(key_salt, value, secret=None):
# line is redundant and could be replaced by key = key_salt + secret, since
# the hmac module does the same thing for keys longer than the block size.
# However, we need to ensure that we *always* do this.
return hmac.new(key, msg=smart_bytes(value), digestmod=hashlib.sha1)
return hmac.new(key, msg=force_bytes(value), digestmod=hashlib.sha1)
def get_random_string(length=12,
@ -88,8 +89,12 @@ def constant_time_compare(val1, val2):
if len(val1) != len(val2):
return False
result = 0
for x, y in zip(val1, val2):
result |= ord(x) ^ ord(y)
if six.PY3 and isinstance(val1, bytes) and isinstance(val2, bytes):
for x, y in zip(val1, val2):
result |= x ^ y
else:
for x, y in zip(val1, val2):
result |= ord(x) ^ ord(y)
return result == 0
@ -142,8 +147,8 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None):
assert iterations > 0
if not digest:
digest = hashlib.sha256
password = smart_bytes(password)
salt = smart_bytes(salt)
password = force_bytes(password)
salt = force_bytes(salt)
hlen = digest().digest_size
if not dklen:
dklen = hlen

View File

@ -174,7 +174,7 @@ def force_bytes(s, encoding='utf-8', strings_only=False, errors='strict'):
# An Exception subclass containing non-ASCII data that doesn't
# know how to print itself properly. We shouldn't raise a
# further exception.
return ' '.join([smart_bytes(arg, encoding, strings_only,
return b' '.join([force_bytes(arg, encoding, strings_only,
errors) for arg in s])
return six.text_type(s).encode(encoding, errors)
else:
@ -225,7 +225,7 @@ def iri_to_uri(iri):
# converted.
if iri is None:
return iri
return quote(smart_bytes(iri), safe=b"/#%[]=:;$&()+,!?*@'~")
return quote(force_bytes(iri), safe=b"/#%[]=:;$&()+,!?*@'~")
def filepath_to_uri(path):
"""Convert an file system path to a URI portion that is suitable for
@ -244,7 +244,7 @@ def filepath_to_uri(path):
return path
# I know about `os.sep` and `os.altsep` but I want to leave
# some flexibility for hardcoding separators.
return quote(smart_bytes(path.replace("\\", "/")), safe=b"/~!*()'")
return quote(force_bytes(path.replace("\\", "/")), safe=b"/~!*()'")
# The encoding of the default system locale but falls back to the
# given fallback encoding if the encoding is unsupported by python or could

View File

@ -4,7 +4,7 @@ import datetime
from django.conf import settings
from django.utils import dateformat, numberformat, datetime_safe
from django.utils.importlib import import_module
from django.utils.encoding import smart_str
from django.utils.encoding import force_str
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils import six
@ -66,7 +66,7 @@ def get_format(format_type, lang=None, use_l10n=None):
If use_l10n is provided and is not None, that will force the value to
be localized (or not), overriding the value of settings.USE_L10N.
"""
format_type = smart_str(format_type)
format_type = force_str(format_type)
if use_l10n or (use_l10n is None and settings.USE_L10N):
if lang is None:
lang = get_language()
@ -160,14 +160,14 @@ def localize_input(value, default=None):
return number_format(value)
elif isinstance(value, datetime.datetime):
value = datetime_safe.new_datetime(value)
format = smart_str(default or get_format('DATETIME_INPUT_FORMATS')[0])
format = force_str(default or get_format('DATETIME_INPUT_FORMATS')[0])
return value.strftime(format)
elif isinstance(value, datetime.date):
value = datetime_safe.new_date(value)
format = smart_str(default or get_format('DATE_INPUT_FORMATS')[0])
format = force_str(default or get_format('DATE_INPUT_FORMATS')[0])
return value.strftime(format)
elif isinstance(value, datetime.time):
format = smart_str(default or get_format('TIME_INPUT_FORMATS')[0])
format = force_str(default or get_format('TIME_INPUT_FORMATS')[0])
return value.strftime(format)
return value

View File

@ -238,7 +238,6 @@ class LazyObject(object):
raise NotImplementedError
# introspection support:
__members__ = property(lambda self: self.__dir__())
__dir__ = new_method_proxy(dir)

View File

@ -11,7 +11,7 @@ except ImportError: # Python 2
from urlparse import urlsplit, urlunsplit
from django.utils.safestring import SafeData, mark_safe
from django.utils.encoding import smart_bytes, force_text
from django.utils.encoding import force_bytes, force_text
from django.utils.functional import allow_lazy
from django.utils import six
from django.utils.text import normalize_newlines
@ -164,7 +164,7 @@ def smart_urlquote(url):
# contains a % not followed by two hexadecimal digits. See #9655.
if '%' not in url or unquoted_percents_re.search(url):
# See http://bugs.python.org/issue2637
url = quote(smart_bytes(url), safe=b'!*\'();:@&=+$,/?#[]~')
url = quote(force_bytes(url), safe=b'!*\'();:@&=+$,/?#[]~')
return force_text(url)

View File

@ -15,7 +15,7 @@ except ImportError: # Python 2
from email.utils import formatdate
from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_text, smart_str
from django.utils.encoding import force_str, force_text
from django.utils.functional import allow_lazy
from django.utils import six
@ -39,7 +39,7 @@ def urlquote(url, safe='/'):
can safely be used as part of an argument to a subsequent iri_to_uri() call
without double-quoting occurring.
"""
return force_text(urllib_parse.quote(smart_str(url), smart_str(safe)))
return force_text(urllib_parse.quote(force_str(url), force_str(safe)))
urlquote = allow_lazy(urlquote, six.text_type)
def urlquote_plus(url, safe=''):
@ -49,7 +49,7 @@ def urlquote_plus(url, safe=''):
returned string can safely be used as part of an argument to a subsequent
iri_to_uri() call without double-quoting occurring.
"""
return force_text(urllib_parse.quote_plus(smart_str(url), smart_str(safe)))
return force_text(urllib_parse.quote_plus(force_str(url), force_str(safe)))
urlquote_plus = allow_lazy(urlquote_plus, six.text_type)
def urlunquote(quoted_url):
@ -57,7 +57,7 @@ def urlunquote(quoted_url):
A wrapper for Python's urllib.unquote() function that can operate on
the result of django.utils.http.urlquote().
"""
return force_text(urllib_parse.unquote(smart_str(quoted_url)))
return force_text(urllib_parse.unquote(force_str(quoted_url)))
urlunquote = allow_lazy(urlunquote, six.text_type)
def urlunquote_plus(quoted_url):
@ -65,7 +65,7 @@ def urlunquote_plus(quoted_url):
A wrapper for Python's urllib.unquote_plus() function that can operate on
the result of django.utils.http.urlquote_plus().
"""
return force_text(urllib_parse.unquote_plus(smart_str(quoted_url)))
return force_text(urllib_parse.unquote_plus(force_str(quoted_url)))
urlunquote_plus = allow_lazy(urlunquote_plus, six.text_type)
def urlencode(query, doseq=0):
@ -79,8 +79,8 @@ def urlencode(query, doseq=0):
elif hasattr(query, 'items'):
query = query.items()
return urllib_parse.urlencode(
[(smart_str(k),
[smart_str(i) for i in v] if isinstance(v, (list,tuple)) else smart_str(v))
[(force_str(k),
[force_str(i) for i in v] if isinstance(v, (list,tuple)) else force_str(v))
for k, v in query],
doseq)

View File

@ -5,7 +5,7 @@ import sys
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.1.0"
__version__ = "1.2.0"
# True if we are running on Python 3.
@ -26,19 +26,23 @@ else:
text_type = unicode
binary_type = str
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
def __len__(self):
return 1 << 31
try:
len(X())
except OverflowError:
# 32-bit
if sys.platform == "java":
# Jython always uses 32 bits.
MAXSIZE = int((1 << 31) - 1)
else:
# 64-bit
MAXSIZE = int((1 << 63) - 1)
del X
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
def __len__(self):
return 1 << 31
try:
len(X())
except OverflowError:
# 32-bit
MAXSIZE = int((1 << 31) - 1)
else:
# 64-bit
MAXSIZE = int((1 << 63) - 1)
del X
def _add_doc(func, doc):
@ -201,12 +205,19 @@ else:
_iteritems = "iteritems"
try:
advance_iterator = next
except NameError:
def advance_iterator(it):
return it.next()
next = advance_iterator
if PY3:
def get_unbound_function(unbound):
return unbound
advance_iterator = next
Iterator = object
def callable(obj):
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
@ -214,9 +225,10 @@ else:
def get_unbound_function(unbound):
return unbound.im_func
class Iterator(object):
def advance_iterator(it):
return it.next()
def next(self):
return type(self).__next__(self)
callable = callable
_add_doc(get_unbound_function,
@ -231,15 +243,15 @@ get_function_defaults = operator.attrgetter(_func_defaults)
def iterkeys(d):
"""Return an iterator over the keys of a dictionary."""
return getattr(d, _iterkeys)()
return iter(getattr(d, _iterkeys)())
def itervalues(d):
"""Return an iterator over the values of a dictionary."""
return getattr(d, _itervalues)()
return iter(getattr(d, _itervalues)())
def iteritems(d):
"""Return an iterator over the (key, value) pairs of a dictionary."""
return getattr(d, _iteritems)()
return iter(getattr(d, _iteritems)())
if PY3:

View File

@ -1,22 +1,22 @@
from __future__ import unicode_literals
import re
from django.utils import six
import unicodedata
import warnings
from gzip import GzipFile
from django.utils.six.moves import html_entities
from io import BytesIO
from django.utils.encoding import force_text
from django.utils.functional import allow_lazy, SimpleLazyObject
from django.utils import six
from django.utils.six.moves import html_entities
from django.utils.translation import ugettext_lazy, ugettext as _, pgettext
from django.utils.safestring import mark_safe
if not six.PY3:
# Import force_unicode even though this module doesn't use it, because some
# people rely on it being here.
from django.utils.encoding import force_unicode
from django.utils.functional import allow_lazy, SimpleLazyObject
from django.utils import six
from django.utils.translation import ugettext_lazy, ugettext as _, pgettext
from django.utils.safestring import mark_safe
# Capitalizes the first letter of a string.
capfirst = lambda x: x and force_text(x)[0].upper() + force_text(x)[1:]

View File

@ -9,7 +9,7 @@ import gettext as gettext_module
from threading import local
from django.utils.importlib import import_module
from django.utils.encoding import smart_str, smart_text
from django.utils.encoding import force_str, force_text
from django.utils.safestring import mark_safe, SafeData
from django.utils import six
from django.utils.six import StringIO
@ -454,7 +454,7 @@ def templatize(src, origin=None):
from django.conf import settings
from django.template import (Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK,
TOKEN_COMMENT, TRANSLATOR_COMMENT_MARK)
src = smart_text(src, settings.FILE_CHARSET)
src = force_text(src, settings.FILE_CHARSET)
out = StringIO()
message_context = None
intrans = False
@ -469,7 +469,7 @@ def templatize(src, origin=None):
content = ''.join(comment)
translators_comment_start = None
for lineno, line in enumerate(content.splitlines(True)):
if line.lstrip().startswith(smart_text(TRANSLATOR_COMMENT_MARK)):
if line.lstrip().startswith(TRANSLATOR_COMMENT_MARK):
translators_comment_start = lineno
for lineno, line in enumerate(content.splitlines(True)):
if translators_comment_start is not None and lineno >= translators_comment_start:
@ -584,7 +584,7 @@ def templatize(src, origin=None):
out.write(' # %s' % t.contents)
else:
out.write(blankout(t.contents, 'X'))
return smart_str(out.getvalue())
return force_str(out.getvalue())
def parse_accept_lang_header(lang_string):
"""

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import time
from datetime import timedelta, tzinfo
from django.utils.encoding import smart_text, smart_str, DEFAULT_LOCALE_ENCODING
from django.utils.encoding import force_str, force_text, DEFAULT_LOCALE_ENCODING
# Python's doc say: "A tzinfo subclass must have an __init__() method that can
# be called with no arguments". FixedOffset and LocalTimezone don't honor this
@ -53,7 +53,7 @@ class LocalTimezone(tzinfo):
self._tzname = self.tzname(dt)
def __repr__(self):
return smart_str(self._tzname)
return force_str(self._tzname)
def __getinitargs__(self):
return self.__dt,
@ -72,7 +72,7 @@ class LocalTimezone(tzinfo):
def tzname(self, dt):
try:
return smart_text(time.tzname[self._isdst(dt)],
return force_text(time.tzname[self._isdst(dt)],
DEFAULT_LOCALE_ENCODING)
except UnicodeDecodeError:
return None

View File

@ -14,7 +14,7 @@ from django.template import Template, Context, TemplateDoesNotExist
from django.template.defaultfilters import force_escape, pprint
from django.utils.html import escape
from django.utils.importlib import import_module
from django.utils.encoding import smart_text, smart_bytes
from django.utils.encoding import force_bytes, smart_text
from django.utils import six
HIDDEN_SETTINGS = re.compile('API|TOKEN|KEY|SECRET|PASS|PROFANITIES_LIST|SIGNATURE')
@ -440,7 +440,7 @@ def technical_404_response(request, exception):
'root_urlconf': settings.ROOT_URLCONF,
'request_path': request.path_info[1:], # Trim leading slash
'urlpatterns': tried,
'reason': smart_bytes(exception, errors='replace'),
'reason': force_bytes(exception, errors='replace'),
'request': request,
'settings': get_safe_settings(),
})

View File

@ -61,7 +61,7 @@ def serve(request, path, document_root=None, show_indexes=False):
mimetype = mimetype or 'application/octet-stream'
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
statobj.st_mtime, statobj.st_size):
return HttpResponseNotModified(content_type=mimetype)
return HttpResponseNotModified()
with open(fullpath, 'rb') as f:
response = HttpResponse(f.read(), content_type=mimetype)
response["Last-Modified"] = http_date(statobj.st_mtime)

View File

@ -191,7 +191,8 @@ modindex_common_prefix = ["django."]
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
'preamble': '\\DeclareUnicodeCharacter{2265}{\\ensuremath{\\ge}}'
'preamble': ('\\DeclareUnicodeCharacter{2264}{\\ensuremath{\\le}}'
'\\DeclareUnicodeCharacter{2265}{\\ensuremath{\\ge}}')
}
# Grouping the document tree into LaTeX files. List of tuples

View File

@ -185,6 +185,6 @@ would be happy to help you.
You might also be interested in posting a job to http://djangogigs.com/ .
If you want to find Django-capable people in your local area, try
http://djangopeople.net/ .
https://people.djangoproject.com/ .
.. _developers for hire page: https://code.djangoproject.com/wiki/DevelopersForHire

View File

@ -108,7 +108,8 @@ to know about views via the links below:
:doc:`Built-in display views <topics/class-based-views/generic-display>` |
:doc:`Built-in editing views <topics/class-based-views/generic-editing>` |
:doc:`Using mixins <topics/class-based-views/mixins>` |
:doc:`API reference <ref/class-based-views/index>`
:doc:`API reference <ref/class-based-views/index>` |
:doc:`Flattened index<ref/class-based-views/flattened-index>`
* **Advanced:**
:doc:`Generating CSV <howto/outputting-csv>` |

View File

@ -77,7 +77,7 @@ record of being helpful on the mailing lists, and a proven desire to dedicate
serious time to Django. In return, they've been granted the coveted commit bit,
and have free rein to hack on all parts of Django.
`Malcolm Tredinnick`_
Malcolm Tredinnick
Malcolm originally wanted to be a mathematician, somehow ended up a software
developer. He's contributed to many Open Source projects, has served on the
board of the GNOME foundation, and will kick your ass at chess.
@ -85,8 +85,6 @@ and have free rein to hack on all parts of Django.
When he's not busy being an International Man of Mystery, Malcolm lives in
Sydney, Australia.
.. _malcolm tredinnick: http://www.pointy-stick.com/
`Russell Keith-Magee`_
Russell studied physics as an undergraduate, and studied neural networks for
his PhD. His first job was with a startup in the defense industry developing

View File

@ -0,0 +1,556 @@
===========================================
Class-based generic views - flattened index
===========================================
This index provides an alternate organization of the reference documentation
for class-based views. For each view, the effective attributes and methods from
the class tree are represented under that view. For the reference
documentation organized by the class which defines the behavior, see
:doc:`Class-based views</ref/class-based-views/index>`
Simple generic views
--------------------
View
~~~~
.. class:: View()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.base.View.http_method_names`
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
TemplateView
~~~~~~~~~~~~
.. class:: TemplateView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.base.TemplateView.get`
* :meth:`~django.views.generic.base.TemplateView.get_context_data`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
RedirectView
~~~~~~~~~~~~
.. class:: RedirectView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.base.RedirectView.permanent`
* :attr:`~django.views.generic.base.RedirectView.query_string`
* :attr:`~django.views.generic.base.RedirectView.url`
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.RedirectView.delete`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.base.RedirectView.get`
* :meth:`~django.views.generic.base.RedirectView.get_redirect_url`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.base.RedirectView.options`
* :meth:`~django.views.generic.base.RedirectView.post`
* :meth:`~django.views.generic.base.RedirectView.put`
Detail Views
------------
DetailView
~~~~~~~~~~
.. class:: DetailView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.detail.BaseDetailView.get`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
List Views
----------
ListView
~~~~~~~~
.. class:: ListView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.list.BaseListView.get`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
Editing views
-------------
FormView
~~~~~~~~
.. class:: FormView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.edit.FormMixin.form_invalid`
* :meth:`~django.views.generic.edit.FormMixin.form_valid`
* :meth:`~django.views.generic.edit.ProcessFormView.get`
* :meth:`~django.views.generic.edit.FormMixin.get_context_data`
* :meth:`~django.views.generic.edit.FormMixin.get_form`
* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.edit.ProcessFormView.post`
* :meth:`~django.views.generic.edit.ProcessFormView.put`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
CreateView
~~~~~~~~~~
.. class:: CreateView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.edit.FormMixin.form_invalid`
* :meth:`~django.views.generic.edit.FormMixin.form_valid`
* :meth:`~django.views.generic.edit.ProcessFormView.get`
* :meth:`~django.views.generic.edit.FormMixin.get_context_data`
* :meth:`~django.views.generic.edit.FormMixin.get_form`
* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.edit.ProcessFormView.post`
* :meth:`~django.views.generic.edit.ProcessFormView.put`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
UpdateView
~~~~~~~~~~
.. class:: UpdateView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.edit.FormMixin.form_invalid`
* :meth:`~django.views.generic.edit.FormMixin.form_valid`
* :meth:`~django.views.generic.edit.ProcessFormView.get`
* :meth:`~django.views.generic.edit.FormMixin.get_context_data`
* :meth:`~django.views.generic.edit.FormMixin.get_form`
* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.edit.ProcessFormView.post`
* :meth:`~django.views.generic.edit.ProcessFormView.put`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
DeleteView
~~~~~~~~~~
.. class:: DeleteView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.DeletionMixin.success_url` [:meth:`~django.views.generic.edit.DeletionMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.edit.DeletionMixin.delete`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.detail.BaseDetailView.get`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.edit.DeletionMixin.post`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
Date-based views
----------------
ArchiveIndexView
~~~~~~~~~~~~~~~~
.. class:: ArchiveIndexView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.dates.BaseDateListView.get`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
YearArchiveView
~~~~~~~~~~~~~~~
.. class:: YearArchiveView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.dates.BaseYearArchiveView.make_object_list` [:meth:`~django.views.generic.dates.BaseYearArchiveView.get_make_object_list`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.dates.BaseDateListView.get`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
MonthArchiveView
~~~~~~~~~~~~~~~~
.. class:: MonthArchiveView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.dates.BaseDateListView.get`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.dates.MonthMixin.get_next_month`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
WeekArchiveView
~~~~~~~~~~~~~~~
.. class:: WeekArchiveView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.WeekMixin.week` [:meth:`~django.views.generic.dates.WeekMixin.get_week`]
* :attr:`~django.views.generic.dates.WeekMixin.week_format` [:meth:`~django.views.generic.dates.WeekMixin.get_week_format`]
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.dates.BaseDateListView.get`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
DayArchiveView
~~~~~~~~~~~~~~
.. class:: DayArchiveView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`]
* :attr:`~django.views.generic.dates.DayMixin.day_format` [:meth:`~django.views.generic.dates.DayMixin.get_day_format`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.dates.BaseDateListView.get`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.dates.DayMixin.get_next_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_next_month`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.dates.DayMixin.get_previous_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
TodayArchiveView
~~~~~~~~~~~~~~~~
.. class:: TodayArchiveView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`]
* :attr:`~django.views.generic.dates.DayMixin.day_format` [:meth:`~django.views.generic.dates.DayMixin.get_day_format`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.dates.BaseDateListView.get`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.dates.DayMixin.get_next_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_next_month`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.dates.DayMixin.get_previous_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
DateDetailView
~~~~~~~~~~~~~~
.. class:: DateDetailView()
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`]
* :attr:`~django.views.generic.dates.DayMixin.day_format` [:meth:`~django.views.generic.dates.DayMixin.get_day_format`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field`
* :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
* :attr:`~django.views.generic.dates.YearMixin.year_format` [:meth:`~django.views.generic.dates.YearMixin.get_year_format`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
* :meth:`~django.views.generic.detail.BaseDetailView.get`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.DayMixin.get_next_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_next_month`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
* :meth:`~django.views.generic.dates.DayMixin.get_previous_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month`
* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`

View File

@ -13,6 +13,7 @@ Class-based views API reference. For introductory material, see
generic-editing
generic-date-based
mixins
flattened-index
Specification
-------------

View File

@ -561,8 +561,6 @@ subclass::
.. attribute:: ModelAdmin.list_filter
.. versionchanged:: 1.4
Set ``list_filter`` to activate filters in the right sidebar of the change
list page of the admin, as illustrated in the following screenshot:
@ -586,6 +584,8 @@ subclass::
class PersonAdmin(UserAdmin):
list_filter = ('company__name',)
.. versionadded:: 1.4
* a class inheriting from :mod:`django.contrib.admin.SimpleListFilter`,
which you need to provide the ``title`` and ``parameter_name``
attributes to and override the ``lookups`` and ``queryset`` methods,
@ -671,6 +671,8 @@ subclass::
birthday__lte=date(1999, 12, 31)).exists():
yield ('90s', _('in the nineties'))
.. versionadded:: 1.4
* a tuple, where the first element is a field name and the second
element is a class inheriting from
:mod:`django.contrib.admin.FieldListFilter`, for example::

View File

@ -158,11 +158,13 @@ For example::
.. warning::
There's a known bug in Safari/Webkit which causes the named anchor to be
There's a `known bug`_ in Safari/Webkit which causes the named anchor to be
forgotten following a redirect. The practical impact for comments is that
the Safari/webkit browsers will arrive at the correct page but will not
scroll to the named anchor.
.. _`known bug`: https://bugs.webkit.org/show_bug.cgi?id=24175
.. templatetag:: get_comment_count
Counting comments

View File

@ -84,47 +84,94 @@ AJAX
While the above method can be used for AJAX POST requests, it has some
inconveniences: you have to remember to pass the CSRF token in as POST data with
every POST request. For this reason, there is an alternative method: on each
XMLHttpRequest, set a custom `X-CSRFToken` header to the value of the CSRF
XMLHttpRequest, set a custom ``X-CSRFToken`` header to the value of the CSRF
token. This is often easier, because many javascript frameworks provide hooks
that allow headers to be set on every request. In jQuery, you can use the
``ajaxSend`` event as follows:
that allow headers to be set on every request.
As a first step, you must get the CSRF token itself. The recommended source for
the token is the ``csrftoken`` cookie, which will be set if you've enabled CSRF
protection for your views as outlined above.
.. note::
The CSRF token cookie is named ``csrftoken`` by default, but you can control
the cookie name via the :setting:`CSRF_COOKIE_NAME` setting.
Acquiring the token is straightforward:
.. code-block:: javascript
jQuery(document).ajaxSend(function(event, xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
return cookieValue;
}
function sameOrigin(url) {
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
function safeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
The above code could be simplified by using the `jQuery cookie plugin
<http://plugins.jquery.com/project/Cookie>`_ to replace ``getCookie``:
.. code-block:: javascript
var csrftoken = $.cookie('csrftoken');
.. note::
The CSRF token is also present in the DOM, but only if explicitly included
using :ttag:`csrf_token` in a template. The cookie contains the canonical
token; the ``CsrfViewMiddleware`` will prefer the cookie to the token in
the DOM. Regardless, you're guaranteed to have the cookie if the token is
present in the DOM, so you should use the cookie!
.. warning::
If your view is not rendering a template containing the :ttag:`csrf_token`
template tag, Django might not set the CSRF token cookie. This is common in
cases where forms are dynamically added to the page. To address this case,
Django provides a view decorator which forces setting of the cookie:
:func:`~django.views.decorators.csrf.ensure_csrf_cookie`.
Finally, you'll have to actually set the header on your AJAX request, while
protecting the CSRF token from being sent to other domains.
.. code-block:: javascript
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
// test that a given url is a same-origin URL
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
// Send the token to same-origin, relative URLs only.
// Send the token only if the method warrants CSRF protection
// Using the CSRFToken value acquired earlier
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
@ -133,18 +180,32 @@ that allow headers to be set on every request. In jQuery, you can use the
Due to a bug introduced in jQuery 1.5, the example above will not work
correctly on that version. Make sure you are running at least jQuery 1.5.1.
Adding this to a javascript file that is included on your site will ensure that
AJAX POST requests that are made via jQuery will not be caught by the CSRF
protection.
You can use `settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in
jQuery 1.5 and newer in order to replace the `sameOrigin` logic above:
The above code could be simplified by using the `jQuery cookie plugin
<http://plugins.jquery.com/project/Cookie>`_ to replace ``getCookie``, and
`settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in jQuery 1.5 and
later to replace ``sameOrigin``.
.. code-block:: javascript
In addition, if the CSRF cookie has not been sent to the client by use of
:ttag:`csrf_token`, you may need to ensure the client receives the cookie by
using :func:`~django.views.decorators.csrf.ensure_csrf_cookie`.
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
crossDomain: false, // obviates need for sameOrigin test
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
.. note::
In a `security release blogpost`_, a simpler "same origin test" example
was provided which only checked for a relative URL. The ``sameOrigin``
test above supersedes that example—it works for edge cases like
scheme-relative or absolute URLs for the same domain.
.. _security release blogpost: https://www.djangoproject.com/weblog/2011/feb/08/security/
Other template engines
----------------------

View File

@ -42,6 +42,16 @@ To install the flatpages app, follow these steps:
2. Add ``'django.contrib.flatpages'`` to your :setting:`INSTALLED_APPS`
setting.
Then either:
3. Add an entry in your URLconf. For example::
urlpatterns = patterns('',
('^pages/', include('django.contrib.flatpages.urls')),
)
or:
3. Add ``'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'``
to your :setting:`MIDDLEWARE_CLASSES` setting.
@ -57,8 +67,38 @@ and ``django_flatpage_sites``. ``django_flatpage`` is a simple lookup table
that simply maps a URL to a title and bunch of text content.
``django_flatpage_sites`` associates a flatpage with a site.
Using the URLconf
-----------------
There are several ways to include the flat pages in your URLconf. You can
dedicate a particular path to flat pages::
urlpatterns = patterns('',
('^pages/', include('django.contrib.flatpages.urls')),
)
You can also set it up as a "catchall" pattern. In this case, it is important
to place the pattern at the end of the other urlpatterns::
# Your other patterns here
urlpatterns += patterns('django.contrib.flatpages.views',
(r'^(?P<url>.*)$', 'flatpage'),
)
Another common setup is to use flat pages for a limited set of known pages and
to hard code the urls, so you can reference them with the :ttag:`url` template
tag::
urlpatterns += patterns('django.contrib.flatpages.views',
url(r'^about-us/$', 'flatpage', {'url': '/about-us/'}, name='about'),
url(r'^license/$', 'flatpage', {'url': '/license/'}, name='license'),
)
Using the middleware
--------------------
The :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`
does all of the work.
can do all of the work.
.. class:: FlatpageFallbackMiddleware
@ -255,4 +295,3 @@ For example:
{% get_flatpages '/about/' as about_pages %}
{% get_flatpages about_prefix as about_pages %}
{% get_flatpages '/about/' for someuser as about_pages %}

View File

@ -582,7 +582,7 @@ Creating a spatial database for SpatiaLite
After you've installed SpatiaLite, you'll need to create a number of spatial
metadata tables in your database in order to perform spatial queries.
If you're using SpatiaLite 3.0 or newer, use the ``spatialite`` utility to
If you're using SpatiaLite 2.4 or newer, use the ``spatialite`` utility to
call the ``InitSpatialMetaData()`` function, like this::
$ spatialite geodjango.db "SELECT InitSpatialMetaData();"
@ -593,12 +593,10 @@ call the ``InitSpatialMetaData()`` function, like this::
You can safely ignore the error messages shown. When you've done this, you can
skip the rest of this section.
If you're using a version of SpatiaLite older than 3.0, you'll need to download
a database-initialization file and execute its SQL queries in your database.
If you're using SpatiaLite 2.3, you'll need to download a
database-initialization file and execute its SQL queries in your database.
First, get it from the appropriate SpatiaLite Resources page (
http://www.gaia-gis.it/spatialite-2.3.1/resources.html for 2.3 or
http://www.gaia-gis.it/spatialite-2.4.0/ for 2.4)::
First, get it from the `SpatiaLite Resources`__ page::
$ wget http://www.gaia-gis.it/spatialite-2.3.1/init_spatialite-2.3.sql.gz
$ gunzip init_spatialite-2.3.sql.gz
@ -613,6 +611,8 @@ Then, use the ``spatialite`` command to initialize a spatial database::
you want to use. Use the same in the :setting:`DATABASES` ``"name"`` key
inside your ``settings.py``.
__ http://www.gaia-gis.it/spatialite-2.3.1/resources.html
Add ``django.contrib.gis`` to :setting:`INSTALLED_APPS`
-------------------------------------------------------
@ -820,8 +820,10 @@ Download the framework packages for:
* GDAL
Install the packages in the order they are listed above, as the GDAL and SQLite
packages require the packages listed before them. Afterwards, you can also
install the KyngChaos binary packages for `PostgreSQL and PostGIS`__.
packages require the packages listed before them.
Afterwards, you can also install the KyngChaos binary packages for `PostgreSQL
and PostGIS`__.
After installing the binary packages, you'll want to add the following to
your ``.profile`` to be able to run the package programs from the command-line::

View File

@ -119,7 +119,7 @@ Settings
``SPATIALITE_SQL``
^^^^^^^^^^^^^^^^^^
Only relevant when using a SpatiaLite version older than 3.0.
Only relevant when using a SpatiaLite version 2.3.
By default, the GeoDjango test runner looks for the :ref:`file containing the
SpatiaLite dababase-initialization SQL code <create_spatialite_db>` in the

View File

@ -671,6 +671,17 @@ of abstraction::
__ http://spatialreference.org/ref/epsg/32140/
.. admonition:: Raw queries
When using :doc:`raw queries </topics/db/sql>`, you should generally wrap
your geometry fields with the ``asText()`` SQL function so as the field
value will be recognized by GEOS::
City.objects.raw('SELECT id, name, asText(point) from myapp_city')
This is not absolutely required by PostGIS, but generally you should only
use raw queries when you know exactly what you are doing.
Lazy Geometries
---------------
Geometries come to GeoDjango in a standardized textual representation. Upon

View File

@ -91,14 +91,18 @@ The ``ContentFile`` Class
.. class:: ContentFile(File)
The ``ContentFile`` class inherits from :class:`~django.core.files.File`,
but unlike :class:`~django.core.files.File` it operates on string content,
rather than an actual file. For example::
but unlike :class:`~django.core.files.File` it operates on string content
(bytes also supported), rather than an actual file. For example::
from __future__ import unicode_literals
from django.core.files.base import ContentFile
f1 = ContentFile(b"my string content")
f2 = ContentFile("my unicode content encoded as UTF-8".encode('UTF-8'))
f1 = ContentFile("esta sentencia está en español")
f2 = ContentFile(b"these are bytes")
.. versionchanged:: 1.5
ContentFile also accepts Unicode strings.
.. currentmodule:: django.core.files.images

View File

@ -70,8 +70,8 @@ If ``True``, the field is allowed to be blank. Default is ``False``.
Note that this is different than :attr:`~Field.null`. :attr:`~Field.null` is
purely database-related, whereas :attr:`~Field.blank` is validation-related. If
a field has ``blank=True``, validation on Django's admin site will allow entry
of an empty value. If a field has ``blank=False``, the field will be required.
a field has ``blank=True``, form validation will allow entry of an empty value.
If a field has ``blank=False``, the field will be required.
.. _field-choices:
@ -81,14 +81,11 @@ of an empty value. If a field has ``blank=False``, the field will be required.
.. attribute:: Field.choices
An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this
field.
field. If this is given, the default form widget will be a select box with
these choices instead of the standard text field.
If this is given, Django's admin will use a select box instead of the standard
text field and will limit choices to the choices given.
A choices list is an iterable of 2-tuples; the first element in each
tuple is the actual value to be stored, and the second element is the
human-readable name. For example::
The first element in each tuple is the actual value to be stored, and the
second element is the human-readable name. For example::
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
@ -176,7 +173,7 @@ scenes.
.. attribute:: Field.db_index
If ``True``, djadmin:`django-admin.py sqlindexes <sqlindexes>` will output a
If ``True``, :djadmin:`django-admin.py sqlindexes <sqlindexes>` will output a
``CREATE INDEX`` statement for this field.
``db_tablespace``
@ -203,8 +200,8 @@ callable it will be called every time a new object is created.
.. attribute:: Field.editable
If ``False``, the field will not be editable in the admin or via forms
automatically generated from the model class. Default is ``True``.
If ``False``, the field will not be displayed in the admin or any other
:class:`~django.forms.ModelForm`. Default is ``True``.
``error_messages``
------------------
@ -224,11 +221,11 @@ the `Field types`_ section below.
.. attribute:: Field.help_text
Extra "help" text to be displayed under the field on the object's admin form.
It's useful for documentation even if your object doesn't have an admin form.
Extra "help" text to be displayed with the form widget. It's useful for
documentation even if your field isn't used on a form.
Note that this value is *not* HTML-escaped when it's displayed in the admin
interface. This lets you include HTML in :attr:`~Field.help_text` if you so
Note that this value is *not* HTML-escaped in automatically-generated
forms. This lets you include HTML in :attr:`~Field.help_text` if you so
desire. For example::
help_text="Please use the following format: <em>YYYY-MM-DD</em>."
@ -259,7 +256,7 @@ Only one primary key is allowed on an object.
If ``True``, this field must be unique throughout the table.
This is enforced at the database level and at the Django admin-form level. If
This is enforced at the database level and by model validation. If
you try to save a model with a duplicate value in a :attr:`~Field.unique`
field, a :exc:`django.db.IntegrityError` will be raised by the model's
:meth:`~django.db.models.Model.save` method.
@ -279,7 +276,7 @@ For example, if you have a field ``title`` that has
``unique_for_date="pub_date"``, then Django wouldn't allow the entry of two
records with the same ``title`` and ``pub_date``.
This is enforced at the Django admin-form level but not at the database level.
This is enforced by model validation but not at the database level.
``unique_for_month``
--------------------
@ -337,7 +334,7 @@ otherwise. See :ref:`automatic-primary-key-fields`.
A 64 bit integer, much like an :class:`IntegerField` except that it is
guaranteed to fit numbers from -9223372036854775808 to 9223372036854775807. The
admin represents this as an ``<input type="text">`` (a single-line input).
default form widget for this field is a :class:`~django.forms.TextInput`.
``BooleanField``
@ -347,7 +344,8 @@ admin represents this as an ``<input type="text">`` (a single-line input).
A true/false field.
The admin represents this as a checkbox.
The default form widget for this field is a
:class:`~django.forms.CheckboxInput`.
If you need to accept :attr:`~Field.null` values then use
:class:`NullBooleanField` instead.
@ -361,7 +359,7 @@ A string field, for small- to large-sized strings.
For large amounts of text, use :class:`~django.db.models.TextField`.
The admin represents this as an ``<input type="text">`` (a single-line input).
The default form widget for this field is a :class:`~django.forms.TextInput`.
:class:`CharField` has one extra required argument:
@ -414,9 +412,10 @@ optional arguments:
for creation of timestamps. Note that the current date is *always* used;
it's not just a default value that you can override.
The admin represents this as an ``<input type="text">`` with a JavaScript
calendar, and a shortcut for "Today". Includes an additional ``invalid_date``
error message key.
The default form widget for this field is a
:class:`~django.forms.TextInput`. The admin adds a JavaScript calendar,
and a shortcut for "Today". Includes an additional ``invalid_date`` error
message key.
.. note::
As currently implemented, setting ``auto_now`` or ``auto_now_add`` to
@ -431,8 +430,9 @@ error message key.
A date and time, represented in Python by a ``datetime.datetime`` instance.
Takes the same extra arguments as :class:`DateField`.
The admin represents this as two ``<input type="text">`` fields, with
JavaScript shortcuts.
The default form widget for this field is a single
:class:`~django.forms.TextInput`. The admin uses two separate
:class:`~django.forms.TextInput` widgets with JavaScript shortcuts.
``DecimalField``
----------------
@ -461,7 +461,7 @@ decimal places::
models.DecimalField(..., max_digits=19, decimal_places=10)
The admin represents this as an ``<input type="text">`` (a single-line input).
The default form widget for this field is a :class:`~django.forms.TextInput`.
.. note::
@ -539,8 +539,8 @@ Also has one optional argument:
Optional. A storage object, which handles the storage and retrieval of your
files. See :doc:`/topics/files` for details on how to provide this object.
The admin represents this field as an ``<input type="file">`` (a file-upload
widget).
The default form widget for this field is a
:class:`~django.forms.widgets.FileInput`.
Using a :class:`FileField` or an :class:`ImageField` (see below) in a model
takes a few steps:
@ -725,7 +725,7 @@ can change the maximum length using the :attr:`~CharField.max_length` argument.
A floating-point number represented in Python by a ``float`` instance.
The admin represents this as an ``<input type="text">`` (a single-line input).
The default form widget for this field is a :class:`~django.forms.TextInput`.
.. _floatfield_vs_decimalfield:
@ -776,16 +776,16 @@ length using the :attr:`~CharField.max_length` argument.
.. class:: IntegerField([**options])
An integer. The admin represents this as an ``<input type="text">`` (a
single-line input).
An integer. The default form widget for this field is a
:class:`~django.forms.TextInput`.
``IPAddressField``
------------------
.. class:: IPAddressField([**options])
An IP address, in string format (e.g. "192.0.2.30"). The admin represents this
as an ``<input type="text">`` (a single-line input).
An IP address, in string format (e.g. "192.0.2.30"). The default form widget
for this field is a :class:`~django.forms.TextInput`.
``GenericIPAddressField``
-------------------------
@ -795,8 +795,8 @@ as an ``<input type="text">`` (a single-line input).
.. versionadded:: 1.4
An IPv4 or IPv6 address, in string format (e.g. ``192.0.2.30`` or
``2a02:42fe::4``). The admin represents this as an ``<input type="text">``
(a single-line input).
``2a02:42fe::4``). The default form widget for this field is a
:class:`~django.forms.TextInput`.
The IPv6 address normalization follows :rfc:`4291#section-2.2` section 2.2,
including using the IPv4 format suggested in paragraph 3 of that section, like
@ -823,8 +823,8 @@ are converted to lowercase.
.. class:: NullBooleanField([**options])
Like a :class:`BooleanField`, but allows ``NULL`` as one of the options. Use
this instead of a :class:`BooleanField` with ``null=True``. The admin represents
this as a ``<select>`` box with "Unknown", "Yes" and "No" choices.
this instead of a :class:`BooleanField` with ``null=True``. The default form
widget for this field is a :class:`~django.forms.NullBooleanSelect`.
``PositiveIntegerField``
------------------------
@ -875,8 +875,8 @@ Like an :class:`IntegerField`, but only allows values under a certain
.. class:: TextField([**options])
A large text field. The admin represents this as a ``<textarea>`` (a multi-line
input).
A large text field. The default form widget for this field is a
:class:`~django.forms.Textarea`.
.. admonition:: MySQL users
@ -893,8 +893,8 @@ input).
A time, represented in Python by a ``datetime.time`` instance. Accepts the same
auto-population options as :class:`DateField`.
The admin represents this as an ``<input type="text">`` with some JavaScript
shortcuts.
The default form widget for this field is a :class:`~django.forms.TextInput`.
The admin adds some JavaScript shortcuts.
``URLField``
------------
@ -903,7 +903,7 @@ shortcuts.
A :class:`CharField` for a URL.
The admin represents this as an ``<input type="text">`` (a single-line input).
The default form widget for this field is a :class:`~django.forms.TextInput`.
Like all :class:`CharField` subclasses, :class:`URLField` takes the optional
:attr:`~CharField.max_length`argument. If you don't specify
@ -979,9 +979,9 @@ define the details of how the relation works.
.. attribute:: ForeignKey.limit_choices_to
A dictionary of lookup arguments and values (see :doc:`/topics/db/queries`)
that limit the available admin choices for this object. Use this with
functions from the Python ``datetime`` module to limit choices of objects by
date. For example::
that limit the available admin or ModelForm choices for this object. Use
this with functions from the Python ``datetime`` module to limit choices of
objects by date. For example::
limit_choices_to = {'pub_date__lte': datetime.now}

View File

@ -48,7 +48,7 @@ that, you need to :meth:`~Model.save()`.
2. Add a method on a custom manager (usually preferred)::
class BookManager(models.Manager):
def create_book(title):
def create_book(self, title):
book = self.create(title=title)
# do something with the book
return book
@ -459,9 +459,9 @@ using ``__str__()`` like this::
last_name = models.CharField(max_length=50)
def __str__(self):
# Note use of django.utils.encoding.smart_bytes() here because
# Note use of django.utils.encoding.force_bytes() here because
# first_name and last_name will be unicode strings.
return smart_bytes('%s %s' % (self.first_name, self.last_name))
return force_bytes('%s %s' % (self.first_name, self.last_name))
``get_absolute_url``
--------------------

View File

@ -2064,7 +2064,7 @@ Note this is only available in MySQL and requires direct manipulation of the
database to add the full-text index. By default Django uses BOOLEAN MODE for
full text searches. See the `MySQL documentation`_ for additional details.
.. _MySQL documentation: http://dev.mysql.com/doc/refman/5.1/en/fulltext-boolean.html>
.. _MySQL documentation: http://dev.mysql.com/doc/refman/5.1/en/fulltext-boolean.html
.. fieldlookup:: regex
@ -2236,4 +2236,3 @@ Variance
extension.
.. _SQLite documentation: http://www.sqlite.org/contrib

View File

@ -731,10 +731,11 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. class:: HttpResponseRedirect
The constructor takes a single argument -- the path to redirect to. This
can be a fully qualified URL (e.g. ``'http://www.yahoo.com/search/'``) or
an absolute path with no domain (e.g. ``'/search/'``). Note that this
returns an HTTP status code 302.
The first argument to the constructor is required -- the path to redirect
to. This can be a fully qualified URL
(e.g. ``'http://www.yahoo.com/search/'``) or an absolute path with no
domain (e.g. ``'/search/'``). See :class:`HttpResponse` for other optional
constructor arguments. Note that this returns an HTTP status code 302.
.. class:: HttpResponsePermanentRedirect
@ -743,8 +744,9 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. class:: HttpResponseNotModified
The constructor doesn't take any arguments. Use this to designate that a
page hasn't been modified since the user's last request (status code 304).
The constructor doesn't take any arguments and no content should be added
to this response. Use this to designate that a page hasn't been modified
since the user's last request (status code 304).
.. class:: HttpResponseBadRequest
@ -760,8 +762,9 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
.. class:: HttpResponseNotAllowed
Like :class:`HttpResponse`, but uses a 405 status code. Takes a single,
required argument: a list of permitted methods (e.g. ``['GET', 'POST']``).
Like :class:`HttpResponse`, but uses a 405 status code. The first argument
to the constructor is required: a list of permitted methods (e.g.
``['GET', 'POST']``).
.. class:: HttpResponseGone

View File

@ -192,7 +192,7 @@ compose a prefix, version and key into a final cache key. The default
implementation is equivalent to the function::
def make_key(key, key_prefix, version):
return ':'.join([key_prefix, str(version), smart_bytes(key)])
return ':'.join([key_prefix, str(version), key])
You may use any key function you want, as long as it has the same
argument signature.
@ -1375,8 +1375,8 @@ MEDIA_ROOT
Default: ``''`` (Empty string)
Absolute path to the directory that holds media for this installation, used
for :doc:`managing stored files </topics/files>`.
Absolute filesystem path to the directory that will hold :doc:`user-uploaded
files </topics/files>`.
Example: ``"/var/www/example.com/media/"``

View File

@ -125,6 +125,10 @@ dot in a variable name, it tries the following lookups, in this order:
* Attribute lookup. Example: ``foo.bar``
* List-index lookup. Example: ``foo[bar]``
Note that "bar" in a template expression like ``{{ foo.bar }}`` will be
interpreted as a literal string and not using the value of the variable "bar",
if one exists in the template context.
The template system uses the first lookup type that works. It's short-circuit
logic. Here are a few examples::
@ -424,6 +428,10 @@ optional, third positional argument, ``processors``. In this example, the
my_data_dictionary,
context_instance=RequestContext(request))
Alternatively, use the :meth:`~django.shortcuts.render()` shortcut which is
the same as a call to :func:`~django.shortcuts.render_to_response()` with a
context_instance argument that forces the use of a ``RequestContext``.
Here's what each of the default processors does:
django.contrib.auth.context_processors.auth

14
docs/releases/1.3.2.txt Normal file
View File

@ -0,0 +1,14 @@
==========================
Django 1.3.2 release notes
==========================
*July 30, 2012*
This is the second security release in the Django 1.3 series, fixing several
security issues in Django 1.3. Django 1.3.2 is a recommended upgrade for
all users of Django 1.3.
For a full list of issues addressed in this release, see the `security
advisory`_.
.. _security advisory: https://www.djangoproject.com/weblog/2012/jul/30/security-releases-issued/

View File

@ -587,7 +587,7 @@ gettext domain):
ones listed later.
* The ``locale`` subdirectory of the directory containing the settings, that
usually coincides with and is know as the *project directory* is being
usually coincides with and is known as the *project directory* is being
deprecated in this release as a source of translations. (the precedence of
these translations is intermediate between applications and :setting:`LOCALE_PATHS`
translations). See the `corresponding deprecated features section`_

14
docs/releases/1.4.1.txt Normal file
View File

@ -0,0 +1,14 @@
==========================
Django 1.4.1 release notes
==========================
*July 30, 2012*
This is the first security release in the Django 1.4 series, fixing several
security issues in Django 1.4. Django 1.4.1 is a recommended upgrade for
all users of Django 1.4.
For a full list of issues addressed in this release, see the `security
advisory`_.
.. _security advisory: https://www.djangoproject.com/weblog/2012/jul/30/security-releases-issued/

14
docs/releases/1.4.2.txt Normal file
View File

@ -0,0 +1,14 @@
==========================
Django 1.4.2 release notes
==========================
*TO BE RELEASED*
This is the second security release in the Django 1.4 series.
Backwards incompatible changes
==============================
* The newly introduced :class:`~django.db.models.GenericIPAddressField`
constructor arguments have been adapted to match those of all other model
fields. The first two keyword arguments are now verbose_name and name.

View File

@ -172,6 +172,54 @@ If you were using the ``data`` parameter in a PUT request without a
``content_type``, you must encode your data before passing it to the test
client and set the ``content_type`` argument.
.. _simplejson-incompatibilities:
System version of :mod:`simplejson` no longer used
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:ref:`As explained below <simplejson-deprecation>`, Django 1.5 deprecates
:mod:`django.utils.simplejson` in favor of Python 2.6's built-in :mod:`json`
module. In theory, this change is harmless. Unfortunately, because of
incompatibilities between versions of :mod:`simplejson`, it may trigger errors
in some circumstances.
JSON-related features in Django 1.4 always used :mod:`django.utils.simplejson`.
This module was actually:
- A system version of :mod:`simplejson`, if one was available (ie. ``import
simplejson`` works), if it was more recent than Django's built-in copy or it
had the C speedups, or
- The :mod:`json` module from the standard library, if it was available (ie.
Python 2.6 or greater), or
- A built-in copy of version 2.0.7 of :mod:`simplejson`.
In Django 1.5, those features use Python's :mod:`json` module, which is based
on version 2.0.9 of :mod:`simplejson`.
There are no known incompatibilities between Django's copy of version 2.0.7 and
Python's copy of version 2.0.9. However, there are some incompatibilities
between other versions of :mod:`simplejson`:
- While the :mod:`simplejson` API is documented as always returning unicode
strings, the optional C implementation can return a byte string. This was
fixed in Python 2.7.
- :class:`simplejson.JSONEncoder` gained a ``namedtuple_as_object`` keyword
argument in version 2.2.
More information on these incompatibilities is available in `ticket #18023`_.
The net result is that, if you have installed :mod:`simplejson` and your code
uses Django's serialization internals directly -- for instance
:class:`django.core.serializers.json.DjangoJSONEncoder`, the switch from
:mod:`simplejson` to :mod:`json` could break your code. (In general, changes to
internals aren't documented; we're making an exception here.)
At this point, the maintainers of Django believe that using :mod:`json` from
the standard library offers the strongest guarantee of backwards-compatibility.
They recommend to use it from now on.
.. _ticket #18023: https://code.djangoproject.com/ticket/18023#comment:10
String types of hasher method parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -179,7 +227,7 @@ If you have written a :ref:`custom password hasher <auth_password_storage>`,
your ``encode()``, ``verify()`` or ``safe_summary()`` methods should accept
Unicode parameters (``password``, ``salt`` or ``encoded``). If any of the
hashing methods need byte strings, you can use the
:func:`~django.utils.encoding.smart_bytes` utility to encode the strings.
:func:`~django.utils.encoding.force_bytes` utility to encode the strings.
Validation of previous_page_number and next_page_number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -282,13 +330,21 @@ Miscellaneous
Features deprecated in 1.5
==========================
.. _simplejson-deprecation:
``django.utils.simplejson``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Since Django 1.5 drops support for Python 2.5, we can now rely on the
:mod:`json` module being in Python's standard library -- so we've removed
our own copy of ``simplejson``. You can safely change any use of
:mod:`django.utils.simplejson` to :mod:`json`.
:mod:`json` module being available in Python's standard library, so we've
removed our own copy of :mod:`simplejson`. You should now import :mod:`json`
instead :mod:`django.utils.simplejson`.
Unfortunately, this change might have unwanted side-effects, because of
incompatibilities between versions of :mod:`simplejson` -- see the
:ref:`backwards-incompatible changes <simplejson-incompatibilities>` section.
If you rely on features added to :mod:`simplejson` after it became Python's
:mod:`json`, you should import :mod:`simplejson` explicitly.
``itercompat.product``
~~~~~~~~~~~~~~~~~~~~~~

View File

@ -27,6 +27,8 @@ Final releases
.. toctree::
:maxdepth: 1
.. 1.4.2 (uncomment on release)
1.4.1
1.4
1.3 release
@ -34,6 +36,7 @@ Final releases
.. toctree::
:maxdepth: 1
1.3.2
1.3.1
1.3

View File

@ -460,7 +460,7 @@ algorithm.
Increasing the work factor
~~~~~~~~~~~~~~~~~~~~~~~~~~
The PDKDF2 and bcrypt algorithms use a number of iterations or rounds of
The PBKDF2 and bcrypt algorithms use a number of iterations or rounds of
hashing. This deliberately slows down attackers, making attacks against hashed
passwords harder. However, as computing power increases, the number of
iterations needs to be increased. We've chosen a reasonable default (and will
@ -468,7 +468,7 @@ increase it with each release of Django), but you may wish to tune it up or
down, depending on your security needs and available processing power. To do so,
you'll subclass the appropriate algorithm and override the ``iterations``
parameters. For example, to increase the number of iterations used by the
default PDKDF2 algorithm:
default PBKDF2 algorithm:
1. Create a subclass of ``django.contrib.auth.hashers.PBKDF2PasswordHasher``::
@ -1170,24 +1170,25 @@ includes a few other useful built-in views located in
:file:`registration/password_reset_form.html` if not supplied.
* ``email_template_name``: The full name of a template to use for
generating the email with the new password. Defaults to
generating the email with the reset password link. Defaults to
:file:`registration/password_reset_email.html` if not supplied.
* ``subject_template_name``: The full name of a template to use for
the subject of the email with the new password. Defaults
the subject of the email with the reset password link. Defaults
to :file:`registration/password_reset_subject.txt` if not supplied.
.. versionadded:: 1.4
* ``password_reset_form``: Form that will be used to set the password.
Defaults to :class:`~django.contrib.auth.forms.PasswordResetForm`.
* ``password_reset_form``: Form that will be used to get the email of
the user to reset the password for. Defaults to
:class:`~django.contrib.auth.forms.PasswordResetForm`.
* ``token_generator``: Instance of the class to check the password. This
will default to ``default_token_generator``, it's an instance of
* ``token_generator``: Instance of the class to check the one time link.
This will default to ``default_token_generator``, it's an instance of
``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
* ``post_reset_redirect``: The URL to redirect to after a successful
password change.
password reset request.
* ``from_email``: A valid email address. By default Django uses
the :setting:`DEFAULT_FROM_EMAIL`.
@ -1218,7 +1219,7 @@ includes a few other useful built-in views located in
* ``uid``: The user's id encoded in base 36.
* ``token``: Token to check that the password is valid.
* ``token``: Token to check that the reset link is valid.
Sample ``registration/password_reset_email.html`` (email body template):

Some files were not shown because too many files have changed in this diff Show More