diff --git a/django/contrib/admindocs/utils.py b/django/contrib/admindocs/utils.py index 3a7ca4e781..5b28a8d2c6 100644 --- a/django/contrib/admindocs/utils.py +++ b/django/contrib/admindocs/utils.py @@ -99,6 +99,21 @@ ROLES = { "tag": "%s/tags/#%s", } +explicit_title_re = re.compile(r"^(.+?)\s*(?$", re.DOTALL) + + +def split_explicit_title(text): + """ + Split role content into title and target, if given. + + From sphinx.util.nodes.split_explicit_title + See https://github.com/sphinx-doc/sphinx/blob/230ccf2/sphinx/util/nodes.py#L389 + """ + match = explicit_title_re.match(text) + if match: + return True, match.group(1), match.group(2) + return False, text, text + def create_reference_role(rolename, urlbase): # Views and template names are case-sensitive. @@ -107,14 +122,15 @@ def create_reference_role(rolename, urlbase): def _role(name, rawtext, text, lineno, inliner, options=None, content=None): if options is None: options = {} + _, title, target = split_explicit_title(text) node = docutils.nodes.reference( rawtext, - text, + title, refuri=( urlbase % ( inliner.document.settings.link_base, - text if is_case_sensitive else text.lower(), + target if is_case_sensitive else target.lower(), ) ), **options, diff --git a/docs/ref/contrib/admin/admindocs.txt b/docs/ref/contrib/admin/admindocs.txt index edc29b4a5c..240def8efb 100644 --- a/docs/ref/contrib/admin/admindocs.txt +++ b/docs/ref/contrib/admin/admindocs.txt @@ -31,6 +31,8 @@ Once those steps are complete, you can start browsing the documentation by going to your admin interface and clicking the "Documentation" link in the upper right of the page. +.. _admindocs-helpers: + Documentation helpers ===================== @@ -47,6 +49,13 @@ Template filters ``:filter:`filtername``` Templates ``:template:`path/to/template.html``` ================= ======================= +Each of these support custom link text with the format +``:role:`link text ```. For example, ``:tag:`block ```. + +.. versionchanged:: 5.2 + + Support for custom link text was added. + Model reference =============== diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt index 8327de7405..d07e9cb098 100644 --- a/docs/releases/5.2.txt +++ b/docs/releases/5.2.txt @@ -44,7 +44,10 @@ Minor features :mod:`django.contrib.admindocs` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ... +* Links to components in docstrings now supports custom link text, using the + format ``:role:`link text ```. See :ref:`documentation helpers + ` for more details. + :mod:`django.contrib.auth` ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/admin_docs/models.py b/tests/admin_docs/models.py index b4ef84caba..4b52b4a4ea 100644 --- a/tests/admin_docs/models.py +++ b/tests/admin_docs/models.py @@ -15,6 +15,16 @@ class Group(models.Model): class Family(models.Model): + """ + Links with different link text. + + This is a line with tag :tag:`extends ` + This is a line with model :model:`Family ` + This is a line with view :view:`Index ` + This is a line with template :template:`index template ` + This is a line with filter :filter:`example filter ` + """ + last_name = models.CharField(max_length=200) diff --git a/tests/admin_docs/test_views.py b/tests/admin_docs/test_views.py index c48a89a1b0..f7232a7e03 100644 --- a/tests/admin_docs/test_views.py +++ b/tests/admin_docs/test_views.py @@ -441,6 +441,25 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase): self.assertContains(self.response, body, html=True) self.assertContains(self.response, model_body, html=True) + def test_model_docstring_built_in_tag_links(self): + summary = "Links with different link text." + body = ( + '

This is a line with tag extends\n' + 'This is a line with model Family\n' + 'This is a line with view Index\n' + 'This is a line with template index template\n' + 'This is a line with filter example filter

' + ) + url = reverse("django-admindocs-models-detail", args=["admin_docs", "family"]) + response = self.client.get(url) + self.assertContains(response, summary, html=True) + self.assertContains(response, body, html=True) + def test_model_detail_title(self): self.assertContains(self.response, "

admin_docs.Person

", html=True)