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() +[, ] + """