mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
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.
This commit is contained in:
parent
1cf109515a
commit
7b42036752
1
AUTHORS
1
AUTHORS
@ -701,6 +701,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Zach Thompson <zthompson47@gmail.com>
|
Zach Thompson <zthompson47@gmail.com>
|
||||||
Zain Memon
|
Zain Memon
|
||||||
Zak Johnson <zakj@nox.cx>
|
Zak Johnson <zakj@nox.cx>
|
||||||
|
Žan Anderle <zan.anderle@gmail.com>
|
||||||
Zbigniew Siciarz <zbigniew@siciarz.net>
|
Zbigniew Siciarz <zbigniew@siciarz.net>
|
||||||
zegor
|
zegor
|
||||||
Zlatko Mašek <zlatko.masek@gmail.com>
|
Zlatko Mašek <zlatko.masek@gmail.com>
|
||||||
|
@ -22,11 +22,10 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="content-main">
|
<div id="content-main">
|
||||||
<h1>{{ summary }}</h1>
|
<h1>{{ name }}</h1>
|
||||||
|
<h2 class="subhead">{{ summary }}</h2>
|
||||||
|
|
||||||
{% if description %}
|
{{ description }}
|
||||||
<p>{% filter linebreaksbr %}{% trans description %}{% endfilter %}</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="module">
|
<div class="module">
|
||||||
<table class="model">
|
<table class="model">
|
||||||
|
@ -178,18 +178,25 @@ class ModelDetailView(BaseAdminDocsView):
|
|||||||
template_name = 'admin_doc/model_detail.html'
|
template_name = 'admin_doc/model_detail.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
model_name = self.kwargs['model_name']
|
||||||
# Get the model class.
|
# Get the model class.
|
||||||
try:
|
try:
|
||||||
app_config = apps.get_app_config(self.kwargs['app_label'])
|
app_config = apps.get_app_config(self.kwargs['app_label'])
|
||||||
except LookupError:
|
except LookupError:
|
||||||
raise Http404(_("App %(app_label)r not found") % self.kwargs)
|
raise Http404(_("App %(app_label)r not found") % self.kwargs)
|
||||||
try:
|
try:
|
||||||
model = app_config.get_model(self.kwargs['model_name'])
|
model = app_config.get_model(model_name)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs)
|
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs)
|
||||||
|
|
||||||
opts = model._meta
|
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.
|
# Gather fields/field descriptions.
|
||||||
fields = []
|
fields = []
|
||||||
for field in opts.fields:
|
for field in opts.fields:
|
||||||
@ -271,9 +278,8 @@ class ModelDetailView(BaseAdminDocsView):
|
|||||||
})
|
})
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
'name': '%s.%s' % (opts.app_label, opts.object_name),
|
'name': '%s.%s' % (opts.app_label, opts.object_name),
|
||||||
# Translators: %s is an object type name
|
'summary': title,
|
||||||
'summary': _("Attributes on %s objects") % opts.object_name,
|
'description': body,
|
||||||
'description': model.__doc__,
|
|
||||||
'fields': fields,
|
'fields': fields,
|
||||||
})
|
})
|
||||||
return super(ModelDetailView, self).get_context_data(**kwargs)
|
return super(ModelDetailView, self).get_context_data(**kwargs)
|
||||||
|
@ -76,6 +76,11 @@ Minor features
|
|||||||
<django.contrib.admin.ModelAdmin.show_full_result_count>` to control whether
|
<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.
|
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`
|
:mod:`django.contrib.auth`
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -18,6 +18,18 @@ class Family(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Person(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")
|
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")
|
last_name = models.CharField(max_length=200, help_text="The person's last name")
|
||||||
company = models.ForeignKey(Company, help_text="place of work")
|
company = models.ForeignKey(Company, help_text="place of work")
|
||||||
|
@ -291,3 +291,110 @@ class TestModelDetailView(AdminDocsTestCase):
|
|||||||
|
|
||||||
fields = response.context_data.get('fields')
|
fields = response.context_data.get('fields')
|
||||||
self.assertEqual(len(fields), 2)
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user