Fixed #32421 -- Made admindocs ModelDetailView show model cached properties.

This commit is contained in:
Ramon Saraiva 2021-02-10 09:33:57 -03:00 committed by Mariusz Felisiak
parent 4372233ebf
commit dcb094abe8
5 changed files with 21 additions and 4 deletions

View File

@ -16,6 +16,7 @@ from django.http import Http404
from django.template.engine import Engine from django.template.engine import Engine
from django.urls import get_mod_func, get_resolver, get_urlconf from django.urls import get_mod_func, get_resolver, get_urlconf
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.inspect import ( from django.utils.inspect import (
func_accepts_kwargs, func_accepts_var_args, get_func_full_args, func_accepts_kwargs, func_accepts_var_args, get_func_full_args,
method_has_no_args, method_has_no_args,
@ -250,7 +251,7 @@ class ModelDetailView(BaseAdminDocsView):
methods = [] methods = []
# Gather model methods. # Gather model methods.
for func_name, func in model.__dict__.items(): for func_name, func in model.__dict__.items():
if inspect.isfunction(func) or isinstance(func, property): if inspect.isfunction(func) or isinstance(func, (cached_property, property)):
try: try:
for exclude in MODEL_METHODS_EXCLUDE: for exclude in MODEL_METHODS_EXCLUDE:
if func_name.startswith(exclude): if func_name.startswith(exclude):
@ -261,9 +262,10 @@ class ModelDetailView(BaseAdminDocsView):
verbose = verbose and ( verbose = verbose and (
utils.parse_rst(cleandoc(verbose), 'model', _('model:') + opts.model_name) utils.parse_rst(cleandoc(verbose), 'model', _('model:') + opts.model_name)
) )
# Show properties and methods without arguments as fields. # Show properties, cached_properties, and methods without
# Otherwise, show as a 'method with arguments'. # arguments as fields. Otherwise, show as a 'method with
if isinstance(func, property): # arguments'.
if isinstance(func, (cached_property, property)):
fields.append({ fields.append({
'name': func_name, 'name': func_name,
'data_type': get_return_data_type(func_name), 'data_type': get_return_data_type(func_name),

View File

@ -55,6 +55,10 @@ system along with all the fields, properties, and methods available on it.
Relationships to other models appear as hyperlinks. Descriptions are pulled Relationships to other models appear as hyperlinks. Descriptions are pulled
from ``help_text`` attributes on fields or from docstrings on model methods. from ``help_text`` attributes on fields or from docstrings on model methods.
.. versionchanged:: 4.0
Older versions don't display model cached properties.
A model with useful documentation might look like this:: A model with useful documentation might look like this::
class BlogEntry(models.Model): class BlogEntry(models.Model):

View File

@ -43,6 +43,8 @@ Minor features
* The admindocs now allows esoteric setups where :setting:`ROOT_URLCONF` is not * The admindocs now allows esoteric setups where :setting:`ROOT_URLCONF` is not
a string. a string.
* The model section of the ``admindocs`` now shows cached properties.
:mod:`django.contrib.auth` :mod:`django.contrib.auth`
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -3,6 +3,7 @@ Models for testing various aspects of the djang.contrib.admindocs app
""" """
from django.db import models from django.db import models
from django.utils.functional import cached_property
class Company(models.Model): class Company(models.Model):
@ -56,6 +57,10 @@ class Person(models.Model):
def a_property(self): def a_property(self):
return 'a_property' return 'a_property'
@cached_property
def a_cached_property(self):
return 'a_cached_property'
def suffix_company_name(self, suffix='ltd'): def suffix_company_name(self, suffix='ltd'):
return self.company.name + suffix return self.company.name + suffix

View File

@ -232,6 +232,10 @@ class TestModelDetailView(TestDataMixin, AdminDocsTestCase):
"""Model properties are displayed as fields.""" """Model properties are displayed as fields."""
self.assertContains(self.response, '<td>a_property</td>') self.assertContains(self.response, '<td>a_property</td>')
def test_instance_of_cached_property_methods_are_displayed(self):
"""Model cached properties are displayed as fields."""
self.assertContains(self.response, '<td>a_cached_property</td>')
def test_method_data_types(self): def test_method_data_types(self):
company = Company.objects.create(name="Django") company = Company.objects.create(name="Django")
person = Person.objects.create(first_name="Human", last_name="User", company=company) person = Person.objects.create(first_name="Human", last_name="User", company=company)