mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +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:
		
							
								
								
									
										1
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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> | ||||
|   | ||||
| @@ -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"> | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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` | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user