diff --git a/sphinxcontrib_django/docstrings/attributes.py b/sphinxcontrib_django/docstrings/attributes.py index 86c36bd..ba94572 100644 --- a/sphinxcontrib_django/docstrings/attributes.py +++ b/sphinxcontrib_django/docstrings/attributes.py @@ -120,17 +120,20 @@ def get_field_details(app, field): ] if hasattr(field, "choices") and field.choices: field_details.extend(["", "Choices:", ""]) + # ensure choices are evaluated + choices = list(field.choices) field_details.extend( [ f"* ``{key}``" if key != "" else "* ``''`` (Empty string)" - for key, value in field.choices[:choices_limit] + for key, value in choices[:choices_limit] ] ) + # Check if list has been truncated - if len(field.choices) > choices_limit: + if len(choices) > choices_limit: # If only one element has been truncated, just list it as well - if len(field.choices) == choices_limit + 1: - field_details.append(f"* ``{field.choices[-1][0]}``") + if len(choices) == choices_limit + 1: + field_details.append(f"* ``{choices[-1][0]}``") else: - field_details.append(f"* and {len(field.choices) - choices_limit} more") + field_details.append(f"* and {len(choices) - choices_limit} more") return field_details diff --git a/tests/roots/test-docstrings/dummy_django_app/models.py b/tests/roots/test-docstrings/dummy_django_app/models.py index 8be4566..4d74c28 100644 --- a/tests/roots/test-docstrings/dummy_django_app/models.py +++ b/tests/roots/test-docstrings/dummy_django_app/models.py @@ -1,5 +1,6 @@ from __future__ import annotations +from django import VERSION from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models @@ -87,6 +88,10 @@ class ChildModelB(AbstractModel): pass +def callable_choices(): + return [("Something", "Not empty")] + + class ChoiceModel(models.Model): choice_limit_below = models.IntegerField( choices=[(i, i) for i in range(CHOICES_LIMIT - 1)] @@ -100,6 +105,8 @@ class ChoiceModel(models.Model): choice_with_empty = models.CharField( choices=[("", "Empty"), ("Something", "Not empty")] ) + if VERSION >= (5, 0): + choice_with_callable = models.CharField(choices=callable_choices) class TaggedItem(models.Model): diff --git a/tests/test_attribute_docstrings.py b/tests/test_attribute_docstrings.py index 3b582c3..9ade2cb 100644 --- a/tests/test_attribute_docstrings.py +++ b/tests/test_attribute_docstrings.py @@ -1,4 +1,7 @@ import pytest +from django import VERSION as DJANGO_VERSION + +SUPPORT_CALLABLE_CHOICES = DJANGO_VERSION >= (5, 0) try: from phonenumber_field.modelfields import PhoneNumberField # noqa: F401 @@ -429,6 +432,30 @@ def test_choice_field_empty(app, do_autodoc): ] +if SUPPORT_CALLABLE_CHOICES: + + @pytest.mark.sphinx("html", testroot="docstrings") + def test_choice_field_callable(app, do_autodoc): + actual = do_autodoc( + app, "attribute", "dummy_django_app.models.ChoiceModel.choice_with_callable" + ) + print(actual) + assert list(actual) == [ + "", + ".. py:attribute:: ChoiceModel.choice_with_callable", + " :module: dummy_django_app.models", + "", + " Type: :class:`~django.db.models.CharField`", + "", + " Choice with callable", + "", + " Choices:", + "", + " * ``Something``", + "", + ] + + if PHONENUMBER: @pytest.mark.sphinx("html", testroot="docstrings")