From 7b420367524ad9f29b8bf0284f4b40bd6cfc8b93 Mon Sep 17 00:00:00 2001 From: Zan Anderle Date: Mon, 3 Nov 2014 20:16:28 +0100 Subject: [PATCH] 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. --- AUTHORS | 1 + .../templates/admin_doc/model_detail.html | 7 +- django/contrib/admindocs/views.py | 14 ++- docs/releases/1.8.txt | 5 + tests/admin_docs/models.py | 12 ++ tests/admin_docs/tests.py | 107 ++++++++++++++++++ 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 Zain Memon Zak Johnson + Žan Anderle Zbigniew Siciarz zegor Zlatko Mašek 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 %}
-

{{ summary }}

+

{{ name }}

+

{{ summary }}

-{% if description %} -

{% filter linebreaksbr %}{% trans description %}{% endfilter %}

-{% endif %} +{{ description }}
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 ` 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 = ( + '

Stores information about a person, related to myapp.Company.

' + ) + subheading = '

Notes

' + body = '

Use save_changes() when saving this object.

' + model_body = ( + '
company
Field storing ' + 'myapp.Company where the person works.
' + ) + 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 = ( + '

This __doc__ output is required for testing. I copied this ' + 'example from\nadmindocs documentation. ' + '(TITLE)

\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 = ( + '

Display an individual myapp.MyModel.

\n' + '

Context

\n

' + 'RequestContext

\n
\n
mymodel
\n
An instance of ' + 'myapp.MyModel.
\n
\n

Template:

' + '\n

myapp/my_template.html (DESCRIPTION)' + '

\n' + ) + self.assertHTMLEqual(description_output, description_rendered)