Merged master changes.
This commit is contained in:
commit
7e82e83d67
|
@ -1,4 +1,4 @@
|
|||
include README
|
||||
include README.rst
|
||||
include AUTHORS
|
||||
include INSTALL
|
||||
include LICENSE
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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, '')
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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."
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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"]))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
""]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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()]
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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__'):
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 + \
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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'):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -238,7 +238,6 @@ class LazyObject(object):
|
|||
raise NotImplementedError
|
||||
|
||||
# introspection support:
|
||||
__members__ = property(lambda self: self.__dir__())
|
||||
__dir__ = new_method_proxy(dir)
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:]
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>` |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`
|
|
@ -13,6 +13,7 @@ Class-based views API reference. For introductory material, see
|
|||
generic-editing
|
||||
generic-date-based
|
||||
mixins
|
||||
flattened-index
|
||||
|
||||
Specification
|
||||
-------------
|
||||
|
|
|
@ -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::
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
----------------------
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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::
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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``
|
||||
--------------------
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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/"``
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
|
@ -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`_
|
||||
|
|
|
@ -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/
|
|
@ -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.
|
|
@ -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``
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue