diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 70c37d4de2..d3afdd796a 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -67,6 +67,10 @@ LANGUAGES = (
# Languages using BiDi (right-to-left) layout
LANGUAGES_BIDI = ("he",)
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
# Not-necessarily-technical managers of the site. They get broken link
# notifications and other various e-mails.
MANAGERS = ADMINS
diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html
index e7f1c7e5a9..7838bba6a5 100644
--- a/django/contrib/admin/templates/admin/base.html
+++ b/django/contrib/admin/templates/admin/base.html
@@ -43,7 +43,7 @@
-
+ {% block footer %}{% endblock %}
diff --git a/django/contrib/admin/urls.py b/django/contrib/admin/urls.py
index a2d3ccae48..bd894d8de1 100644
--- a/django/contrib/admin/urls.py
+++ b/django/contrib/admin/urls.py
@@ -1,9 +1,15 @@
+from django.conf import settings
from django.conf.urls.defaults import *
+if settings.USE_I18N:
+ i18n_view = 'django.views.i18n.javascript_catalog'
+else:
+ i18n_view = 'django.views.i18n.null_javascript_catalog'
+
urlpatterns = patterns('',
('^$', 'django.contrib.admin.views.main.index'),
('^r/(\d+)/(.*)/$', 'django.views.defaults.shortcut'),
- ('^jsi18n/$', 'django.views.i18n.javascript_catalog', {'packages': 'django.conf'}),
+ ('^jsi18n/$', i18n_view, {'packages': 'django.conf'}),
('^logout/$', 'django.contrib.auth.views.logout'),
('^password_change/$', 'django.contrib.auth.views.password_change'),
('^password_change/done/$', 'django.contrib.auth.views.password_change_done'),
@@ -29,3 +35,5 @@ urlpatterns = patterns('',
('^([^/]+)/([^/]+)/(.+)/delete/$', 'django.contrib.admin.views.main.delete_stage'),
('^([^/]+)/([^/]+)/(.+)/$', 'django.contrib.admin.views.main.change_stage'),
)
+
+del i18n_view
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index dde7ea5c9c..2150893022 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -27,17 +27,17 @@ def get_backends():
def authenticate(**credentials):
"""
- If the given credentials, return a user object.
+ If the given credentials are valid, return a User object.
"""
for backend in get_backends():
try:
user = backend.authenticate(**credentials)
except TypeError:
- # this backend doesn't accept these credentials as arguments, try the next one.
+ # This backend doesn't accept these credentials as arguments. Try the next one.
continue
if user is None:
continue
- # annotate the user object with the path of the backend
+ # Annotate the user object with the path of the backend.
user.backend = str(backend.__class__)
return user
@@ -54,7 +54,7 @@ def login(request, user):
def logout(request):
"""
- Remove the authenticated user's id from request.
+ Remove the authenticated user's ID from the request.
"""
del request.session[SESSION_KEY]
del request.session[BACKEND_SESSION_KEY]
diff --git a/django/contrib/markup/templatetags/markup.py b/django/contrib/markup/templatetags/markup.py
index 9cbf1b3807..4bb135cc32 100644
--- a/django/contrib/markup/templatetags/markup.py
+++ b/django/contrib/markup/templatetags/markup.py
@@ -47,7 +47,8 @@ def restructuredtext(value):
raise template.TemplateSyntaxError, "Error in {% restructuredtext %} filter: The Python docutils library isn't installed."
return value
else:
- parts = publish_parts(source=value, writer_name="html4css1")
+ docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {})
+ parts = publish_parts(source=value, writer_name="html4css1", settings_overrides=docutils_settings)
return parts["fragment"]
register.filter(textile)
diff --git a/django/core/serializers/__init__.py b/django/core/serializers/__init__.py
index 72c4407b59..75e087ee1b 100644
--- a/django/core/serializers/__init__.py
+++ b/django/core/serializers/__init__.py
@@ -20,7 +20,9 @@ from django.conf import settings
# Built-in serializers
BUILTIN_SERIALIZERS = {
- "xml" : "django.core.serializers.xml_serializer",
+ "xml" : "django.core.serializers.xml_serializer",
+ "python" : "django.core.serializers.python",
+ "json" : "django.core.serializers.json",
}
_serializers = {}
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index 5c84861326..e939c0c6e7 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -33,7 +33,9 @@ class Serializer(object):
for obj in queryset:
self.start_object(obj)
for field in obj._meta.fields:
- if field.rel is None:
+ if field is obj._meta.pk:
+ continue
+ elif field.rel is None:
self.handle_field(obj, field)
else:
self.handle_fk_field(obj, field)
diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py
new file mode 100644
index 0000000000..dd6513db57
--- /dev/null
+++ b/django/core/serializers/json.py
@@ -0,0 +1,51 @@
+"""
+Serialize data to/from JSON
+"""
+
+import datetime
+from django.utils import simplejson
+from django.core.serializers.python import Serializer as PythonSerializer
+from django.core.serializers.python import Deserializer as PythonDeserializer
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+class Serializer(PythonSerializer):
+ """
+ Convert a queryset to JSON.
+ """
+ def end_serialization(self):
+ simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder)
+
+ def getvalue(self):
+ return self.stream.getvalue()
+
+def Deserializer(stream_or_string, **options):
+ """
+ Deserialize a stream or string of JSON data.
+ """
+ if isinstance(stream_or_string, basestring):
+ stream = StringIO(stream_or_string)
+ else:
+ stream = stream_or_string
+ for obj in PythonDeserializer(simplejson.load(stream)):
+ yield obj
+
+class DateTimeAwareJSONEncoder(simplejson.JSONEncoder):
+ """
+ JSONEncoder subclass that knows how to encode date/time types
+ """
+
+ DATE_FORMAT = "%Y-%m-%d"
+ TIME_FORMAT = "%H:%M:%S"
+
+ def default(self, o):
+ if isinstance(o, datetime.date):
+ return o.strftime(self.DATE_FORMAT)
+ elif isinstance(o, datetime.time):
+ return o.strftime(self.TIME_FORMAT)
+ elif isinstance(o, datetime.datetime):
+ return o.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT))
+ else:
+ return super(self, DateTimeAwareJSONEncoder).default(o)
\ No newline at end of file
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
new file mode 100644
index 0000000000..7989e1d469
--- /dev/null
+++ b/django/core/serializers/python.py
@@ -0,0 +1,101 @@
+"""
+A Python "serializer". Doesn't do much serializing per se -- just converts to
+and from basic Python data types (lists, dicts, strings, etc.). Useful as a basis for
+other serializers.
+"""
+
+from django.conf import settings
+from django.core.serializers import base
+from django.db import models
+
+class Serializer(base.Serializer):
+ """
+ Serializes a QuerySet to basic Python objects.
+ """
+
+ def start_serialization(self):
+ self._current = None
+ self.objects = []
+
+ def end_serialization(self):
+ pass
+
+ def start_object(self, obj):
+ self._current = {}
+
+ def end_object(self, obj):
+ self.objects.append({
+ "model" : str(obj._meta),
+ "pk" : str(obj._get_pk_val()),
+ "fields" : self._current
+ })
+ self._current = None
+
+ def handle_field(self, obj, field):
+ self._current[field.name] = getattr(obj, field.name)
+
+ def handle_fk_field(self, obj, field):
+ related = getattr(obj, field.name)
+ if related is not None:
+ related = related._get_pk_val()
+ self._current[field.name] = related
+
+ def handle_m2m_field(self, obj, field):
+ self._current[field.name] = [related._get_pk_val() for related in getattr(obj, field.name).iterator()]
+
+ def getvalue(self):
+ return self.objects
+
+def Deserializer(object_list, **options):
+ """
+ Deserialize simple Python objects back into Django ORM instances.
+
+ It's expected that you pass the Python objects themselves (instead of a
+ stream or a string) to the constructor
+ """
+ models.get_apps()
+ for d in object_list:
+ # Look up the model and starting build a dict of data for it.
+ Model = _get_model(d["model"])
+ data = {Model._meta.pk.name : d["pk"]}
+ m2m_data = {}
+
+ # Handle each field
+ for (field_name, field_value) in d["fields"].iteritems():
+ if isinstance(field_value, unicode):
+ field_value = field_value.encode(options.get("encoding", settings.DEFAULT_CHARSET))
+
+ field = Model._meta.get_field(field_name)
+
+ # Handle M2M relations (with in_bulk() for performance)
+ if field.rel and isinstance(field.rel, models.ManyToManyRel):
+ pks = []
+ for pk in field_value:
+ if isinstance(pk, unicode):
+ pk = pk.encode(options.get("encoding", settings.DEFAULT_CHARSET))
+ m2m_data[field.name] = field.rel.to._default_manager.in_bulk(field_value).values()
+
+ # Handle FK fields
+ elif field.rel and isinstance(field.rel, models.ManyToOneRel):
+ try:
+ data[field.name] = field.rel.to._default_manager.get(pk=field_value)
+ except RelatedModel.DoesNotExist:
+ data[field.name] = None
+
+ # Handle all other fields
+ else:
+ data[field.name] = field.to_python(field_value)
+
+ yield base.DeserializedObject(Model(**data), m2m_data)
+
+def _get_model(model_identifier):
+ """
+ Helper to look up a model from an "app_label.module_name" string.
+ """
+ try:
+ Model = models.get_model(*model_identifier.split("."))
+ except TypeError:
+ Model = None
+ if Model is None:
+ raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)
+ return Model
diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py
index ab8769f237..09fff408cf 100644
--- a/django/core/serializers/xml_serializer.py
+++ b/django/core/serializers/xml_serializer.py
@@ -2,10 +2,11 @@
XML serializer.
"""
-from xml.dom import pulldom
-from django.utils.xmlutils import SimplerXMLGenerator
+from django.conf import settings
from django.core.serializers import base
from django.db import models
+from django.utils.xmlutils import SimplerXMLGenerator
+from xml.dom import pulldom
class Serializer(base.Serializer):
"""
@@ -16,7 +17,7 @@ class Serializer(base.Serializer):
"""
Start serialization -- open the XML document and the root element.
"""
- self.xml = SimplerXMLGenerator(self.stream, self.options.get("encoding", "utf-8"))
+ self.xml = SimplerXMLGenerator(self.stream, self.options.get("encoding", settings.DEFAULT_CHARSET))
self.xml.startDocument()
self.xml.startElement("django-objects", {"version" : "1.0"})
@@ -58,9 +59,7 @@ class Serializer(base.Serializer):
# Get a "string version" of the object's data (this is handled by the
# serializer base class). None is handled specially.
value = self.get_string_value(obj, field)
- if value is None:
- self.xml.addQuickElement("None")
- else:
+ if value is not None:
self.xml.characters(str(value))
self.xml.endElement("field")
@@ -106,7 +105,7 @@ class Deserializer(base.Deserializer):
def __init__(self, stream_or_string, **options):
super(Deserializer, self).__init__(stream_or_string, **options)
- self.encoding = self.options.get("encoding", "utf-8")
+ self.encoding = self.options.get("encoding", settings.DEFAULT_CHARSET)
self.event_stream = pulldom.parse(self.stream)
def next(self):
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 3bbe174188..17e07fe9e7 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -123,7 +123,6 @@ OPERATOR_MAPPING = {
'iexact': 'LIKE %s',
'contains': 'LIKE %s',
'icontains': 'LIKE %s',
- 'ne': '!= %s',
'gt': '> %s',
'gte': '>= %s',
'lt': '< %s',
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 8b000d3c2a..f99f555625 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -6,7 +6,7 @@ from django import forms
from django.core.exceptions import ObjectDoesNotExist
from django.utils.functional import curry, lazy
from django.utils.text import capfirst
-from django.utils.translation import gettext, gettext_lazy, ngettext
+from django.utils.translation import gettext, gettext_lazy
import datetime, os, time
class NOT_PROVIDED:
@@ -162,7 +162,7 @@ class Field(object):
def get_db_prep_lookup(self, lookup_type, value):
"Returns field's value prepared for database lookup."
- if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'year', 'month', 'day', 'search'):
+ if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'year', 'month', 'day', 'search'):
return [value]
elif lookup_type in ('range', 'in'):
return value
@@ -406,12 +406,15 @@ class DateField(Field):
if isinstance(value, datetime.date):
return value
validators.isValidANSIDate(value, None)
- return datetime.date(*time.strptime(value, '%Y-%m-%d')[:3])
+ try:
+ return datetime.date(*time.strptime(value, '%Y-%m-%d')[:3])
+ except ValueError:
+ raise validators.ValidationError, gettext('Enter a valid date in YYYY-MM-DD format.')
def get_db_prep_lookup(self, lookup_type, value):
if lookup_type == 'range':
value = [str(v) for v in value]
- elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne') and hasattr(value, 'strftime'):
+ elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'):
value = value.strftime('%Y-%m-%d')
else:
value = str(value)
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index f7ca012351..4c4b4a93c6 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -78,6 +78,32 @@ class RelatedField(object):
related = RelatedObject(other, cls, self)
self.contribute_to_related_class(other, related)
+ def get_db_prep_lookup(self, lookup_type, value):
+ # If we are doing a lookup on a Related Field, we must be
+ # comparing object instances. The value should be the PK of value,
+ # not value itself.
+ def pk_trace(value):
+ # Value may be a primary key, or an object held in a relation.
+ # If it is an object, then we need to get the primary key value for
+ # that object. In certain conditions (especially one-to-one relations),
+ # the primary key may itself be an object - so we need to keep drilling
+ # down until we hit a value that can be used for a comparison.
+ v = value
+ try:
+ while True:
+ v = getattr(v, v._meta.pk.name)
+ except AttributeError:
+ pass
+ return v
+
+ if lookup_type == 'exact':
+ return [pk_trace(value)]
+ if lookup_type == 'in':
+ return [pk_trace(v) for v in value]
+ elif lookup_type == 'isnull':
+ return []
+ raise TypeError, "Related Field has invalid lookup: %s" % lookup_type
+
def _get_related_query_name(self, opts):
# This method defines the name that can be used to identify this related object
# in a table-spanning query. It uses the lower-cased object_name by default,
diff --git a/django/db/models/loading.py b/django/db/models/loading.py
index 3d34845147..c7920fa4e0 100644
--- a/django/db/models/loading.py
+++ b/django/db/models/loading.py
@@ -15,7 +15,7 @@ _app_models = {} # Dictionary of models against app label
_app_errors = {} # Dictionary of errors that were experienced when loading the INSTALLED_APPS
# Key is the app_name of the model, value is the exception that was raised
# during model loading.
-_loaded = False # Has the contents of settings.INSTALLED_APPS been loaded?
+_loaded = False # Has the contents of settings.INSTALLED_APPS been loaded?
# i.e., has get_apps() been called?
def get_apps():
@@ -60,7 +60,7 @@ def get_app_errors():
global _app_errors
get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
return _app_errors
-
+
def get_models(app_mod=None):
"""
Given a module containing models, returns a list of the models. Otherwise
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 8efdcd11e1..6af00abf28 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -10,8 +10,17 @@ import re
if not hasattr(__builtins__, 'set'):
from sets import Set as set
+# The string constant used to separate query parts
LOOKUP_SEPARATOR = '__'
+# The list of valid query types
+QUERY_TERMS = (
+ 'exact', 'iexact', 'contains', 'icontains',
+ 'gt', 'gte', 'lt', 'lte', 'in',
+ 'startswith', 'istartswith', 'endswith', 'iendswith',
+ 'range', 'year', 'month', 'day', 'isnull',
+)
+
# Size of each "chunk" for get_iterator calls.
# Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100
@@ -206,7 +215,7 @@ class QuerySet(object):
raise self.model.DoesNotExist, "%s matching query does not exist." % self.model._meta.object_name
assert len(obj_list) == 1, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs)
return obj_list[0]
-
+
def create(self, **kwargs):
"""
Create a new object with the given kwargs, saving it to the database
@@ -723,12 +732,13 @@ def parse_lookup(kwarg_items, opts):
# if we find "pk", make the clause "exact', and insert
# a dummy name of None, which we will replace when
# we know which table column to grab as the primary key.
- # 2) If there is only one part, assume it to be an __exact
+ # 2) If there is only one part, or the last part is not a query
+ # term, assume that the query is an __exact
clause = path.pop()
if clause == 'pk':
clause = 'exact'
path.append(None)
- elif len(path) == 0:
+ elif len(path) == 0 or clause not in QUERY_TERMS:
path.append(clause)
clause = 'exact'
@@ -857,12 +867,14 @@ def lookup_inner(path, clause, value, opts, table, column):
)
if path:
+ # There are elements left in the path. More joins are required.
if len(path) == 1 and path[0] in (new_opts.pk.name, None) \
and clause in ('exact', 'isnull') and not join_required:
- # If the last name query is for a key, and the search is for
- # isnull/exact, then the current (for N-1) or intermediate
- # (for N-N) table can be used for the search - no need to join an
- # extra table just to check the primary key.
+ # If the next and final name query is for a primary key,
+ # and the search is for isnull/exact, then the current
+ # (for N-1) or intermediate (for N-N) table can be used
+ # for the search - no need to join an extra table just
+ # to check the primary key.
new_table = current_table
else:
# There are 1 or more name queries pending, and we have ruled out
@@ -888,13 +900,41 @@ def lookup_inner(path, clause, value, opts, table, column):
where.extend(where2)
params.extend(params2)
else:
- # Evaluate clause on current table.
- if name in (current_opts.pk.name, None) and clause in ('exact', 'isnull') and current_column:
- # If this is an exact/isnull key search, and the last pass
- # found/introduced a current/intermediate table that we can use to
- # optimize the query, then use that column name.
+ # No elements left in path. Current element is the element on which
+ # the search is being performed.
+
+ if join_required:
+ # Last query term is a RelatedObject
+ if field.field.rel.multiple:
+ # RelatedObject is from a 1-N relation.
+ # Join is required; query operates on joined table.
+ column = new_opts.pk.name
+ joins[backend.quote_name(new_table)] = (
+ backend.quote_name(new_opts.db_table),
+ "INNER JOIN",
+ "%s.%s = %s.%s" %
+ (backend.quote_name(current_table),
+ backend.quote_name(join_column),
+ backend.quote_name(new_table),
+ backend.quote_name(new_column))
+ )
+ current_table = new_table
+ else:
+ # RelatedObject is from a 1-1 relation,
+ # No need to join; get the pk value from the related object,
+ # and compare using that.
+ column = current_opts.pk.name
+ elif intermediate_table:
+ # Last query term is a related object from an N-N relation.
+ # Join from intermediate table is sufficient.
+ column = join_column
+ elif name == current_opts.pk.name and clause in ('exact', 'isnull') and current_column:
+ # Last query term is for a primary key. If previous iterations
+ # introduced a current/intermediate table that can be used to
+ # optimize the query, then use that table and column name.
column = current_column
else:
+ # Last query term was a normal field.
column = field.column
where.append(get_where_clause(current_opts, clause, current_table + '.', column, value))
diff --git a/django/db/models/related.py b/django/db/models/related.py
index 4ab8cde5e7..ee3b916cf4 100644
--- a/django/db/models/related.py
+++ b/django/db/models/related.py
@@ -70,6 +70,10 @@ class RelatedObject(object):
else:
return [None] * self.field.rel.num_in_admin
+ def get_db_prep_lookup(self, lookup_type, value):
+ # Defer to the actual field definition for db prep
+ return self.field.get_db_prep_lookup(lookup_type, value)
+
def editable_fields(self):
"Get the fields in this class that should be edited inline."
return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field]
diff --git a/django/utils/simplejson/LICENSE.txt b/django/utils/simplejson/LICENSE.txt
new file mode 100644
index 0000000000..90251a9f62
--- /dev/null
+++ b/django/utils/simplejson/LICENSE.txt
@@ -0,0 +1,20 @@
+simplejson 1.3
+Copyright (c) 2006 Bob Ippolito
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/django/utils/simplejson/__init__.py b/django/utils/simplejson/__init__.py
new file mode 100644
index 0000000000..f88329b950
--- /dev/null
+++ b/django/utils/simplejson/__init__.py
@@ -0,0 +1,221 @@
+r"""
+A simple, fast, extensible JSON encoder and decoder
+
+JSON (JavaScript Object Notation) is a subset of
+JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
+interchange format.
+
+simplejson exposes an API familiar to uses of the standard library
+marshal and pickle modules.
+
+Encoding basic Python object hierarchies::
+
+ >>> import simplejson
+ >>> simplejson.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
+ '["foo", {"bar": ["baz", null, 1.0, 2]}]'
+ >>> print simplejson.dumps("\"foo\bar")
+ "\"foo\bar"
+ >>> print simplejson.dumps(u'\u1234')
+ "\u1234"
+ >>> print simplejson.dumps('\\')
+ "\\"
+ >>> print simplejson.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
+ {"a": 0, "b": 0, "c": 0}
+ >>> from StringIO import StringIO
+ >>> io = StringIO()
+ >>> simplejson.dump(['streaming API'], io)
+ >>> io.getvalue()
+ '["streaming API"]'
+
+Decoding JSON::
+
+ >>> import simplejson
+ >>> simplejson.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
+ [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
+ >>> simplejson.loads('"\\"foo\\bar"')
+ u'"foo\x08ar'
+ >>> from StringIO import StringIO
+ >>> io = StringIO('["streaming API"]')
+ >>> simplejson.load(io)
+ [u'streaming API']
+
+Specializing JSON object decoding::
+
+ >>> import simplejson
+ >>> def as_complex(dct):
+ ... if '__complex__' in dct:
+ ... return complex(dct['real'], dct['imag'])
+ ... return dct
+ ...
+ >>> simplejson.loads('{"__complex__": true, "real": 1, "imag": 2}',
+ ... object_hook=as_complex)
+ (1+2j)
+
+Extending JSONEncoder::
+
+ >>> import simplejson
+ >>> class ComplexEncoder(simplejson.JSONEncoder):
+ ... def default(self, obj):
+ ... if isinstance(obj, complex):
+ ... return [obj.real, obj.imag]
+ ... return simplejson.JSONEncoder.default(self, obj)
+ ...
+ >>> dumps(2 + 1j, cls=ComplexEncoder)
+ '[2.0, 1.0]'
+ >>> ComplexEncoder().encode(2 + 1j)
+ '[2.0, 1.0]'
+ >>> list(ComplexEncoder().iterencode(2 + 1j))
+ ['[', '2.0', ', ', '1.0', ']']
+
+
+Note that the JSON produced by this module is a subset of YAML,
+so it may be used as a serializer for that as well.
+"""
+__version__ = '1.3'
+__all__ = [
+ 'dump', 'dumps', 'load', 'loads',
+ 'JSONDecoder', 'JSONEncoder',
+]
+
+from django.utils.simplejson.decoder import JSONDecoder
+from django.utils.simplejson.encoder import JSONEncoder
+
+def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
+ allow_nan=True, cls=None, **kw):
+ """
+ Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
+ ``.write()``-supporting file-like object).
+
+ If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
+ (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+ will be skipped instead of raising a ``TypeError``.
+
+ If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp``
+ may be ``unicode`` instances, subject to normal Python ``str`` to
+ ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
+ understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
+ to cause an error.
+
+ If ``check_circular`` is ``False``, then the circular reference check
+ for container types will be skipped and a circular reference will
+ result in an ``OverflowError`` (or worse).
+
+ If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
+ serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
+ in strict compliance of the JSON specification, instead of using the
+ JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+ To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+ ``.default()`` method to serialize additional types), specify it with
+ the ``cls`` kwarg.
+ """
+ if cls is None:
+ cls = JSONEncoder
+ iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+ check_circular=check_circular, allow_nan=allow_nan,
+ **kw).iterencode(obj)
+ # could accelerate with writelines in some versions of Python, at
+ # a debuggability cost
+ for chunk in iterable:
+ fp.write(chunk)
+
+def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
+ allow_nan=True, cls=None, **kw):
+ """
+ Serialize ``obj`` to a JSON formatted ``str``.
+
+ If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
+ (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
+ will be skipped instead of raising a ``TypeError``.
+
+ If ``ensure_ascii`` is ``False``, then the return value will be a
+ ``unicode`` instance subject to normal Python ``str`` to ``unicode``
+ coercion rules instead of being escaped to an ASCII ``str``.
+
+ If ``check_circular`` is ``False``, then the circular reference check
+ for container types will be skipped and a circular reference will
+ result in an ``OverflowError`` (or worse).
+
+ If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
+ serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
+ strict compliance of the JSON specification, instead of using the
+ JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
+
+ To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
+ ``.default()`` method to serialize additional types), specify it with
+ the ``cls`` kwarg.
+ """
+ if cls is None:
+ cls = JSONEncoder
+ return cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
+ check_circular=check_circular, allow_nan=allow_nan, **kw).encode(obj)
+
+def load(fp, encoding=None, cls=None, object_hook=None, **kw):
+ """
+ Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
+ a JSON document) to a Python object.
+
+ If the contents of ``fp`` is encoded with an ASCII based encoding other
+ than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must
+ be specified. Encodings that are not ASCII based (such as UCS-2) are
+ not allowed, and should be wrapped with
+ ``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
+ object and passed to ``loads()``
+
+ ``object_hook`` is an optional function that will be called with the
+ result of any object literal decode (a ``dict``). The return value of
+ ``object_hook`` will be used instead of the ``dict``. This feature
+ can be used to implement custom decoders (e.g. JSON-RPC class hinting).
+
+ To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+ kwarg.
+ """
+ if cls is None:
+ cls = JSONDecoder
+ if object_hook is not None:
+ kw['object_hook'] = object_hook
+ return cls(encoding=encoding, **kw).decode(fp.read())
+
+def loads(s, encoding=None, cls=None, object_hook=None, **kw):
+ """
+ Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
+ document) to a Python object.
+
+ If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding
+ other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name
+ must be specified. Encodings that are not ASCII based (such as UCS-2)
+ are not allowed and should be decoded to ``unicode`` first.
+
+ ``object_hook`` is an optional function that will be called with the
+ result of any object literal decode (a ``dict``). The return value of
+ ``object_hook`` will be used instead of the ``dict``. This feature
+ can be used to implement custom decoders (e.g. JSON-RPC class hinting).
+
+ To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
+ kwarg.
+ """
+ if cls is None:
+ cls = JSONDecoder
+ if object_hook is not None:
+ kw['object_hook'] = object_hook
+ return cls(encoding=encoding, **kw).decode(s)
+
+def read(s):
+ """
+ json-py API compatibility hook. Use loads(s) instead.
+ """
+ import warnings
+ warnings.warn("simplejson.loads(s) should be used instead of read(s)",
+ DeprecationWarning)
+ return loads(s)
+
+def write(obj):
+ """
+ json-py API compatibility hook. Use dumps(s) instead.
+ """
+ import warnings
+ warnings.warn("simplejson.dumps(s) should be used instead of write(s)",
+ DeprecationWarning)
+ return dumps(obj)
+
+
diff --git a/django/utils/simplejson/decoder.py b/django/utils/simplejson/decoder.py
new file mode 100644
index 0000000000..684af8c9ad
--- /dev/null
+++ b/django/utils/simplejson/decoder.py
@@ -0,0 +1,271 @@
+"""
+Implementation of JSONDecoder
+"""
+import re
+
+from django.utils.simplejson.scanner import Scanner, pattern
+
+FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
+
+def _floatconstants():
+ import struct
+ import sys
+ _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
+ if sys.byteorder != 'big':
+ _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
+ nan, inf = struct.unpack('dd', _BYTES)
+ return nan, inf, -inf
+
+NaN, PosInf, NegInf = _floatconstants()
+
+def linecol(doc, pos):
+ lineno = doc.count('\n', 0, pos) + 1
+ if lineno == 1:
+ colno = pos
+ else:
+ colno = pos - doc.rindex('\n', 0, pos)
+ return lineno, colno
+
+def errmsg(msg, doc, pos, end=None):
+ lineno, colno = linecol(doc, pos)
+ if end is None:
+ return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
+ endlineno, endcolno = linecol(doc, end)
+ return '%s: line %d column %d - line %d column %d (char %d - %d)' % (
+ msg, lineno, colno, endlineno, endcolno, pos, end)
+
+_CONSTANTS = {
+ '-Infinity': NegInf,
+ 'Infinity': PosInf,
+ 'NaN': NaN,
+ 'true': True,
+ 'false': False,
+ 'null': None,
+}
+
+def JSONConstant(match, context, c=_CONSTANTS):
+ return c[match.group(0)], None
+pattern('(-?Infinity|NaN|true|false|null)')(JSONConstant)
+
+def JSONNumber(match, context):
+ match = JSONNumber.regex.match(match.string, *match.span())
+ integer, frac, exp = match.groups()
+ if frac or exp:
+ res = float(integer + (frac or '') + (exp or ''))
+ else:
+ res = int(integer)
+ return res, None
+pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(JSONNumber)
+
+STRINGCHUNK = re.compile(r'(.*?)(["\\])', FLAGS)
+BACKSLASH = {
+ '"': u'"', '\\': u'\\', '/': u'/',
+ 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
+}
+
+DEFAULT_ENCODING = "utf-8"
+
+def scanstring(s, end, encoding=None, _b=BACKSLASH, _m=STRINGCHUNK.match):
+ if encoding is None:
+ encoding = DEFAULT_ENCODING
+ chunks = []
+ _append = chunks.append
+ begin = end - 1
+ while 1:
+ chunk = _m(s, end)
+ if chunk is None:
+ raise ValueError(
+ errmsg("Unterminated string starting at", s, begin))
+ end = chunk.end()
+ content, terminator = chunk.groups()
+ if content:
+ if not isinstance(content, unicode):
+ content = unicode(content, encoding)
+ _append(content)
+ if terminator == '"':
+ break
+ try:
+ esc = s[end]
+ except IndexError:
+ raise ValueError(
+ errmsg("Unterminated string starting at", s, begin))
+ if esc != 'u':
+ try:
+ m = _b[esc]
+ except KeyError:
+ raise ValueError(
+ errmsg("Invalid \\escape: %r" % (esc,), s, end))
+ end += 1
+ else:
+ esc = s[end + 1:end + 5]
+ try:
+ m = unichr(int(esc, 16))
+ if len(esc) != 4 or not esc.isalnum():
+ raise ValueError
+ except ValueError:
+ raise ValueError(errmsg("Invalid \\uXXXX escape", s, end))
+ end += 5
+ _append(m)
+ return u''.join(chunks), end
+
+def JSONString(match, context):
+ encoding = getattr(context, 'encoding', None)
+ return scanstring(match.string, match.end(), encoding)
+pattern(r'"')(JSONString)
+
+WHITESPACE = re.compile(r'\s*', FLAGS)
+
+def JSONObject(match, context, _w=WHITESPACE.match):
+ pairs = {}
+ s = match.string
+ end = _w(s, match.end()).end()
+ nextchar = s[end:end + 1]
+ # trivial empty object
+ if nextchar == '}':
+ return pairs, end + 1
+ if nextchar != '"':
+ raise ValueError(errmsg("Expecting property name", s, end))
+ end += 1
+ encoding = getattr(context, 'encoding', None)
+ while True:
+ key, end = scanstring(s, end, encoding)
+ end = _w(s, end).end()
+ if s[end:end + 1] != ':':
+ raise ValueError(errmsg("Expecting : delimiter", s, end))
+ end = _w(s, end + 1).end()
+ try:
+ value, end = JSONScanner.iterscan(s, idx=end).next()
+ except StopIteration:
+ raise ValueError(errmsg("Expecting object", s, end))
+ pairs[key] = value
+ end = _w(s, end).end()
+ nextchar = s[end:end + 1]
+ end += 1
+ if nextchar == '}':
+ break
+ if nextchar != ',':
+ raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
+ end = _w(s, end).end()
+ nextchar = s[end:end + 1]
+ end += 1
+ if nextchar != '"':
+ raise ValueError(errmsg("Expecting property name", s, end - 1))
+ object_hook = getattr(context, 'object_hook', None)
+ if object_hook is not None:
+ pairs = object_hook(pairs)
+ return pairs, end
+pattern(r'{')(JSONObject)
+
+def JSONArray(match, context, _w=WHITESPACE.match):
+ values = []
+ s = match.string
+ end = _w(s, match.end()).end()
+ # look-ahead for trivial empty array
+ nextchar = s[end:end + 1]
+ if nextchar == ']':
+ return values, end + 1
+ while True:
+ try:
+ value, end = JSONScanner.iterscan(s, idx=end).next()
+ except StopIteration:
+ raise ValueError(errmsg("Expecting object", s, end))
+ values.append(value)
+ end = _w(s, end).end()
+ nextchar = s[end:end + 1]
+ end += 1
+ if nextchar == ']':
+ break
+ if nextchar != ',':
+ raise ValueError(errmsg("Expecting , delimiter", s, end))
+ end = _w(s, end).end()
+ return values, end
+pattern(r'\[')(JSONArray)
+
+ANYTHING = [
+ JSONObject,
+ JSONArray,
+ JSONString,
+ JSONConstant,
+ JSONNumber,
+]
+
+JSONScanner = Scanner(ANYTHING)
+
+class JSONDecoder(object):
+ """
+ Simple JSON decoder
+
+ Performs the following translations in decoding:
+
+ +---------------+-------------------+
+ | JSON | Python |
+ +===============+===================+
+ | object | dict |
+ +---------------+-------------------+
+ | array | list |
+ +---------------+-------------------+
+ | string | unicode |
+ +---------------+-------------------+
+ | number (int) | int, long |
+ +---------------+-------------------+
+ | number (real) | float |
+ +---------------+-------------------+
+ | true | True |
+ +---------------+-------------------+
+ | false | False |
+ +---------------+-------------------+
+ | null | None |
+ +---------------+-------------------+
+
+ It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
+ their corresponding ``float`` values, which is outside the JSON spec.
+ """
+
+ _scanner = Scanner(ANYTHING)
+ __all__ = ['__init__', 'decode', 'raw_decode']
+
+ def __init__(self, encoding=None, object_hook=None):
+ """
+ ``encoding`` determines the encoding used to interpret any ``str``
+ objects decoded by this instance (utf-8 by default). It has no
+ effect when decoding ``unicode`` objects.
+
+ Note that currently only encodings that are a superset of ASCII work,
+ strings of other encodings should be passed in as ``unicode``.
+
+ ``object_hook``, if specified, will be called with the result
+ of every JSON object decoded and its return value will be used in
+ place of the given ``dict``. This can be used to provide custom
+ deserializations (e.g. to support JSON-RPC class hinting).
+ """
+ self.encoding = encoding
+ self.object_hook = object_hook
+
+ def decode(self, s, _w=WHITESPACE.match):
+ """
+ Return the Python representation of ``s`` (a ``str`` or ``unicode``
+ instance containing a JSON document)
+ """
+ obj, end = self.raw_decode(s, idx=_w(s, 0).end())
+ end = _w(s, end).end()
+ if end != len(s):
+ raise ValueError(errmsg("Extra data", s, end, len(s)))
+ return obj
+
+ def raw_decode(self, s, **kw):
+ """
+ Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
+ with a JSON document) and return a 2-tuple of the Python
+ representation and the index in ``s`` where the document ended.
+
+ This can be used to decode a JSON document from a string that may
+ have extraneous data at the end.
+ """
+ kw.setdefault('context', self)
+ try:
+ obj, end = self._scanner.iterscan(s, **kw).next()
+ except StopIteration:
+ raise ValueError("No JSON object could be decoded")
+ return obj, end
+
+__all__ = ['JSONDecoder']
diff --git a/django/utils/simplejson/encoder.py b/django/utils/simplejson/encoder.py
new file mode 100644
index 0000000000..bb1aba09f0
--- /dev/null
+++ b/django/utils/simplejson/encoder.py
@@ -0,0 +1,289 @@
+"""
+Implementation of JSONEncoder
+"""
+import re
+
+# this should match any kind of infinity
+INFCHARS = re.compile(r'[infINF]')
+ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
+ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
+ESCAPE_DCT = {
+ '\\': '\\\\',
+ '"': '\\"',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+}
+for i in range(20):
+ ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
+
+def floatstr(o, allow_nan=True):
+ s = str(o)
+ # If the first non-sign is a digit then it's not a special value
+ if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
+ return s
+ elif not allow_nan:
+ raise ValueError("Out of range float values are not JSON compliant: %r"
+ % (o,))
+ # These are the string representations on the platforms I've tried
+ if s == 'nan':
+ return 'NaN'
+ if s == 'inf':
+ return 'Infinity'
+ if s == '-inf':
+ return '-Infinity'
+ # NaN should either be inequal to itself, or equal to everything
+ if o != o or o == 0.0:
+ return 'NaN'
+ # Last ditch effort, assume inf
+ if o < 0:
+ return '-Infinity'
+ return 'Infinity'
+
+def encode_basestring(s):
+ """
+ Return a JSON representation of a Python string
+ """
+ def replace(match):
+ return ESCAPE_DCT[match.group(0)]
+ return '"' + ESCAPE.sub(replace, s) + '"'
+
+def encode_basestring_ascii(s):
+ def replace(match):
+ s = match.group(0)
+ try:
+ return ESCAPE_DCT[s]
+ except KeyError:
+ return '\\u%04x' % (ord(s),)
+ return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
+
+
+class JSONEncoder(object):
+ """
+ Extensible JSON encoder for Python data structures.
+
+ Supports the following objects and types by default:
+
+ +-------------------+---------------+
+ | Python | JSON |
+ +===================+===============+
+ | dict | object |
+ +-------------------+---------------+
+ | list, tuple | array |
+ +-------------------+---------------+
+ | str, unicode | string |
+ +-------------------+---------------+
+ | int, long, float | number |
+ +-------------------+---------------+
+ | True | true |
+ +-------------------+---------------+
+ | False | false |
+ +-------------------+---------------+
+ | None | null |
+ +-------------------+---------------+
+
+ To extend this to recognize other objects, subclass and implement a
+ ``.default()`` method with another method that returns a serializable
+ object for ``o`` if possible, otherwise it should call the superclass
+ implementation (to raise ``TypeError``).
+ """
+ __all__ = ['__init__', 'default', 'encode', 'iterencode']
+ def __init__(self, skipkeys=False, ensure_ascii=True,
+ check_circular=True, allow_nan=True, sort_keys=False):
+ """
+ Constructor for JSONEncoder, with sensible defaults.
+
+ If skipkeys is False, then it is a TypeError to attempt
+ encoding of keys that are not str, int, long, float or None. If
+ skipkeys is True, such items are simply skipped.
+
+ If ensure_ascii is True, the output is guaranteed to be str
+ objects with all incoming unicode characters escaped. If
+ ensure_ascii is false, the output will be unicode object.
+
+ If check_circular is True, then lists, dicts, and custom encoded
+ objects will be checked for circular references during encoding to
+ prevent an infinite recursion (which would cause an OverflowError).
+ Otherwise, no such check takes place.
+
+ If allow_nan is True, then NaN, Infinity, and -Infinity will be
+ encoded as such. This behavior is not JSON specification compliant,
+ but is consistent with most JavaScript based encoders and decoders.
+ Otherwise, it will be a ValueError to encode such floats.
+
+ If sort_keys is True, then the output of dictionaries will be
+ sorted by key; this is useful for regression tests to ensure
+ that JSON serializations can be compared on a day-to-day basis.
+ """
+
+ self.skipkeys = skipkeys
+ self.ensure_ascii = ensure_ascii
+ self.check_circular = check_circular
+ self.allow_nan = allow_nan
+ self.sort_keys = sort_keys
+
+ def _iterencode_list(self, lst, markers=None):
+ if not lst:
+ yield '[]'
+ return
+ if markers is not None:
+ markerid = id(lst)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = lst
+ yield '['
+ first = True
+ for value in lst:
+ if first:
+ first = False
+ else:
+ yield ', '
+ for chunk in self._iterencode(value, markers):
+ yield chunk
+ yield ']'
+ if markers is not None:
+ del markers[markerid]
+
+ def _iterencode_dict(self, dct, markers=None):
+ if not dct:
+ yield '{}'
+ return
+ if markers is not None:
+ markerid = id(dct)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = dct
+ yield '{'
+ first = True
+ if self.ensure_ascii:
+ encoder = encode_basestring_ascii
+ else:
+ encoder = encode_basestring
+ allow_nan = self.allow_nan
+ if self.sort_keys:
+ keys = dct.keys()
+ keys.sort()
+ items = [(k,dct[k]) for k in keys]
+ else:
+ items = dct.iteritems()
+ for key, value in items:
+ if isinstance(key, basestring):
+ pass
+ # JavaScript is weakly typed for these, so it makes sense to
+ # also allow them. Many encoders seem to do something like this.
+ elif isinstance(key, float):
+ key = floatstr(key, allow_nan)
+ elif isinstance(key, (int, long)):
+ key = str(key)
+ elif key is True:
+ key = 'true'
+ elif key is False:
+ key = 'false'
+ elif key is None:
+ key = 'null'
+ elif self.skipkeys:
+ continue
+ else:
+ raise TypeError("key %r is not a string" % (key,))
+ if first:
+ first = False
+ else:
+ yield ', '
+ yield encoder(key)
+ yield ': '
+ for chunk in self._iterencode(value, markers):
+ yield chunk
+ yield '}'
+ if markers is not None:
+ del markers[markerid]
+
+ def _iterencode(self, o, markers=None):
+ if isinstance(o, basestring):
+ if self.ensure_ascii:
+ encoder = encode_basestring_ascii
+ else:
+ encoder = encode_basestring
+ yield encoder(o)
+ elif o is None:
+ yield 'null'
+ elif o is True:
+ yield 'true'
+ elif o is False:
+ yield 'false'
+ elif isinstance(o, (int, long)):
+ yield str(o)
+ elif isinstance(o, float):
+ yield floatstr(o, self.allow_nan)
+ elif isinstance(o, (list, tuple)):
+ for chunk in self._iterencode_list(o, markers):
+ yield chunk
+ elif isinstance(o, dict):
+ for chunk in self._iterencode_dict(o, markers):
+ yield chunk
+ else:
+ if markers is not None:
+ markerid = id(o)
+ if markerid in markers:
+ raise ValueError("Circular reference detected")
+ markers[markerid] = o
+ for chunk in self._iterencode_default(o, markers):
+ yield chunk
+ if markers is not None:
+ del markers[markerid]
+
+ def _iterencode_default(self, o, markers=None):
+ newobj = self.default(o)
+ return self._iterencode(newobj, markers)
+
+ def default(self, o):
+ """
+ Implement this method in a subclass such that it returns
+ a serializable object for ``o``, or calls the base implementation
+ (to raise a ``TypeError``).
+
+ For example, to support arbitrary iterators, you could
+ implement default like this::
+
+ def default(self, o):
+ try:
+ iterable = iter(o)
+ except TypeError:
+ pass
+ else:
+ return list(iterable)
+ return JSONEncoder.default(self, o)
+ """
+ raise TypeError("%r is not JSON serializable" % (o,))
+
+ def encode(self, o):
+ """
+ Return a JSON string representation of a Python data structure.
+
+ >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
+ '{"foo":["bar", "baz"]}'
+ """
+ # This doesn't pass the iterator directly to ''.join() because it
+ # sucks at reporting exceptions. It's going to do this internally
+ # anyway because it uses PySequence_Fast or similar.
+ chunks = list(self.iterencode(o))
+ return ''.join(chunks)
+
+ def iterencode(self, o):
+ """
+ Encode the given object and yield each string
+ representation as available.
+
+ For example::
+
+ for chunk in JSONEncoder().iterencode(bigobject):
+ mysocket.write(chunk)
+ """
+ if self.check_circular:
+ markers = {}
+ else:
+ markers = None
+ return self._iterencode(o, markers)
+
+__all__ = ['JSONEncoder']
diff --git a/django/utils/simplejson/scanner.py b/django/utils/simplejson/scanner.py
new file mode 100644
index 0000000000..c2e9b6eb89
--- /dev/null
+++ b/django/utils/simplejson/scanner.py
@@ -0,0 +1,63 @@
+"""
+Iterator based sre token scanner
+"""
+import sre_parse, sre_compile, sre_constants
+from sre_constants import BRANCH, SUBPATTERN
+from sre import VERBOSE, MULTILINE, DOTALL
+import re
+
+__all__ = ['Scanner', 'pattern']
+
+FLAGS = (VERBOSE | MULTILINE | DOTALL)
+class Scanner(object):
+ def __init__(self, lexicon, flags=FLAGS):
+ self.actions = [None]
+ # combine phrases into a compound pattern
+ s = sre_parse.Pattern()
+ s.flags = flags
+ p = []
+ for idx, token in enumerate(lexicon):
+ phrase = token.pattern
+ try:
+ subpattern = sre_parse.SubPattern(s,
+ [(SUBPATTERN, (idx + 1, sre_parse.parse(phrase, flags)))])
+ except sre_constants.error:
+ raise
+ p.append(subpattern)
+ self.actions.append(token)
+
+ p = sre_parse.SubPattern(s, [(BRANCH, (None, p))])
+ self.scanner = sre_compile.compile(p)
+
+
+ def iterscan(self, string, idx=0, context=None):
+ """
+ Yield match, end_idx for each match
+ """
+ match = self.scanner.scanner(string, idx).match
+ actions = self.actions
+ lastend = idx
+ end = len(string)
+ while True:
+ m = match()
+ if m is None:
+ break
+ matchbegin, matchend = m.span()
+ if lastend == matchend:
+ break
+ action = actions[m.lastindex]
+ if action is not None:
+ rval, next_pos = action(m, context)
+ if next_pos is not None and next_pos != matchend:
+ # "fast forward" the scanner
+ matchend = next_pos
+ match = self.scanner.scanner(string, matchend).match
+ yield rval, matchend
+ lastend = matchend
+
+def pattern(pattern, flags=FLAGS):
+ def decorator(fn):
+ fn.pattern = pattern
+ fn.regex = re.compile(pattern, flags)
+ return fn
+ return decorator
diff --git a/django/views/i18n.py b/django/views/i18n.py
index a2bc54c9ed..b5eb32bda3 100644
--- a/django/views/i18n.py
+++ b/django/views/i18n.py
@@ -28,21 +28,9 @@ def set_language(request):
NullSource = """
/* gettext identity library */
-function gettext(msgid) {
- return msgid;
-}
-
-function ngettext(singular, plural, count) {
- if (count == 1) {
- return singular;
- } else {
- return plural;
- }
-}
-
-function gettext_noop(msgid) {
- return msgid;
-}
+function gettext(msgid) { return msgid; }
+function ngettext(singular, plural, count) { return (count == 1) ? singular : plural; }
+function gettext_noop(msgid) { return msgid; }
"""
LibHead = """
@@ -54,56 +42,47 @@ var catalog = new Array();
LibFoot = """
function gettext(msgid) {
- var value = catalog[msgid];
- if (typeof(value) == 'undefined') {
- return msgid;
- } else {
- if (typeof(value) == 'string') {
- return value;
- } else {
- return value[0];
- }
- }
+ var value = catalog[msgid];
+ if (typeof(value) == 'undefined') {
+ return msgid;
+ } else {
+ return (typeof(value) == 'string') ? value : value[0];
+ }
}
function ngettext(singular, plural, count) {
- value = catalog[singular];
- if (typeof(value) == 'undefined') {
- if (count == 1) {
- return singular;
- } else {
- return plural;
- }
- } else {
- return value[pluralidx(count)];
- }
+ value = catalog[singular];
+ if (typeof(value) == 'undefined') {
+ return (count == 1) ? singular : plural;
+ } else {
+ return value[pluralidx(count)];
+ }
}
-function gettext_noop(msgid) {
- return msgid;
-}
+function gettext_noop(msgid) { return msgid; }
"""
SimplePlural = """
-function pluralidx(count) {
- if (count == 1) {
- return 0;
- } else {
- return 1;
- }
-}
+function pluralidx(count) { return (count == 1) ? 0 : 1; }
"""
InterPolate = r"""
function interpolate(fmt, obj, named) {
- if (named) {
- return fmt.replace(/%\(\w+\)s/, function(match){return String(obj[match.slice(2,-2)])});
- } else {
- return fmt.replace(/%s/, function(match){return String(obj.shift())});
- }
+ if (named) {
+ return fmt.replace(/%\(\w+\)s/, function(match){return String(obj[match.slice(2,-2)])});
+ } else {
+ return fmt.replace(/%s/, function(match){return String(obj.shift())});
+ }
}
"""
+def null_javascript_catalog(request, domain=None, packages=None):
+ """
+ Returns "identity" versions of the JavaScript i18n functions -- i.e.,
+ versions that don't actually do anything.
+ """
+ return http.HttpResponse(NullSource + InterPolate, 'text/javascript')
+
def javascript_catalog(request, domain='djangojs', packages=None):
"""
Returns the selected language catalog as a javascript library.
@@ -191,4 +170,3 @@ def javascript_catalog(request, domain='djangojs', packages=None):
src.append(InterPolate)
src = ''.join(src)
return http.HttpResponse(src, 'text/javascript')
-
diff --git a/docs/add_ons.txt b/docs/add_ons.txt
index d72e92b018..90c98b7176 100644
--- a/docs/add_ons.txt
+++ b/docs/add_ons.txt
@@ -128,6 +128,8 @@ A collection of template filters that implement these common markup languages:
* Markdown
* ReST (ReStructured Text)
+For documentation, read the source code in django/contrib/markup/templatetags/markup.py.
+
redirects
=========
diff --git a/docs/authentication.txt b/docs/authentication.txt
index 3edbc21f7a..68b9024a90 100644
--- a/docs/authentication.txt
+++ b/docs/authentication.txt
@@ -267,25 +267,54 @@ previous section). You can tell them apart with ``is_anonymous()``, like so::
How to log a user in
--------------------
-Depending on your task, you'll probably want to make sure to validate the
-user's username and password before you log them in. The easiest way to do so
-is to use the built-in ``authenticate`` and ``login`` functions from within a
-view::
+Django provides two functions in ``django.contrib.auth``: ``authenticate()``
+and ``login()``.
+
+To authenticate a given username and password, use ``authenticate()``. It
+takes two keyword arguments, ``username`` and ``password``, and it returns
+a ``User`` object if the password is valid for the given username. If the
+password is invalid, ``authenticate()`` returns ``None``. Example::
+
+ from django.contrib.auth import authenticate
+ user = authenticate(username='john', password='secret')
+ if user is not None:
+ print "You provided a correct username and password!"
+ else:
+ print "Your username and password were incorrect."
+
+To log a user in, in a view, use ``login()``. It takes an ``HttpRequest``
+object and a ``User`` object. ``login()`` saves the user's ID in the session,
+using Django's session framework, so, as mentioned above, you'll need to make
+sure to have the session middleware installed.
+
+This example shows how you might use both ``authenticate()`` and ``login()``::
from django.contrib.auth import authenticate, login
- username = request.POST['username']
- password = request.POST['password']
- user = authenticate(username=username, password=password)
- if user is not None:
- login(request, user)
-``authenticate`` checks the username and password. If they are valid it
-returns a user object, otherwise it returns ``None``. ``login`` makes it so
-your users don't have send a username and password for every request. Because
-the ``login`` function uses sessions, you'll need to make sure you have
-``SessionMiddleware`` enabled. See the `session documentation`_ for
-more information.
+ def my_view(request):
+ username = request.POST['username']
+ password = request.POST['password']
+ user = authenticate(username=username, password=password)
+ if user is not None:
+ login(request, user)
+ # Redirect to a success page.
+ else:
+ # Return an error message.
+How to log a user out
+---------------------
+
+To log out a user who has been logged in via ``django.contrib.auth.login()``,
+use ``django.contrib.auth.logout()`` within your view. It takes an
+``HttpRequest`` object and has no return value. Example::
+
+ from django.contrib.auth import logout
+
+ def logout_view(request):
+ logout(request)
+ # Redirect to a success page.
+
+Note that ``logout()`` doesn't throw any errors if the user wasn't logged in.
Limiting access to logged-in users
----------------------------------
@@ -568,7 +597,7 @@ The currently logged-in user and his/her permissions are made available in the
setting contains ``"django.core.context_processors.auth"``, which is default.
For more, see the `RequestContext docs`_.
- .. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-djangocontext
+ .. _RequestContext docs: http://www.djangoproject.com/documentation/templates_python/#subclassing-context-requestcontext
Users
-----
@@ -681,61 +710,93 @@ database. To send messages to anonymous users, use the `session framework`_.
.. _session framework: http://www.djangoproject.com/documentation/sessions/
-Other Authentication Sources
+Other authentication sources
============================
-Django supports other authentication sources as well. You can even use
-multiple sources at the same time.
+The authentication that comes with Django is good enough for most common cases,
+but you may have the need to hook into another authentication source -- that
+is, another source of usernames and passwords or authentication methods.
-Using multiple backends
------------------------
+For example, your company may already have an LDAP setup that stores a username
+and password for every employee. It'd be a hassle for both the network
+administrator and the users themselves if users had separate accounts in LDAP
+and the Django-based applications.
-The list of backends to use is controlled by the ``AUTHENTICATION_BACKENDS``
-setting. This should be a tuple of python path names. It defaults to
-``('django.contrib.auth.backends.ModelBackend',)``. To add additional backends
-just add them to your settings.py file. Ordering matters, so if the same
-username and password is valid in multiple backends, the first one in the
-list will return a user object, and the remaining ones won't even get a chance.
+So, to handle situations like this, the Django authentication system lets you
+plug in another authentication sources. You can override Django's default
+database-based scheme, or you can use the default system in tandem with other
+systems.
+
+Specifying authentication backends
+----------------------------------
+
+Behind the scenes, Django maintains a list of "authentication backends" that it
+checks for authentication. When somebody calls
+``django.contrib.auth.authenticate()`` -- as described in "How to log a user in"
+above -- Django tries authenticating across all of its authentication backends.
+If the first authentication method fails, Django tries the second one, and so
+on, until all backends have been attempted.
+
+The list of authentication backends to use is specified in the
+``AUTHENTICATION_BACKENDS`` setting. This should be a tuple of Python path
+names that point to Python classes that know how to authenticate. These classes
+can be anywhere on your Python path.
+
+By default, ``AUTHENTICATION_BACKENDS`` is set to::
+
+ ('django.contrib.auth.backends.ModelBackend',)
+
+That's the basic authentication scheme that checks the Django users database.
+
+The order of ``AUTHENTICATION_BACKENDS`` matters, so if the same username and
+password is valid in multiple backends, Django will stop processing at the
+first positive match.
Writing an authentication backend
---------------------------------
-An authentication backend is a class that implements 2 methods:
-``get_user(id)`` and ``authenticate(**credentials)``. The ``get_user`` method
-takes an id, which could be a username, and database id, whatever, and returns
-a user object. The ``authenticate`` method takes credentials as keyword
-arguments. Many times it will just look like this::
+An authentication backend is a class that implements two methods:
+``get_user(id)`` and ``authenticate(**credentials)``.
+
+The ``get_user`` method takes an ``id`` -- which could be a username, database
+ID or whatever -- and returns a ``User`` object.
+
+The ``authenticate`` method takes credentials as keyword arguments. Most of
+the time, it'll just look like this::
class MyBackend:
def authenticate(username=None, password=None):
- # check the username/password and return a user
+ # Check the username/password and return a User.
-but it could also authenticate a token like so::
+But it could also authenticate a token, like so::
class MyBackend:
def authenticate(token=None):
- # check the token and return a user
+ # Check the token and return a User.
-Regardless, ``authenticate`` should check the credentials it gets, and if they
-are valid, it should return a user object that matches those credentials.
+Either way, ``authenticate`` should check the credentials it gets, and it
+should return a ``User`` object that matches those credentials, if the
+credentials are valid. If they're not valid, it should return ``None``.
-The Django admin system is tightly coupled to the Django User object described
-at the beginning of this document. For now, the best way to deal with this is
-to create a Django User object for each user that exists for your backend
-(i.e. in your LDAP directory, your external SQL database, etc.) You can either
-write a script to do this in advance, or your ``authenticate`` method can do
-it the first time a user logs in. Here's an example backend that
-authenticates against a username and password variable defined in your
-``settings.py`` file and creates a Django user object the first time they
-authenticate::
+The Django admin system is tightly coupled to the Django ``User`` object
+described at the beginning of this document. For now, the best way to deal with
+this is to create a Django ``User`` object for each user that exists for your
+backend (e.g., in your LDAP directory, your external SQL database, etc.) You
+can either write a script to do this in advance, or your ``authenticate``
+method can do it the first time a user logs in.
+
+Here's an example backend that authenticates against a username and password
+variable defined in your ``settings.py`` file and creates a Django ``User``
+object the first time a user authenticates::
from django.conf import settings
from django.contrib.auth.models import User, check_password
class SettingsBackend:
"""
- Authenticate against vars in settings.py Use the login name, and a hash
- of the password. For example:
+ Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
+
+ Use the login name, and a hash of the password. For example:
ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
@@ -747,8 +808,9 @@ authenticate::
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
- # Create a new user. Note that we can set password to anything
- # as it won't be checked, the password from settings.py will.
+ # Create a new user. Note that we can set password
+ # to anything, because it won't be checked; the password
+ # from settings.py will.
user = User(username=username, password='get from settings.py')
user.is_staff = True
user.is_superuser = True
diff --git a/docs/faq.txt b/docs/faq.txt
index 37e15878f2..b374abfbf3 100644
--- a/docs/faq.txt
+++ b/docs/faq.txt
@@ -411,6 +411,36 @@ Using a ``FileField`` or an ``ImageField`` in a model takes a few steps:
absolute URL to your image in a template with
``{{ object.get_mug_shot_url }}``.
+Databases and models
+====================
+
+How can I see the raw SQL queries Django is running?
+----------------------------------------------------
+
+Make sure your Django ``DEBUG`` setting is set to ``True``. Then, just do
+this::
+
+ >>> from django.db import connection
+ >>> connection.queries
+ [{'sql': 'SELECT polls_polls.id,polls_polls.question,polls_polls.pub_date FROM polls_polls',
+ 'time': '0.002'}]
+
+``connection.queries`` is only available if ``DEBUG`` is ``True``. It's a list
+of dictionaries in order of query execution. Each dictionary has the following::
+
+ ``sql`` -- The raw SQL statement
+ ``time`` -- How long the statement took to execute, in seconds.
+
+``connection.queries`` includes all SQL statements -- INSERTs, UPDATES,
+SELECTs, etc. Each time your app hits the database, the query will be recorded.
+
+Can I use Django with a pre-existing database?
+----------------------------------------------
+
+Yes. See `Integrating with a legacy database`_.
+
+.. _`Integrating with a legacy database`: http://www.djangoproject.com/documentation/legacy_databases/
+
If I make changes to a model, how do I update the database?
-----------------------------------------------------------
@@ -439,35 +469,24 @@ uniqueness at that level. Single-column primary keys are needed for things such
as the admin interface to work; e.g., you need a simple way of being able to
specify an object to edit or delete.
-The database API
-================
+How do I add database-specific options to my CREATE TABLE statements, such as specifying MyISAM as the table type?
+------------------------------------------------------------------------------------------------------------------
-How can I see the raw SQL queries Django is running?
-----------------------------------------------------
+We try to avoid adding special cases in the Django code to accomodate all the
+database-specific options such as table type, etc. If you'd like to use any of
+these options, create an `SQL initial data file`_ that contains ``ALTER TABLE``
+statements that do what you want to do. The initial data files are executed in
+your database after the ``CREATE TABLE`` statements.
-Make sure your Django ``DEBUG`` setting is set to ``True``. Then, just do
-this::
+For example, if you're using MySQL and want your tables to use the MyISAM table
+type, create an initial data file and put something like this in it::
- >>> from django.db import connection
- >>> connection.queries
- [{'sql': 'SELECT polls_polls.id,polls_polls.question,polls_polls.pub_date FROM polls_polls',
- 'time': '0.002'}]
+ ALTER TABLE myapp_mytable ENGINE=MyISAM;
-``connection.queries`` is only available if ``DEBUG`` is ``True``. It's a list
-of dictionaries in order of query execution. Each dictionary has the following::
+As explained in the `SQL initial data file`_ documentation, this SQL file can
+contain arbitrary SQL, so you can make any sorts of changes you need to make.
- ``sql`` -- The raw SQL statement
- ``time`` -- How long the statement took to execute, in seconds.
-
-``connection.queries`` includes all SQL statements -- INSERTs, UPDATES,
-SELECTs, etc. Each time your app hits the database, the query will be recorded.
-
-Can I use Django with a pre-existing database?
-----------------------------------------------
-
-Yes. See `Integrating with a legacy database`_.
-
-.. _`Integrating with a legacy database`: http://www.djangoproject.com/documentation/legacy_databases/
+.. _SQL initial data file: http://www.djangoproject.com/documentation/model_api/#providing-initial-sql-data
Why is Django leaking memory?
-----------------------------
diff --git a/docs/i18n.txt b/docs/i18n.txt
index 1220ea95b3..1382d6df0c 100644
--- a/docs/i18n.txt
+++ b/docs/i18n.txt
@@ -35,12 +35,25 @@ How to internationalize your app: in three steps
support.
3. Activate the locale middleware in your Django settings.
-
.. admonition:: Behind the scenes
Django's translation machinery uses the standard ``gettext`` module that
comes with Python.
+If you don't need internationalization
+======================================
+
+Django's internationalization hooks are on by default, and that means there's a
+bit of i18n-related overhead in certain places of the framework. If you don't
+use internationalization, you should take the two seconds to set
+``USE_I18N = False`` in your settings file. If ``USE_I18N`` is set to
+``False``, then Django will make some optimizations so as not to load the
+internationalization machinery.
+
+See the `documentation for USE_I18N`_.
+
+.. _documentation for USE_I18N: http://www.djangoproject.com/documentation/settings/#use-i18n
+
How to specify translation strings
==================================
diff --git a/docs/serialization.txt b/docs/serialization.txt
index 41954b7a0d..25199e7a50 100644
--- a/docs/serialization.txt
+++ b/docs/serialization.txt
@@ -78,8 +78,25 @@ The Django object itself can be inspected as ``deserialized_object.object``.
Serialization formats
---------------------
-Django "ships" with a few included serializers, and there's a simple API for creating and registering your own...
+Django "ships" with a few included serializers:
-.. note::
+ ========== ==============================================================
+ Identifier Information
+ ========== ==============================================================
+ ``xml`` Serializes to and from a simple XML dialect.
- ... which will be documented once the API is stable :)
+ ``json`` Serializes to and from JSON_ (using a version of simplejson_
+ bundled with Django).
+
+ ``python`` Translates to and from "simple" Python objects (lists, dicts,
+ strings, etc.). Not really all that useful on its own, but
+ used as a base for other serializers.
+ ========== ==============================================================
+
+.. _json: http://json.org/
+.. _simplejson: http://undefined.org/python/#simplejson
+
+Writing custom serializers
+``````````````````````````
+
+XXX ...
diff --git a/docs/settings.txt b/docs/settings.txt
index 553736b280..4f4fb70298 100644
--- a/docs/settings.txt
+++ b/docs/settings.txt
@@ -107,15 +107,20 @@ For more, see the `diffsettings documentation`_.
Using settings in Python code
=============================
-In your Django apps, use settings by importing them from
+In your Django apps, use settings by importing the object
``django.conf.settings``. Example::
- from django.conf.settings import DEBUG
+ from django.conf import settings
- if DEBUG:
+ if settings.DEBUG:
# Do something
-Note that your code should *not* import from either ``global_settings`` or
+Note that ``django.conf.settings`` isn't a module -- it's an object. So
+importing individual settings is not possible::
+
+ from django.conf.settings import DEBUG # This won't work.
+
+Also note that your code should *not* import from either ``global_settings`` or
your own settings file. ``django.conf.settings`` abstracts the concepts of
default settings and site-specific settings; it presents a single interface.
It also decouples the code that uses settings from the location of your
@@ -127,9 +132,9 @@ Altering settings at runtime
You shouldn't alter settings in your applications at runtime. For example,
don't do this in a view::
- from django.conf.settings import DEBUG
+ from django.conf import settings
- DEBUG = True # Don't do this!
+ settings.DEBUG = True # Don't do this!
The only place you should assign to settings is in a settings file.
@@ -738,6 +743,16 @@ A boolean that specifies whether to output the "Etag" header. This saves
bandwidth but slows down performance. This is only used if ``CommonMiddleware``
is installed (see the `middleware docs`_).
+USE_I18N
+--------
+
+Default: ``True``
+
+A boolean that specifies whether Django's internationalization system should be
+enabled. This provides an easy way to turn it off, for performance. If this is
+set to ``False, Django will make some optimizations so as not to load the
+internationalization machinery.
+
YEAR_MONTH_FORMAT
-----------------
diff --git a/tests/modeltests/many_to_many/models.py b/tests/modeltests/many_to_many/models.py
index e80afece46..0e989a0fbe 100644
--- a/tests/modeltests/many_to_many/models.py
+++ b/tests/modeltests/many_to_many/models.py
@@ -75,6 +75,10 @@ API_TESTS = """
[, ]
>>> Article.objects.filter(publications__pk=1)
[, ]
+>>> Article.objects.filter(publications=1)
+[, ]
+>>> Article.objects.filter(publications=p1)
+[, ]
>>> Article.objects.filter(publications__title__startswith="Science")
[, ]
@@ -89,6 +93,13 @@ API_TESTS = """
>>> Article.objects.filter(publications__title__startswith="Science").distinct().count()
1
+>>> Article.objects.filter(publications__in=[1,2]).distinct()
+[, ]
+>>> Article.objects.filter(publications__in=[1,p2]).distinct()
+[, ]
+>>> Article.objects.filter(publications__in=[p1,p2]).distinct()
+[, ]
+
# Reverse m2m queries are supported (i.e., starting at the table that doesn't
# have a ManyToManyField).
>>> Publication.objects.filter(id__exact=1)
@@ -101,9 +112,19 @@ API_TESTS = """
>>> Publication.objects.filter(article__id__exact=1)
[]
-
>>> Publication.objects.filter(article__pk=1)
[]
+>>> Publication.objects.filter(article=1)
+[]
+>>> Publication.objects.filter(article=a1)
+[]
+
+>>> Publication.objects.filter(article__in=[1,2]).distinct()
+[, , , ]
+>>> Publication.objects.filter(article__in=[1,a2]).distinct()
+[, , , ]
+>>> Publication.objects.filter(article__in=[a1,a2]).distinct()
+[, , , ]
# If we delete a Publication, its Articles won't be able to access it.
>>> p1.delete()
diff --git a/tests/modeltests/many_to_one/models.py b/tests/modeltests/many_to_one/models.py
index a830ffbdc2..d202975128 100644
--- a/tests/modeltests/many_to_one/models.py
+++ b/tests/modeltests/many_to_one/models.py
@@ -136,6 +136,10 @@ False
>>> Article.objects.filter(reporter__first_name__exact='John')
[, ]
+# Check that implied __exact also works
+>>> Article.objects.filter(reporter__first_name='John')
+[, ]
+
# Query twice over the related field.
>>> Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith')
[, ]
@@ -151,10 +155,20 @@ False
[, ]
# Find all Articles for the Reporter whose ID is 1.
+# Use direct ID check, pk check, and object comparison
>>> Article.objects.filter(reporter__id__exact=1)
[, ]
>>> Article.objects.filter(reporter__pk=1)
[, ]
+>>> Article.objects.filter(reporter=1)
+[, ]
+>>> Article.objects.filter(reporter=r)
+[, ]
+
+>>> Article.objects.filter(reporter__in=[1,2]).distinct()
+[, , ]
+>>> Article.objects.filter(reporter__in=[r,r2]).distinct()
+[, , ]
# You need two underscores between "reporter" and "id" -- not one.
>>> Article.objects.filter(reporter_id__exact=1)
@@ -168,10 +182,6 @@ Traceback (most recent call last):
...
TypeError: Cannot resolve keyword 'reporter_id' into field
-# "pk" shortcut syntax works in a related context, too.
->>> Article.objects.filter(reporter__pk=1)
-[, ]
-
# You can also instantiate an Article by passing
# the Reporter's ID instead of a Reporter object.
>>> a3 = Article(id=None, headline="This is a test", pub_date=datetime(2005, 7, 27), reporter_id=r.id)
@@ -200,6 +210,18 @@ TypeError: Cannot resolve keyword 'reporter_id' into field
[]
>>> Reporter.objects.filter(article__pk=1)
[]
+>>> Reporter.objects.filter(article=1)
+[]
+>>> Reporter.objects.filter(article=a)
+[]
+
+>>> Reporter.objects.filter(article__in=[1,4]).distinct()
+[]
+>>> Reporter.objects.filter(article__in=[1,a3]).distinct()
+[]
+>>> Reporter.objects.filter(article__in=[a,a3]).distinct()
+[]
+
>>> Reporter.objects.filter(article__headline__startswith='This')
[, , ]
>>> Reporter.objects.filter(article__headline__startswith='This').distinct()
@@ -216,6 +238,12 @@ TypeError: Cannot resolve keyword 'reporter_id' into field
[, , , ]
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct()
[]
+>>> Reporter.objects.filter(article__reporter__exact=r).distinct()
+[]
+
+# Check that implied __exact also works
+>>> Reporter.objects.filter(article__reporter=r).distinct()
+[]
# If you delete a reporter, his articles will be deleted.
>>> Article.objects.all()
diff --git a/tests/modeltests/one_to_one/models.py b/tests/modeltests/one_to_one/models.py
index 27c16b5a34..f95556c08d 100644
--- a/tests/modeltests/one_to_one/models.py
+++ b/tests/modeltests/one_to_one/models.py
@@ -94,6 +94,12 @@ DoesNotExist: Restaurant matching query does not exist.
>>> Restaurant.objects.get(place__exact=1)
+>>> Restaurant.objects.get(place__exact=p1)
+
+>>> Restaurant.objects.get(place=1)
+
+>>> Restaurant.objects.get(place=p1)
+
>>> Restaurant.objects.get(place__pk=1)
>>> Restaurant.objects.get(place__name__startswith="Demon")
@@ -105,8 +111,18 @@ DoesNotExist: Restaurant matching query does not exist.
>>> Place.objects.get(restaurant__place__exact=1)
+>>> Place.objects.get(restaurant__place__exact=p1)
+
>>> Place.objects.get(restaurant__pk=1)
+>>> Place.objects.get(restaurant=1)
+
+>>> Place.objects.get(restaurant=r)
+
+>>> Place.objects.get(restaurant__exact=1)
+
+>>> Place.objects.get(restaurant__exact=r)
+
# Add a Waiter to the Restaurant.
>>> w = r.waiter_set.create(name='Joe')
@@ -115,14 +131,22 @@ DoesNotExist: Restaurant matching query does not exist.
# Query the waiters
+>>> Waiter.objects.filter(restaurant__place__pk=1)
+[]
>>> Waiter.objects.filter(restaurant__place__exact=1)
[]
+>>> Waiter.objects.filter(restaurant__place__exact=p1)
+[]
>>> Waiter.objects.filter(restaurant__pk=1)
[]
>>> Waiter.objects.filter(id__exact=1)
[]
>>> Waiter.objects.filter(pk=1)
[]
+>>> Waiter.objects.filter(restaurant=1)
+[]
+>>> Waiter.objects.filter(restaurant=r)
+[]
# Delete the restaurant; the waiter should also be removed
>>> r = Restaurant.objects.get(pk=1)
diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py
index 8c9483beba..ccf565c365 100644
--- a/tests/modeltests/serializers/models.py
+++ b/tests/modeltests/serializers/models.py
@@ -91,4 +91,31 @@ API_TESTS = """
>>> Article.objects.all()
[, ]
+# Django also ships with a built-in JSON serializers
+>>> json = serializers.serialize("json", Category.objects.filter(pk=2))
+>>> json
+'[{"pk": "2", "model": "serializers.category", "fields": {"name": "Music"}}]'
+
+# You can easily create new objects by deserializing data with an empty PK
+# (It's easier to demo this with JSON...)
+>>> new_author_json = '[{"pk": null, "model": "serializers.author", "fields": {"name": "Bill"}}]'
+>>> for obj in serializers.deserialize("json", new_author_json):
+... obj.save()
+>>> Author.objects.all()
+[, , ]
+
+# All the serializers work the same
+>>> json = serializers.serialize("json", Article.objects.all())
+>>> for obj in serializers.deserialize("json", json):
+... print obj
+
+
+
+>>> json = json.replace("Poker has no place on television", "Just kidding; I love TV poker")
+>>> for obj in serializers.deserialize("json", json):
+... obj.save()
+
+>>> Article.objects.all()
+[, ]
+
"""