summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/gis/db/backends/mysql/creation.py2
-rw-r--r--django/contrib/gis/db/models/sql/compiler.py4
-rw-r--r--django/contrib/gis/tests/relatedapp/models.py5
-rw-r--r--django/contrib/gis/tests/relatedapp/tests.py10
-rw-r--r--django/core/management/commands/flush.py10
-rw-r--r--django/db/backends/postgresql/creation.py2
-rw-r--r--django/db/backends/postgresql/operations.py9
-rw-r--r--django/db/models/fields/related.py5
-rw-r--r--django/db/models/sql/query.py5
-rw-r--r--django/template/defaultfilters.py6
-rw-r--r--docs/Makefile67
-rw-r--r--docs/_ext/djangodocs.py171
-rw-r--r--docs/_theme/djangodocs/genindex.html (renamed from docs/_templates/genindex.html)2
-rw-r--r--docs/_theme/djangodocs/layout.html (renamed from docs/_templates/layout.html)4
-rw-r--r--docs/_theme/djangodocs/modindex.html (renamed from docs/_templates/modindex.html)2
-rw-r--r--docs/_theme/djangodocs/search.html (renamed from docs/_templates/search.html)2
-rw-r--r--docs/_theme/djangodocs/static/default.css (renamed from docs/_static/default.css)0
-rw-r--r--docs/_theme/djangodocs/static/djangodocs.css (renamed from docs/_static/djangodocs.css)0
-rw-r--r--docs/_theme/djangodocs/static/docicons-behindscenes.png (renamed from docs/_static/docicons-behindscenes.png)bin2269 -> 2269 bytes
-rw-r--r--docs/_theme/djangodocs/static/docicons-note.png (renamed from docs/_static/docicons-note.png)bin1013 -> 1013 bytes
-rw-r--r--docs/_theme/djangodocs/static/docicons-philosophy.png (renamed from docs/_static/docicons-philosophy.png)bin1523 -> 1523 bytes
-rw-r--r--docs/_theme/djangodocs/static/homepage.css (renamed from docs/_static/homepage.css)0
-rw-r--r--docs/_theme/djangodocs/static/reset-fonts-grids.css (renamed from docs/_static/reset-fonts-grids.css)0
-rw-r--r--docs/_theme/djangodocs/theme.conf4
-rw-r--r--docs/conf.py174
-rw-r--r--docs/internals/documentation.txt5
-rw-r--r--docs/ref/contrib/admin/index.txt110
-rw-r--r--docs/ref/models/instances.txt2
-rw-r--r--tests/regressiontests/backends/models.py17
-rw-r--r--tests/regressiontests/backends/tests.py19
-rw-r--r--tests/regressiontests/templates/filters.py7
-rw-r--r--tests/regressiontests/templates/tests.py33
32 files changed, 437 insertions, 240 deletions
diff --git a/django/contrib/gis/db/backends/mysql/creation.py b/django/contrib/gis/db/backends/mysql/creation.py
index 93fd2e6166..dda77ea6ab 100644
--- a/django/contrib/gis/db/backends/mysql/creation.py
+++ b/django/contrib/gis/db/backends/mysql/creation.py
@@ -6,7 +6,7 @@ class MySQLCreation(DatabaseCreation):
from django.contrib.gis.db.models.fields import GeometryField
output = super(MySQLCreation, self).sql_indexes_for_field(model, f, style)
- if isinstance(f, GeometryField):
+ if isinstance(f, GeometryField) and f.spatial_index:
qn = self.connection.ops.quote_name
db_table = model._meta.db_table
idx_name = '%s_%s_id' % (db_table, f.column)
diff --git a/django/contrib/gis/db/models/sql/compiler.py b/django/contrib/gis/db/models/sql/compiler.py
index 78eeeafe19..55dc4a66ec 100644
--- a/django/contrib/gis/db/models/sql/compiler.py
+++ b/django/contrib/gis/db/models/sql/compiler.py
@@ -95,7 +95,7 @@ class GeoSQLCompiler(compiler.SQLCompiler):
return result
def get_default_columns(self, with_aliases=False, col_aliases=None,
- start_alias=None, opts=None, as_pairs=False):
+ start_alias=None, opts=None, as_pairs=False, local_only=False):
"""
Computes the default columns for selecting every field in the base
model. Will sometimes be called to pull in related models (e.g. via
@@ -121,6 +121,8 @@ class GeoSQLCompiler(compiler.SQLCompiler):
if start_alias:
seen = {None: start_alias}
for field, model in opts.get_fields_with_model():
+ if local_only and model is not None:
+ continue
if start_alias:
try:
alias = seen[model]
diff --git a/django/contrib/gis/tests/relatedapp/models.py b/django/contrib/gis/tests/relatedapp/models.py
index 726f9826c0..2e9a62b61f 100644
--- a/django/contrib/gis/tests/relatedapp/models.py
+++ b/django/contrib/gis/tests/relatedapp/models.py
@@ -38,6 +38,11 @@ class Author(models.Model):
name = models.CharField(max_length=100)
objects = models.GeoManager()
+class Article(models.Model):
+ title = models.CharField(max_length=100)
+ author = models.ForeignKey(Author, unique=True)
+ objects = models.GeoManager()
+
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, related_name='books', null=True)
diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py
index 184b65b9c7..5d3d00f08d 100644
--- a/django/contrib/gis/tests/relatedapp/tests.py
+++ b/django/contrib/gis/tests/relatedapp/tests.py
@@ -4,7 +4,7 @@ from django.contrib.gis.db.models import Collect, Count, Extent, F, Union
from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.tests.utils import mysql, oracle, postgis, spatialite, no_mysql, no_oracle, no_spatialite
from django.conf import settings
-from models import City, Location, DirectoryEntry, Parcel, Book, Author
+from models import City, Location, DirectoryEntry, Parcel, Book, Author, Article
cities = (('Aurora', 'TX', -97.516111, 33.058333),
('Roswell', 'NM', -104.528056, 33.387222),
@@ -291,6 +291,14 @@ class RelatedGeoModelTest(unittest.TestCase):
self.assertEqual(4, len(coll))
self.assertEqual(ref_geom, coll)
+ def test15_invalid_select_related(self):
+ "Testing doing select_related on the related name manager of a unique FK. See #13934."
+ qs = Article.objects.select_related('author__article')
+ # This triggers TypeError when `get_default_columns` has no `local_only`
+ # keyword. The TypeError is swallowed if QuerySet is actually
+ # evaluated as list generation swallows TypeError in CPython.
+ sql = str(qs.query)
+
# TODO: Related tests for KML, GML, and distance lookups.
def suite():
diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py
index 6836fe35ca..98e3f3151b 100644
--- a/django/core/management/commands/flush.py
+++ b/django/core/management/commands/flush.py
@@ -1,7 +1,7 @@
from optparse import make_option
from django.conf import settings
-from django.db import connections, transaction, models, DEFAULT_DB_ALIAS
+from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS
from django.core.management import call_command
from django.core.management.base import NoArgsCommand, CommandError
from django.core.management.color import no_style
@@ -66,7 +66,13 @@ The full error: %s""" % (connection.settings_dict['NAME'], e))
# Emit the post sync signal. This allows individual
# applications to respond as if the database had been
# sync'd from scratch.
- emit_post_sync_signal(models.get_models(), verbosity, interactive, db)
+ all_models = [
+ (app.__name__.split('.')[-2],
+ [m for m in models.get_models(app, include_auto_created=True)
+ if router.allow_syncdb(db, m)])
+ for app in models.get_apps()
+ ]
+ emit_post_sync_signal(all_models, verbosity, interactive, db)
# Reinstall the initial_data fixture.
kwargs = options.copy()
diff --git a/django/db/backends/postgresql/creation.py b/django/db/backends/postgresql/creation.py
index e3587f0e37..9984716389 100644
--- a/django/db/backends/postgresql/creation.py
+++ b/django/db/backends/postgresql/creation.py
@@ -64,7 +64,7 @@ class DatabaseCreation(BaseDatabaseCreation):
# a second index that specifies their operator class, which is
# needed when performing correct LIKE queries outside the
# C locale. See #12234.
- db_type = f.db_type()
+ db_type = f.db_type(connection=self.connection)
if db_type.startswith('varchar'):
output.append(get_index_sql('%s_%s_like' % (db_table, f.column),
' varchar_pattern_ops'))
diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py
index 76f25410fb..e8ce3f242b 100644
--- a/django/db/backends/postgresql/operations.py
+++ b/django/db/backends/postgresql/operations.py
@@ -56,7 +56,8 @@ class DatabaseOperations(BaseDatabaseOperations):
def last_insert_id(self, cursor, table_name, pk_name):
# Use pg_get_serial_sequence to get the underlying sequence name
# from the table name and column name (available since PostgreSQL 8)
- cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (table_name, pk_name))
+ cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (
+ self.quote_name(table_name), pk_name))
return cursor.fetchone()[0]
def no_limit_value(self):
@@ -98,7 +99,7 @@ class DatabaseOperations(BaseDatabaseOperations):
column_name = 'id'
sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \
(style.SQL_KEYWORD('SELECT'),
- style.SQL_TABLE(table_name),
+ style.SQL_TABLE(self.quote_name(table_name)),
style.SQL_FIELD(column_name))
)
return sql
@@ -120,7 +121,7 @@ class DatabaseOperations(BaseDatabaseOperations):
if isinstance(f, models.AutoField):
output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
(style.SQL_KEYWORD('SELECT'),
- style.SQL_TABLE(model._meta.db_table),
+ style.SQL_TABLE(qn(model._meta.db_table)),
style.SQL_FIELD(f.column),
style.SQL_FIELD(qn(f.column)),
style.SQL_FIELD(qn(f.column)),
@@ -132,7 +133,7 @@ class DatabaseOperations(BaseDatabaseOperations):
if not f.rel.through:
output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
(style.SQL_KEYWORD('SELECT'),
- style.SQL_TABLE(model._meta.db_table),
+ style.SQL_TABLE(qn(f.m2m_db_table())),
style.SQL_FIELD('id'),
style.SQL_FIELD(qn('id')),
style.SQL_FIELD(qn('id')),
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 5830a794df..232d5d5e74 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -812,6 +812,9 @@ class ForeignKey(RelatedField, Field):
to_field = to_field or (to._meta.pk and to._meta.pk.name)
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
+ if 'db_index' not in kwargs:
+ kwargs['db_index'] = True
+
kwargs['rel'] = rel_class(to, to_field,
related_name=kwargs.pop('related_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
@@ -819,8 +822,6 @@ class ForeignKey(RelatedField, Field):
parent_link=kwargs.pop('parent_link', False))
Field.__init__(self, **kwargs)
- self.db_index = True
-
def validate(self, value, model_instance):
if self.rel.parent_link:
return
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 0913399e2a..ec477447f3 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1090,10 +1090,7 @@ class Query(object):
# exclude the "foo__in=[]" case from this handling, because
# it's short-circuited in the Where class.
# We also need to handle the case where a subquery is provided
- entry = self.where_class()
- entry.add((Constraint(alias, col, None), 'isnull', True), AND)
- entry.negate()
- self.where.add(entry, AND)
+ self.where.add((Constraint(alias, col, None), 'isnull', False), AND)
if can_reuse is not None:
can_reuse.update(join_list)
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 4b720093b3..d8e7e91efe 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -11,9 +11,10 @@ except ImportError:
from django.template import Variable, Library
from django.conf import settings
from django.utils import formats
-from django.utils.translation import ugettext, ungettext
from django.utils.encoding import force_unicode, iri_to_uri
+from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe, SafeData
+from django.utils.translation import ugettext, ungettext
register = Library()
@@ -496,10 +497,9 @@ def join(value, arg, autoescape=None):
"""
value = map(force_unicode, value)
if autoescape:
- from django.utils.html import conditional_escape
value = [conditional_escape(v) for v in value]
try:
- data = arg.join(value)
+ data = conditional_escape(arg).join(value)
except AttributeError: # fail silently but nicely
return value
return mark_safe(data)
diff --git a/docs/Makefile b/docs/Makefile
index f6a92b6835..9301315040 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -12,20 +12,26 @@ PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
@@ -40,6 +46,11 @@ dirhtml:
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@@ -65,12 +76,42 @@ qthelp:
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django.qhc"
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/django"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
- "run these through (pdf)latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ make -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py
index efcd94d4bc..cee14ba6f1 100644
--- a/docs/_ext/djangodocs.py
+++ b/docs/_ext/djangodocs.py
@@ -1,9 +1,9 @@
"""
Sphinx plugins for Django documentation.
"""
+import os
-import docutils.nodes
-import docutils.transforms
+from docutils import nodes, transforms
try:
import json
except ImportError:
@@ -14,26 +14,12 @@ except ImportError:
from django.utils import simplejson as json
except ImportError:
json = None
-import os
-import sphinx
-import sphinx.addnodes
-try:
- from sphinx import builders
-except ImportError:
- import sphinx.builder as builders
-try:
- import sphinx.builders.html as builders_html
-except ImportError:
- builders_html = builders
+
+from sphinx import addnodes, roles
+from sphinx.builders.html import StandaloneHTMLBuilder
+from sphinx.writers.html import SmartyPantsHTMLTranslator
from sphinx.util.console import bold
-import sphinx.directives
-import sphinx.environment
-try:
- import sphinx.writers.html as sphinx_htmlwriter
-except ImportError:
- import sphinx.htmlwriter as sphinx_htmlwriter
-import sphinx.roles
-from docutils import nodes
+
def setup(app):
app.add_crossref_type(
@@ -74,21 +60,20 @@ def setup(app):
app.add_transform(SuppressBlockquotes)
app.add_builder(DjangoStandaloneHTMLBuilder)
- # Monkeypatch PickleHTMLBuilder so that it doesn't die in Sphinx 0.4.2
- if sphinx.__version__ == '0.4.2':
- monkeypatch_pickle_builder()
-
def parse_version_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
env = state.document.settings.env
is_nextversion = env.config.django_next_version == arguments[0]
ret = []
- node = sphinx.addnodes.versionmodified()
+ node = addnodes.versionmodified()
ret.append(node)
if not is_nextversion:
if len(arguments) == 1:
linktext = 'Please, see the release notes <releases-%s>' % (arguments[0])
- xrefs = sphinx.roles.xfileref_role('ref', linktext, linktext, lineno, state)
+ try:
+ xrefs = roles.XRefRole()('ref', linktext, linktext, lineno, state) # Sphinx >= 1.0
+ except:
+ xrefs = roles.xfileref_role('ref', linktext, linktext, lineno, state) # Sphinx < 1.0
node.extend(xrefs[0])
node['version'] = arguments[0]
else:
@@ -103,29 +88,29 @@ def parse_version_directive(name, arguments, options, content, lineno,
env.note_versionchange(node['type'], node['version'], node, lineno)
return ret
-
-class SuppressBlockquotes(docutils.transforms.Transform):
+
+class SuppressBlockquotes(transforms.Transform):
"""
Remove the default blockquotes that encase indented list, tables, etc.
"""
default_priority = 300
-
+
suppress_blockquote_child_nodes = (
- docutils.nodes.bullet_list,
- docutils.nodes.enumerated_list,
- docutils.nodes.definition_list,
- docutils.nodes.literal_block,
- docutils.nodes.doctest_block,
- docutils.nodes.line_block,
- docutils.nodes.table
+ nodes.bullet_list,
+ nodes.enumerated_list,
+ nodes.definition_list,
+ nodes.literal_block,
+ nodes.doctest_block,
+ nodes.line_block,
+ nodes.table
)
-
+
def apply(self):
- for node in self.document.traverse(docutils.nodes.block_quote):
+ for node in self.document.traverse(nodes.block_quote):
if len(node.children) == 1 and isinstance(node.children[0], self.suppress_blockquote_child_nodes):
node.replace_self(node.children[0])
-class DjangoHTMLTranslator(sphinx_htmlwriter.SmartyPantsHTMLTranslator):
+class DjangoHTMLTranslator(SmartyPantsHTMLTranslator):
"""
Django-specific reST to HTML tweaks.
"""
@@ -133,42 +118,41 @@ class DjangoHTMLTranslator(sphinx_htmlwriter.SmartyPantsHTMLTranslator):
# Don't use border=1, which docutils does by default.
def visit_table(self, node):
self.body.append(self.starttag(node, 'table', CLASS='docutils'))
-
+
# <big>? Really?
def visit_desc_parameterlist(self, node):
self.body.append('(')
self.first_param = 1
-
+
def depart_desc_parameterlist(self, node):
self.body.append(')')
- pass
-
+
#
# Don't apply smartypants to literal blocks
#
def visit_literal_block(self, node):
self.no_smarty += 1
- sphinx_htmlwriter.SmartyPantsHTMLTranslator.visit_literal_block(self, node)
+ SmartyPantsHTMLTranslator.visit_literal_block(self, node)
def depart_literal_block(self, node):
- sphinx_htmlwriter.SmartyPantsHTMLTranslator.depart_literal_block(self, node)
+ SmartyPantsHTMLTranslator.depart_literal_block(self, node)
self.no_smarty -= 1
-
+
#
- # Turn the "new in version" stuff (versoinadded/versionchanged) into a
+ # Turn the "new in version" stuff (versionadded/versionchanged) into a
# better callout -- the Sphinx default is just a little span,
# which is a bit less obvious that I'd like.
#
- # FIXME: these messages are all hardcoded in English. We need to chanage
+ # FIXME: these messages are all hardcoded in English. We need to change
# that to accomodate other language docs, but I can't work out how to make
- # that work and I think it'll require Sphinx 0.5 anyway.
+ # that work.
#
version_text = {
'deprecated': 'Deprecated in Django %s',
'versionchanged': 'Changed in Django %s',
'versionadded': 'New in Django %s',
}
-
+
def visit_versionmodified(self, node):
self.body.append(
self.starttag(node, 'div', CLASS=node['type'])
@@ -178,41 +162,31 @@ class DjangoHTMLTranslator(sphinx_htmlwriter.SmartyPantsHTMLTranslator):
len(node) and ":" or "."
)
self.body.append('<span class="title">%s</span> ' % title)
-
+
def depart_versionmodified(self, node):
self.body.append("</div>\n")
-
- # Give each section a unique ID -- nice for custom CSS hooks
- # This is different on docutils 0.5 vs. 0.4...
- if hasattr(sphinx_htmlwriter.SmartyPantsHTMLTranslator, 'start_tag_with_title') and sphinx.__version__ == '0.4.2':
- def start_tag_with_title(self, node, tagname, **atts):
- node = {
- 'classes': node.get('classes', []),
- 'ids': ['s-%s' % i for i in node.get('ids', [])]
- }
- return self.starttag(node, tagname, **atts)
-
- else:
- def visit_section(self, node):
- old_ids = node.get('ids', [])
- node['ids'] = ['s-' + i for i in old_ids]
- if sphinx.__version__ != '0.4.2':
- node['ids'].extend(old_ids)
- sphinx_htmlwriter.SmartyPantsHTMLTranslator.visit_section(self, node)
- node['ids'] = old_ids
+ # Give each section a unique ID -- nice for custom CSS hooks
+ def visit_section(self, node):
+ old_ids = node.get('ids', [])
+ node['ids'] = ['s-' + i for i in old_ids]
+ node['ids'].extend(old_ids)
+ SmartyPantsHTMLTranslator.visit_section(self, node)
+ node['ids'] = old_ids
def parse_django_admin_node(env, sig, signode):
command = sig.split(' ')[0]
env._django_curr_admin_command = command
title = "django-admin.py %s" % sig
- signode += sphinx.addnodes.desc_name(title, title)
+ signode += addnodes.desc_name(title, title)
return sig
def parse_django_adminopt_node(env, sig, signode):
"""A copy of sphinx.directives.CmdoptionDesc.parse_signature()"""
- from sphinx import addnodes
- from sphinx.directives.desc import option_desc_re
+ try:
+ from sphinx.domains.std import option_desc_re # Sphinx >= 1.0
+ except:
+ from sphinx.directives.desc import option_desc_re # Sphinx < 1.0
count = 0
firstname = ''
for m in option_desc_re.finditer(sig):
@@ -228,44 +202,8 @@ def parse_django_adminopt_node(env, sig, signode):
raise ValueError
return firstname
-def monkeypatch_pickle_builder():
- import shutil
- from os import path
- try:
- import cPickle as pickle
- except ImportError:
- import pickle
-
- def handle_finish(self):
- # dump the global context
- outfilename = path.join(self.outdir, 'globalcontext.pickle')
- f = open(outfilename, 'wb')
- try:
- pickle.dump(self.globalcontext, f, 2)
- finally:
- f.close()
-
- self.info(bold('dumping search index...'))
- self.indexer.prune(self.env.all_docs)
- f = open(path.join(self.outdir, 'searchindex.pickle'), 'wb')
- try:
- self.indexer.dump(f, 'pickle')
- finally:
- f.close()
-
- # copy the environment file from the doctree dir to the output dir
- # as needed by the web app
- shutil.copyfile(path.join(self.doctreedir, builders.ENV_PICKLE_FILENAME),
- path.join(self.outdir, builders.ENV_PICKLE_FILENAME))
- # touch 'last build' file, used by the web application to determine
- # when to reload its environment and clear the cache
- open(path.join(self.outdir, builders.LAST_BUILD_FILENAME), 'w').close()
-
- builders.PickleHTMLBuilder.handle_finish = handle_finish
-
-
-class DjangoStandaloneHTMLBuilder(builders_html.StandaloneHTMLBuilder):
+class DjangoStandaloneHTMLBuilder(StandaloneHTMLBuilder):
"""
Subclass to add some extra things we need.
"""
@@ -278,9 +216,14 @@ class DjangoStandaloneHTMLBuilder(builders_html.StandaloneHTMLBuilder):
self.warn("cannot create templatebuiltins.js due to missing simplejson dependency")
return
self.info(bold("writing templatebuiltins.js..."))
- xrefs = self.env.reftargets.keys()
- templatebuiltins = dict([('ttags', [n for (t,n) in xrefs if t == 'ttag']),
- ('tfilters', [n for (t,n) in xrefs if t == 'tfilter'])])
+ try:
+ xrefs = self.env.reftargets.keys()
+ templatebuiltins = dict([('ttags', [n for (t,n) in xrefs if t == 'ttag']),
+ ('tfilters', [n for (t,n) in xrefs if t == 'tfilter'])])
+ except AttributeError:
+ xrefs = self.env.domaindata["std"]["objects"]
+ templatebuiltins = dict([('ttags', [n for (t,n) in xrefs if t == 'templatetag']),
+ ('tfilters', [n for (t,n) in xrefs if t == 'templatefilter'])])
outfilename = os.path.join(self.outdir, "templatebuiltins.js")
f = open(outfilename, 'wb')
f.write('var django_template_builtins = ')
diff --git a/docs/_templates/genindex.html b/docs/_theme/djangodocs/genindex.html
index 60c19efd45..486994ae91 100644
--- a/docs/_templates/genindex.html
+++ b/docs/_theme/djangodocs/genindex.html
@@ -1,4 +1,4 @@
-{% extends "!genindex.html" %}
+{% extends "basic/genindex.html" %}
{% block bodyclass %}{% endblock %}
{% block sidebarwrapper %}{% endblock %} \ No newline at end of file
diff --git a/docs/_templates/layout.html b/docs/_theme/djangodocs/layout.html
index 70e029c3ac..ef91dd77a9 100644
--- a/docs/_templates/layout.html
+++ b/docs/_theme/djangodocs/layout.html
@@ -1,4 +1,4 @@
-{% extends "!layout.html" %}
+{% extends "basic/layout.html" %}
{%- macro secondnav() %}
{%- if prev %}
@@ -61,7 +61,7 @@
<a title="Home page" href="{{ pathto('index') }}">Home</a> {{ reldelim2 }}
<a title="Table of contents" href="{{ pathto('contents') }}">Table of contents</a> {{ reldelim2 }}
<a title="Global index" href="{{ pathto('genindex') }}">Index</a> {{ reldelim2 }}
- <a title="Module index" href="{{ pathto('modindex') }}">Modules</a>
+ <a title="Module index" href="{{ pathto('py-modindex') }}">Modules</a>
</div>
<div class="nav">{{ secondnav() }}</div>
</div>
diff --git a/docs/_templates/modindex.html b/docs/_theme/djangodocs/modindex.html
index 96a1d2080a..59a5cb31bd 100644
--- a/docs/_templates/modindex.html
+++ b/docs/_theme/djangodocs/modindex.html
@@ -1,3 +1,3 @@
-{% extends "!modindex.html" %}
+{% extends "basic/modindex.html" %}
{% block bodyclass %}{% endblock %}
{% block sidebarwrapper %}{% endblock %} \ No newline at end of file
diff --git a/docs/_templates/search.html b/docs/_theme/djangodocs/search.html
index 8bd6dbd332..943478ce75 100644
--- a/docs/_templates/search.html
+++ b/docs/_theme/djangodocs/search.html
@@ -1,3 +1,3 @@
-{% extends "!search.html" %}
+{% extends "basic/search.html" %}
{% block bodyclass %}{% endblock %}
{% block sidebarwrapper %}{% endblock %} \ No newline at end of file
diff --git a/docs/_static/default.css b/docs/_theme/djangodocs/static/default.css
index 9dc69ee785..9dc69ee785 100644
--- a/docs/_static/default.css
+++ b/docs/_theme/djangodocs/static/default.css
diff --git a/docs/_static/djangodocs.css b/docs/_theme/djangodocs/static/djangodocs.css
index 4adb8387cc..4adb8387cc 100644
--- a/docs/_static/djangodocs.css
+++ b/docs/_theme/djangodocs/static/djangodocs.css
diff --git a/docs/_static/docicons-behindscenes.png b/docs/_theme/djangodocs/static/docicons-behindscenes.png
index dc901bc867..dc901bc867 100644
--- a/docs/_static/docicons-behindscenes.png
+++ b/docs/_theme/djangodocs/static/docicons-behindscenes.png
Binary files differ
diff --git a/docs/_static/docicons-note.png b/docs/_theme/djangodocs/static/docicons-note.png
index 357545fb32..357545fb32 100644
--- a/docs/_static/docicons-note.png
+++ b/docs/_theme/djangodocs/static/docicons-note.png
Binary files differ
diff --git a/docs/_static/docicons-philosophy.png b/docs/_theme/djangodocs/static/docicons-philosophy.png
index 09f16c785b..09f16c785b 100644
--- a/docs/_static/docicons-philosophy.png
+++ b/docs/_theme/djangodocs/static/docicons-philosophy.png
Binary files differ
diff --git a/docs/_static/homepage.css b/docs/_theme/djangodocs/static/homepage.css
index 276c5470ab..276c5470ab 100644
--- a/docs/_static/homepage.css
+++ b/docs/_theme/djangodocs/static/homepage.css
diff --git a/docs/_static/reset-fonts-grids.css b/docs/_theme/djangodocs/static/reset-fonts-grids.css
index f5238d7c91..f5238d7c91 100644
--- a/docs/_static/reset-fonts-grids.css
+++ b/docs/_theme/djangodocs/static/reset-fonts-grids.css
diff --git a/docs/_theme/djangodocs/theme.conf b/docs/_theme/djangodocs/theme.conf
new file mode 100644
index 0000000000..be43c723ae
--- /dev/null
+++ b/docs/_theme/djangodocs/theme.conf
@@ -0,0 +1,4 @@
+[theme]
+inherit = basic
+stylesheet = default.css
+pygments_style = trac
diff --git a/docs/conf.py b/docs/conf.py
index 90e0a6bcb5..606ee6b5ad 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -8,28 +8,35 @@
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
#
-# All configuration values have a default value; values that are commented out
-# serve to show the default value.
+# All configuration values have a default; values that are commented out
+# serve to show the default.
import sys
import os
-# If your extensions are in another directory, add it here.
-sys.path.append(os.path.join(os.path.dirname(__file__), "_ext"))
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "_ext")))
-# General configuration
-# ---------------------
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["djangodocs"]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ["_templates"]
+# templates_path = []
# The suffix of source filenames.
source_suffix = '.txt'
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
# The master toctree document.
master_doc = 'contents'
@@ -37,8 +44,10 @@ master_doc = 'contents'
project = 'Django'
copyright = 'Django Software Foundation and contributors'
-# The default replacements for |version| and |release|, also used in various
-# other places throughout the built documents.
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
#
# The short X.Y version.
version = '1.2'
@@ -47,14 +56,22 @@ release = version
# The next version to be released
django_next_version = '1.3'
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
-# List of documents that shouldn't be included in the build.
-#unused_docs = []
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
@@ -75,13 +92,35 @@ pygments_style = 'trac'
# Note: exclude_dirnames is new in Sphinx 0.5
exclude_dirnames = ['.svn']
-# Options for HTML output
-# -----------------------
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = "djangodocs"
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ["_theme"]
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
-# The style sheet to use for HTML and HTML Help pages. A file of that name
-# must exist either in Sphinx' static/ path, or in one of the custom paths
-# given in html_static_path.
-html_style = 'default.css'
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -110,17 +149,38 @@ html_translator_class = "djangodocs.DjangoHTMLTranslator"
html_additional_pages = {}
# If false, no module index is generated.
-#html_use_modindex = True
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
-# If true, the reST sources are included in the HTML build as _sources/<name>.
-html_copy_source = True
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Djangodoc'
+modindex_common_prefix = ["django."]
+
-# Options for LaTeX output
-# ------------------------
+# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
@@ -132,9 +192,24 @@ htmlhelp_basename = 'Djangodoc'
# (source start file, target name, title, author, document class [howto/manual]).
#latex_documents = []
latex_documents = [
- ('contents', 'django.tex', 'Django Documentation', 'Django Software Foundation', 'manual'),
+ ('contents', 'django.tex', u'Django Documentation',
+ u'Django Software Foundation', 'manual'),
]
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
@@ -142,10 +217,53 @@ latex_documents = [
#latex_appendices = []
# If false, no module index is generated.
-#latex_use_modindex = True
+#latex_domain_indices = True
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-# If this isn't set to True, the LaTex writer can only handle six levels of headers.
-latex_use_parts = True
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('contents', 'django', 'Django Documentation', ['Django Software Foundation'], 1)
+]
+
+
+# -- Options for Epub output ---------------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = u'Django'
+epub_author = u'Django Software Foundation'
+epub_publisher = u'Django Software Foundation'
+epub_copyright = u'2010, Django Software Foundation'
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+#epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+#epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#epub_identifier = ''
+
+# A unique identification for the text.
+#epub_uid = ''
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_pre_files = []
+
+# HTML files shat should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+#epub_exclude_files = []
+
+# The depth of the table of contents in toc.ncx.
+#epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#epub_tocdup = True
diff --git a/docs/internals/documentation.txt b/docs/internals/documentation.txt
index 81480abf9a..9aa8551266 100644
--- a/docs/internals/documentation.txt
+++ b/docs/internals/documentation.txt
@@ -15,6 +15,11 @@ __ http://docutils.sourceforge.net/
To actually build the documentation locally, you'll currently need to install
Sphinx -- ``easy_install Sphinx`` should do the trick.
+.. note::
+
+ Generation of the Django documentation will work with Sphinx version 0.6
+ or newer, but we recommend going straigh to Sphinx 1.0 or newer.
+
Then, building the html is easy; just ``make html`` from the ``docs`` directory.
To get started contributing, you'll want to read the `ReStructuredText
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index f7aefa457d..4d84dfafbc 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -1027,90 +1027,88 @@ The difference between these two is merely the template used to render them.
The ``InlineModelAdmin`` class is a subclass of ``ModelAdmin`` so it inherits
all the same functionality as well as some of its own:
-``model``
-~~~~~~~~~
+.. attribute:: InlineModelAdmin.model
-The model in which the inline is using. This is required.
+ The model in which the inline is using. This is required.
-``fk_name``
-~~~~~~~~~~~
+.. attribute:: InlineModelAdmin.fk_name
-The name of the foreign key on the model. In most cases this will be dealt
-with automatically, but ``fk_name`` must be specified explicitly if there are
-more than one foreign key to the same parent model.
+ The name of the foreign key on the model. In most cases this will be dealt
+ with automatically, but ``fk_name`` must be specified explicitly if there
+ are more than one foreign key to the same parent model.
-``formset``
-~~~~~~~~~~~
+.. attribute:: InlineModelAdmin.formset
-This defaults to ``BaseInlineFormSet``. Using your own formset can give you
-many possibilities of customization. Inlines are built around
-:ref:`model formsets <model-formsets>`.
+ This defaults to ``BaseInlineFormSet``. Using your own formset can give you
+ many possibilities of customization. Inlines are built around
+ :ref:`model formsets <model-formsets>`.
-``form``
-~~~~~~~~
+.. attribute:: InlineModelAdmin.form
-The value for ``form`` defaults to ``ModelForm``. This is what is
-passed through to ``inlineformset_factory`` when creating the formset for this
-inline.
+ The value for ``form`` defaults to ``ModelForm``. This is what is passed
+ through to ``inlineformset_factory`` when creating the formset for this
+ inline.
.. _ref-contrib-admin-inline-extra:
-``extra``
-~~~~~~~~~
+.. attribute:: InlineModelAdmin.extra
-This controls the number of extra forms the formset will display in addition
-to the initial forms. See the
-:ref:`formsets documentation <topics-forms-formsets>` for more information.
-.. versionadded:: 1.2
+ This controls the number of extra forms the formset will display in addition
+ to the initial forms. See the
+ :ref:`formsets documentation <topics-forms-formsets>` for more information.
+
+ .. versionadded:: 1.2
-For users with JavaScript-enabled browsers, an "Add another" link is
-provided to enable any number of additional inlines to be added in
-addition to those provided as a result of the ``extra`` argument.
+ For users with JavaScript-enabled browsers, an "Add another" link is
+ provided to enable any number of additional inlines to be added in addition
+ to those provided as a result of the ``extra`` argument.
-The dynamic link will not appear if the number of currently displayed
-forms exceeds ``max_num``, or if the user does not have JavaScript
-enabled.
+ The dynamic link will not appear if the number of currently displayed forms
+ exceeds ``max_num``, or if the user does not have JavaScript enabled.
.. _ref-contrib-admin-inline-max-num:
-``max_num``
-~~~~~~~~~~~
+.. attribute:: InlineModelAdmin.max_num
-This controls the maximum number of forms to show in the inline. This doesn't
-directly correlate to the number of objects, but can if the value is small
-enough. See :ref:`model-formsets-max-num` for more information.
+ This controls the maximum number of forms to show in the inline. This
+ doesn't directly correlate to the number of objects, but can if the value
+ is small enough. See :ref:`model-formsets-max-num` for more information.
-``raw_id_fields``
-~~~~~~~~~~~~~~~~~
+.. attribute:: InlineModelAdmin.raw_id_fields
-By default, Django's admin uses a select-box interface (<select>) for
-fields that are ``ForeignKey``. Sometimes you don't want to incur the
-overhead of having to select all the related instances to display in the
-drop-down.
+ By default, Django's admin uses a select-box interface (<select>) for
+ fields that are ``ForeignKey``. Sometimes you don't want to incur the
+ overhead of having to select all the related instances to display in the
+ drop-down.
-``raw_id_fields`` is a list of fields you would like to change
-into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``::
+ ``raw_id_fields`` is a list of fields you would like to change into a
+ ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``::
- class BookInline(admin.TabularInline):
- model = Book
- raw_id_fields = ("pages",)
+ class BookInline(admin.TabularInline):
+ model = Book
+ raw_id_fields = ("pages",)
-``template``
-~~~~~~~~~~~~
-The template used to render the inline on the page.
+.. attribute:: InlineModelAdmin.template
-``verbose_name``
-~~~~~~~~~~~~~~~~
+ The template used to render the inline on the page.
-An override to the ``verbose_name`` found in the model's inner ``Meta`` class.
+.. attribute:: InlineModelAdmin.verbose_name
-``verbose_name_plural``
-~~~~~~~~~~~~~~~~~~~~~~~
+ An override to the ``verbose_name`` found in the model's inner ``Meta``
+ class.
+
+.. attribute:: InlineModelAdmin.verbose_name_plural
+
+ An override to the ``verbose_name_plural`` found in the model's inner
+ ``Meta`` class.
+
+.. attribute:: InlineModelAdmin.can_delete
+
+ Specifies whether or not inline objects can be deleted in the inline.
+ Defaults to ``True``.
-An override to the ``verbose_name_plural`` found in the model's inner ``Meta``
-class.
Working with a model with two or more foreign keys to the same parent model
---------------------------------------------------------------------------
diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt
index dd14dd1ce7..1e72e0c662 100644
--- a/docs/ref/models/instances.txt
+++ b/docs/ref/models/instances.txt
@@ -107,7 +107,7 @@ special key that is used for errors that are tied to the entire model instead
of to a specific field. You can access these errors with ``NON_FIELD_ERRORS``::
- from django.core.validators import ValidationError, NON_FIELD_ERRORS
+ from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
article.full_clean()
except ValidationError, e:
diff --git a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py
index e3137f2710..7315408d49 100644
--- a/tests/regressiontests/backends/models.py
+++ b/tests/regressiontests/backends/models.py
@@ -1,3 +1,5 @@
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
from django.conf import settings
from django.db import models
from django.db import connection, DEFAULT_DB_ALIAS
@@ -37,6 +39,21 @@ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person,blank=True)
+class Tag(models.Model):
+ name = models.CharField(max_length=30)
+ content_type = models.ForeignKey(ContentType, related_name='backend_tags')
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+
+
+class Post(models.Model):
+ name = models.CharField(max_length=30)
+ text = models.TextField()
+ tags = generic.GenericRelation('Tag')
+
+ class Meta:
+ db_table = 'CaseSensitive_Post'
+
qn = connection.ops.quote_name
__test__ = {'API_TESTS': """
diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
index ee3ccdc72f..19d7fc5f6c 100644
--- a/tests/regressiontests/backends/tests.py
+++ b/tests/regressiontests/backends/tests.py
@@ -6,7 +6,7 @@ import unittest
from django.conf import settings
from django.core import management
from django.core.management.color import no_style
-from django.db import backend, connection, DEFAULT_DB_ALIAS
+from django.db import backend, connection, connections, DEFAULT_DB_ALIAS
from django.db.backends.signals import connection_created
from django.test import TestCase
@@ -137,6 +137,23 @@ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
for statement in connection.ops.sql_flush(no_style(), tables, sequences):
cursor.execute(statement)
+class SequenceResetTest(TestCase):
+ def test_generic_relation(self):
+ "Sequence names are correct when resetting generic relations (Ref #13941)"
+ # Create an object with a manually specified PK
+ models.Post.objects.create(id=10, name='1st post', text='hello world')
+
+ # Reset the sequences for the database
+ cursor = connection.cursor()
+ commands = connections[DEFAULT_DB_ALIAS].ops.sequence_reset_sql(no_style(), [models.Post])
+ for sql in commands:
+ cursor.execute(sql)
+
+ # If we create a new object now, it should have a PK greater
+ # than the PK we specified manually.
+ obj = models.Post.objects.create(name='New post', text='goodbye world')
+ self.assertTrue(obj.pk > 10)
+
def connection_created_test(sender, **kwargs):
print 'connection_created signal'
diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py
index 3d6284e881..d351c550b7 100644
--- a/tests/regressiontests/templates/filters.py
+++ b/tests/regressiontests/templates/filters.py
@@ -328,7 +328,12 @@ def get_filter_tests():
'join03': (r'{{ a|join:" &amp; " }}', {'a': ['alpha', 'beta & me']}, 'alpha &amp; beta &amp; me'),
'join04': (r'{% autoescape off %}{{ a|join:" &amp; " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha &amp; beta & me'),
-
+ # Test that joining with unsafe joiners don't result in unsafe strings (#11377)
+ 'join05': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': ' & '}, 'alpha &amp; beta &amp; me'),
+ 'join06': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta &amp; me'),
+ 'join07': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': ' & ' }, 'alpha &amp; beta &amp; me'),
+ 'join08': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta &amp; me'),
+
'date01': (r'{{ d|date:"m" }}', {'d': datetime(2008, 1, 1)}, '01'),
'date02': (r'{{ d|date }}', {'d': datetime(2008, 1, 1)}, 'Jan. 1, 2008'),
#Ticket 9520: Make sure |date doesn't blow up on non-dates
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 5902e8d5e7..2f2df65e96 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -506,6 +506,17 @@ class Templates(unittest.TestCase):
'basic-syntax28': ("{{ a.b }}", {'a': SilentGetItemClass()}, ('', 'INVALID')),
'basic-syntax29': ("{{ a.b }}", {'a': SilentAttrClass()}, ('', 'INVALID')),
+ # Something that starts like a number but has an extra lookup works as a lookup.
+ 'basic-syntax30': ("{{ 1.2.3 }}", {"1": {"2": {"3": "d"}}}, "d"),
+ 'basic-syntax31': ("{{ 1.2.3 }}", {"1": {"2": ("a", "b", "c", "d")}}, "d"),
+ 'basic-syntax32': ("{{ 1.2.3 }}", {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))}, "d"),
+ 'basic-syntax33': ("{{ 1.2.3 }}", {"1": ("xxxx", "yyyy", "abcd")}, "d"),
+ 'basic-syntax34': ("{{ 1.2.3 }}", {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})}, "d"),
+
+ # Numbers are numbers even if their digits are in the context.
+ 'basic-syntax35': ("{{ 1 }}", {"1": "abc"}, "1"),
+ 'basic-syntax36': ("{{ 1.2 }}", {"1": "abc"}, "1.2"),
+
# List-index syntax allows a template to access a certain item of a subscriptable object.
'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"),
@@ -592,7 +603,7 @@ class Templates(unittest.TestCase):
#filters should accept empty string constants
'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""),
-
+
### COMMENT SYNTAX ########################################################
'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),
@@ -1285,7 +1296,8 @@ class Templates(unittest.TestCase):
# Regression test for #11270.
'cache17': ('{% load cache %}{% cache 10 long_cache_key poem %}Some Content{% endcache %}', {'poem': 'Oh freddled gruntbuggly/Thy micturations are to me/As plurdled gabbleblotchits/On a lurgid bee/That mordiously hath bitled out/Its earted jurtles/Into a rancid festering/Or else I shall rend thee in the gobberwarts with my blurglecruncheon/See if I dont.'}, 'Some Content'),
-
+
+
### AUTOESCAPE TAG ##############################################
'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"),
'autoescape-tag02': ("{% autoescape off %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"),
@@ -1314,6 +1326,23 @@ class Templates(unittest.TestCase):
# implementation details (fortunately, the (no)autoescape block
# tags can be used in those cases)
'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError),
+
+ # ifqeual compares unescaped vales.
+ 'autoescape-ifequal01': ('{% ifequal var "this & that" %}yes{% endifequal %}', { "var": "this & that" }, "yes" ),
+
+ # Arguments to filters are 'safe' and manipulate their input unescaped.
+ 'autoescape-filters01': ('{{ var|cut:"&" }}', { "var": "this & that" }, "this that" ),
+ 'autoescape-filters02': ('{{ var|join:" & \" }}', { "var": ("Tom", "Dick", "Harry") }, "Tom & Dick & Harry" ),
+
+ # Literal strings are safe.
+ 'autoescape-literals01': ('{{ "this & that" }}',{}, "this & that" ),
+
+ # Iterating over strings outputs safe characters.
+ 'autoescape-stringiterations01': ('{% for l in var %}{{ l }},{% endfor %}', {'var': 'K&R'}, "K,&amp;,R," ),
+
+ # Escape requirement survives lookup.
+ 'autoescape-lookup01': ('{{ var.key }}', { "var": {"key": "this & that" }}, "this &amp; that" ),
+
}