summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZan Anderle <zan.anderle@gmail.com>2014-11-03 20:16:28 +0100
committerTim Graham <timograham@gmail.com>2014-11-03 15:30:46 -0500
commit7b420367524ad9f29b8bf0284f4b40bd6cfc8b93 (patch)
tree15e041668111d6e566684d480b3e72fcdd88a1c4
parent1cf109515a08aecb34c48bedb1df8027f7fad701 (diff)
Fixed #5405 -- Added admindocs support for reStructured text in model docstrings
Thanks elvard and gkmngrgn for work on the patch and Markus H. for review.
-rw-r--r--AUTHORS1
-rw-r--r--django/contrib/admindocs/templates/admin_doc/model_detail.html7
-rw-r--r--django/contrib/admindocs/views.py14
-rw-r--r--docs/releases/1.8.txt5
-rw-r--r--tests/admin_docs/models.py12
-rw-r--r--tests/admin_docs/tests.py107
6 files changed, 138 insertions, 8 deletions
diff --git a/AUTHORS b/AUTHORS
index 7fb6950996..6a527fa229 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -701,6 +701,7 @@ answer newbie questions, and generally made Django that much better:
Zach Thompson <zthompson47@gmail.com>
Zain Memon
Zak Johnson <zakj@nox.cx>
+ Žan Anderle <zan.anderle@gmail.com>
Zbigniew Siciarz <zbigniew@siciarz.net>
zegor
Zlatko Mašek <zlatko.masek@gmail.com>
diff --git a/django/contrib/admindocs/templates/admin_doc/model_detail.html b/django/contrib/admindocs/templates/admin_doc/model_detail.html
index 77c8c2430c..59d8abcd8f 100644
--- a/django/contrib/admindocs/templates/admin_doc/model_detail.html
+++ b/django/contrib/admindocs/templates/admin_doc/model_detail.html
@@ -22,11 +22,10 @@
{% block content %}
<div id="content-main">
-<h1>{{ summary }}</h1>
+<h1>{{ name }}</h1>
+<h2 class="subhead">{{ summary }}</h2>
-{% if description %}
- <p>{% filter linebreaksbr %}{% trans description %}{% endfilter %}</p>
-{% endif %}
+{{ description }}
<div class="module">
<table class="model">
diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py
index 6cd5df1e5c..f4a49431f6 100644
--- a/django/contrib/admindocs/views.py
+++ b/django/contrib/admindocs/views.py
@@ -178,18 +178,25 @@ class ModelDetailView(BaseAdminDocsView):
template_name = 'admin_doc/model_detail.html'
def get_context_data(self, **kwargs):
+ model_name = self.kwargs['model_name']
# Get the model class.
try:
app_config = apps.get_app_config(self.kwargs['app_label'])
except LookupError:
raise Http404(_("App %(app_label)r not found") % self.kwargs)
try:
- model = app_config.get_model(self.kwargs['model_name'])
+ model = app_config.get_model(model_name)
except LookupError:
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs)
opts = model._meta
+ title, body, metadata = utils.parse_docstring(model.__doc__)
+ if title:
+ title = utils.parse_rst(title, 'model', _('model:') + model_name)
+ if body:
+ body = utils.parse_rst(body, 'model', _('model:') + model_name)
+
# Gather fields/field descriptions.
fields = []
for field in opts.fields:
@@ -271,9 +278,8 @@ class ModelDetailView(BaseAdminDocsView):
})
kwargs.update({
'name': '%s.%s' % (opts.app_label, opts.object_name),
- # Translators: %s is an object type name
- 'summary': _("Attributes on %s objects") % opts.object_name,
- 'description': model.__doc__,
+ 'summary': title,
+ 'description': body,
'fields': fields,
})
return super(ModelDetailView, self).get_context_data(**kwargs)
diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt
index 875f17f40f..3cf7e77038 100644
--- a/docs/releases/1.8.txt
+++ b/docs/releases/1.8.txt
@@ -76,6 +76,11 @@ Minor features
<django.contrib.admin.ModelAdmin.show_full_result_count>` to control whether
or not the full count of objects should be displayed on a filtered admin page.
+:mod:`django.contrib.admindocs`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* reStructuredText is now parsed in model docstrings.
+
:mod:`django.contrib.auth`
^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/admin_docs/models.py b/tests/admin_docs/models.py
index 9b8f388f4e..7e8b6c37e8 100644
--- a/tests/admin_docs/models.py
+++ b/tests/admin_docs/models.py
@@ -18,6 +18,18 @@ class Family(models.Model):
class Person(models.Model):
+ """
+ Stores information about a person, related to :model:`myapp.Company`.
+
+ **Notes**
+
+ Use ``save_changes()`` when saving this object.
+
+ ``company``
+ Field storing :model:`myapp.Company` where the person works.
+
+ (DESCRIPTION)
+ """
first_name = models.CharField(max_length=200, help_text="The person's first name")
last_name = models.CharField(max_length=200, help_text="The person's last name")
company = models.ForeignKey(Company, help_text="place of work")
diff --git a/tests/admin_docs/tests.py b/tests/admin_docs/tests.py
index 6234834955..2b0f6dc065 100644
--- a/tests/admin_docs/tests.py
+++ b/tests/admin_docs/tests.py
@@ -291,3 +291,110 @@ class TestModelDetailView(AdminDocsTestCase):
fields = response.context_data.get('fields')
self.assertEqual(len(fields), 2)
+
+ def test_model_docstring_renders_correctly(self):
+ summary = (
+ '<h2 class="subhead"><p>Stores information about a person, related to <a class="reference external" '
+ 'href="/admindocs/models/myapp.company/">myapp.Company</a>.</p></h2>'
+ )
+ subheading = '<p><strong>Notes</strong></p>'
+ body = '<p>Use <tt class="docutils literal">save_changes()</tt> when saving this object.</p>'
+ model_body = (
+ '<dl class="docutils"><dt><tt class="'
+ 'docutils literal">company</tt></dt><dd>Field storing <a class="'
+ 'reference external" href="/admindocs/models/myapp.company/">'
+ 'myapp.Company</a> where the person works.</dd></dl>'
+ )
+ self.assertContains(self.response, 'DESCRIPTION')
+ self.assertContains(self.response, summary, html=True)
+ self.assertContains(self.response, subheading, html=True)
+ self.assertContains(self.response, body, html=True)
+ self.assertContains(self.response, model_body, html=True)
+
+
+@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.")
+class TestUtils(AdminDocsTestCase):
+ """
+ This __doc__ output is required for testing. I copied this example from
+ `admindocs` documentation. (TITLE)
+
+ Display an individual :model:`myapp.MyModel`.
+
+ **Context**
+
+ ``RequestContext``
+
+ ``mymodel``
+ An instance of :model:`myapp.MyModel`.
+
+ **Template:**
+
+ :template:`myapp/my_template.html` (DESCRIPTION)
+
+ some_metadata: some data
+
+ """
+
+ def setUp(self):
+ self.docstring = self.__doc__
+
+ def test_trim_docstring(self):
+ trim_docstring_output = utils.trim_docstring(self.docstring)
+ trimmed_docstring = (
+ 'This __doc__ output is required for testing. I copied this '
+ 'example from\n`admindocs` documentation. (TITLE)\n\n'
+ 'Display an individual :model:`myapp.MyModel`.\n\n'
+ '**Context**\n\n``RequestContext``\n\n``mymodel``\n'
+ ' An instance of :model:`myapp.MyModel`.\n\n'
+ '**Template:**\n\n:template:`myapp/my_template.html` '
+ '(DESCRIPTION)\n\nsome_metadata: some data'
+ )
+ self.assertEqual(trim_docstring_output, trimmed_docstring)
+
+ def test_parse_docstring(self):
+ title, description, metadata = utils.parse_docstring(self.docstring)
+ docstring_title = (
+ 'This __doc__ output is required for testing. I copied this example from\n'
+ '`admindocs` documentation. (TITLE)'
+ )
+ docstring_description = (
+ 'Display an individual :model:`myapp.MyModel`.\n\n'
+ '**Context**\n\n``RequestContext``\n\n``mymodel``\n'
+ ' An instance of :model:`myapp.MyModel`.\n\n'
+ '**Template:**\n\n:template:`myapp/my_template.html` '
+ '(DESCRIPTION)'
+ )
+ self.assertEqual(title, docstring_title)
+ self.assertEqual(description, docstring_description)
+ self.assertEqual(metadata, {'some_metadata': 'some data'})
+
+ def test_title_output(self):
+ title, description, metadata = utils.parse_docstring(self.docstring)
+ title_output = utils.parse_rst(title, 'model', 'model:admindocs')
+ self.assertIn('TITLE', title_output)
+
+ title_rendered = (
+ '<p>This __doc__ output is required for testing. I copied this '
+ 'example from\n<a class="reference external" '
+ 'href="/admindocs/models/admindocs/">admindocs</a> documentation. '
+ '(TITLE)</p>\n'
+ )
+ self.assertHTMLEqual(title_output, title_rendered)
+
+ def test_description_output(self):
+ title, description, metadata = utils.parse_docstring(self.docstring)
+ description_output = utils.parse_rst(description, 'model', 'model:admindocs')
+
+ description_rendered = (
+ '<p>Display an individual <a class="reference external" '
+ 'href="/admindocs/models/myapp.mymodel/">myapp.MyModel</a>.</p>\n'
+ '<p><strong>Context</strong></p>\n<p><tt class="docutils literal">'
+ 'RequestContext</tt></p>\n<dl class="docutils">\n<dt><tt class="'
+ 'docutils literal">mymodel</tt></dt>\n<dd>An instance of <a class="'
+ 'reference external" href="/admindocs/models/myapp.mymodel/">'
+ 'myapp.MyModel</a>.</dd>\n</dl>\n<p><strong>Template:</strong></p>'
+ '\n<p><a class="reference external" href="/admindocs/templates/'
+ 'myapp/my_template.html/">myapp/my_template.html</a> (DESCRIPTION)'
+ '</p>\n'
+ )
+ self.assertHTMLEqual(description_output, description_rendered)