diff --git a/setup.cfg b/setup.cfg index ddf45571..964a5ae0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,6 @@ scripts = bin/use_external_components install_requires = django>=3.2.0,<4.2 - django-choices django-filter>=2.0 django-solo djangorestframework~=3.12.0 @@ -53,6 +52,7 @@ install_requires = PyJWT>=2.0.0 pyyaml requests + coreapi tests_require = pytest pytest-django diff --git a/vng_api_common/audittrails/viewsets.py b/vng_api_common/audittrails/viewsets.py index 59291e75..20310482 100644 --- a/vng_api_common/audittrails/viewsets.py +++ b/vng_api_common/audittrails/viewsets.py @@ -69,6 +69,9 @@ def create_audittrail( toelichting = get_header(self.request, "X-Audit-Toelichting") or "" logrecord_id = get_header(self.request, "X-NLX-Logrecord-ID") or "" + action_labels = dict( + zip(CommonResourceAction.names, CommonResourceAction.labels) + ) trail = AuditTrail( bron=self.audit.component_name, @@ -76,7 +79,7 @@ def create_audittrail( applicatie_id=app_id, applicatie_weergave=app_presentation, actie=action, - actie_weergave=CommonResourceAction.labels.get(action, ""), + actie_weergave=action_labels.get(action, ""), gebruikers_id=user_id, gebruikers_weergave=user_representation, resultaat=status_code, diff --git a/vng_api_common/checks.py b/vng_api_common/checks.py index 86f927fa..2b45593f 100644 --- a/vng_api_common/checks.py +++ b/vng_api_common/checks.py @@ -2,8 +2,7 @@ from typing import Any from django.core.checks import Warning, register - -from djchoices import DjangoChoices +from django.db.models import Choices from .utils import get_subclasses @@ -28,8 +27,8 @@ def check_lowercased_constants(app_configs, **kwargs): """ warnings = [] - for klass in get_subclasses(DjangoChoices): - enum_values = klass.values.keys() + for klass in get_subclasses(Choices): + enum_values = klass.values if any((not enum_value_ok(value) for value in enum_values)): warnings.append( Warning( diff --git a/vng_api_common/constants.py b/vng_api_common/constants.py index c1ae3c04..5ee7824d 100644 --- a/vng_api_common/constants.py +++ b/vng_api_common/constants.py @@ -1,9 +1,8 @@ -import warnings +from typing import Optional +from django.db import models from django.utils.translation import gettext_lazy as _ -from djchoices import ChoiceItem, DjangoChoices - BSN_LENGTH = 9 RSIN_LENGTH = 9 @@ -14,309 +13,173 @@ FILTER_URL_DID_NOT_RESOLVE = "NO_MATCHING_OBJECT" - -class VertrouwelijkheidsAanduiding(DjangoChoices): - openbaar = ChoiceItem("openbaar", "Openbaar") - beperkt_openbaar = ChoiceItem("beperkt_openbaar", "Beperkt openbaar") - intern = ChoiceItem("intern", "Intern") - zaakvertrouwelijk = ChoiceItem("zaakvertrouwelijk", "Zaakvertrouwelijk") - vertrouwelijk = ChoiceItem("vertrouwelijk", "Vertrouwelijk") - confidentieel = ChoiceItem("confidentieel", "Confidentieel") - geheim = ChoiceItem("geheim", "Geheim") - zeer_geheim = ChoiceItem("zeer_geheim", "Zeer geheim") - - -class RolOmschrijving(DjangoChoices): - adviseur = ChoiceItem( - "adviseur", - "Adviseur", - description="Kennis in dienst stellen van de behandeling van (een deel van) een zaak.", - ) - behandelaar = ChoiceItem( - "behandelaar", - "Behandelaar", - description="De vakinhoudelijke behandeling doen van (een deel van) een zaak.", - ) - belanghebbende = ChoiceItem( - "belanghebbende", - "Belanghebbende", - description="Vanuit eigen en objectief belang rechtstreeks betrokken " - "zijn bij de behandeling en/of de uitkomst van een zaak.", - ) - beslisser = ChoiceItem( - "beslisser", - "Beslisser", - description="Nemen van besluiten die voor de uitkomst van een zaak noodzakelijk zijn.", - ) - initiator = ChoiceItem( - "initiator", - "Initiator", - description="Aanleiding geven tot de start van een zaak ..", - ) - klantcontacter = ChoiceItem( - "klantcontacter", - "Klantcontacter", - description="Het eerste aanspreekpunt zijn voor vragen van burgers en bedrijven ..", - ) - zaakcoordinator = ChoiceItem( - "zaakcoordinator", - "Zaakcoƶrdinator", - description="Er voor zorg dragen dat de behandeling van de zaak in samenhang " - "uitgevoerd wordt conform de daarover gemaakte afspraken.", - ) - medeinitiator = ChoiceItem("mede_initiator", "Mede-initiator", description="") - - -class RolTypes(DjangoChoices): - natuurlijk_persoon = ChoiceItem("natuurlijk_persoon", "Natuurlijk persoon") - niet_natuurlijk_persoon = ChoiceItem( - "niet_natuurlijk_persoon", "Niet-natuurlijk persoon" - ) - vestiging = ChoiceItem("vestiging", "Vestiging") - organisatorische_eenheid = ChoiceItem( - "organisatorische_eenheid", "Organisatorische eenheid" - ) - medewerker = ChoiceItem("medewerker", "Medewerker") - - +# constants for ObjectInformatieObjectTypes BESLUIT_CONST = "besluit" -BESLUIT_CHOICE = ChoiceItem(BESLUIT_CONST, _("Besluit")) +BESLUIT_CHOICE = BESLUIT_CONST, _("Besluit") ZAAK_CONST = "zaak" -ZAAK_CHOICE = ChoiceItem(ZAAK_CONST, _("Zaak")) +ZAAK_CHOICE = ZAAK_CONST, _("Zaak") VERZOEK_CONST = "verzoek" -VERZOEK_CHOICE = ChoiceItem(VERZOEK_CONST, _("Verzoek")) - - -class ObjectTypes(DjangoChoices): - besluit = BESLUIT_CHOICE - zaak = ZAAK_CHOICE - - def __init__(self, *args, **kwargs): - warnings.warn( - "The use of ObjectTypes is deprecated. Create your own " - "enumeration based on the relevant objects you need to support.", - DeprecationWarning, - ) - super().__init__(*args, **kwargs) - - -class Archiefnominatie(DjangoChoices): - blijvend_bewaren = ChoiceItem( - "blijvend_bewaren", - _( - "Het zaakdossier moet bewaard blijven en op de Archiefactiedatum overgedragen worden naar een " - "archiefbewaarplaats." - ), - ) - vernietigen = ChoiceItem( - "vernietigen", - _("Het zaakdossier moet op of na de Archiefactiedatum vernietigd worden."), - ) +VERZOEK_CHOICE = VERZOEK_CONST, _("Verzoek") -class Archiefstatus(DjangoChoices): - nog_te_archiveren = ChoiceItem( - "nog_te_archiveren", - _("De zaak cq. het zaakdossier is nog niet als geheel gearchiveerd."), - ) - gearchiveerd = ChoiceItem( - "gearchiveerd", - _( - "De zaak cq. het zaakdossier is als geheel niet-wijzigbaar bewaarbaar gemaakt." - ), - ) - gearchiveerd_procestermijn_onbekend = ChoiceItem( - "gearchiveerd_procestermijn_onbekend", - _( - "De zaak cq. het zaakdossier is als geheel niet-wijzigbaar bewaarbaar gemaakt maar de vernietigingsdatum " - "kan nog niet bepaald worden." - ), - ) - # After deliberation this element was removed because "vernietigd" means - # it's really gone and the status wouldn't make sense: - # - # vernietigd = ChoiceItem('vernietigd', - # _("De zaak cq. het zaakdossier is vernietigd.") - # ) - overgedragen = ChoiceItem( - "overgedragen", - _("De zaak cq. het zaakdossier is overgebracht naar een archiefbewaarplaats."), - ) - - -class BrondatumArchiefprocedureAfleidingswijze(DjangoChoices): - afgehandeld = ChoiceItem( - "afgehandeld", - _("Afgehandeld"), - description=_( - "De termijn start op de datum waarop de zaak is " - "afgehandeld (ZAAK.Einddatum in het RGBZ)." - ), - ) - ander_datumkenmerk = ChoiceItem( - "ander_datumkenmerk", - _("Ander datumkenmerk"), - description=_( - "De termijn start op de datum die is vastgelegd in een " - "ander datumveld dan de datumvelden waarop de overige " - "waarden (van deze attribuutsoort) betrekking hebben. " - "`Objecttype`, `Registratie` en `Datumkenmerk` zijn niet " - "leeg." - ), - ) - eigenschap = ChoiceItem( - "eigenschap", - _("Eigenschap"), - description=_( - "De termijn start op de datum die vermeld is in een " - "zaaktype-specifieke eigenschap (zijnde een `datumveld`). " - "`ResultaatType.ZaakType` heeft een `Eigenschap`; " - "`Objecttype`, en `Datumkenmerk` zijn niet leeg." - ), - ) - gerelateerde_zaak = ChoiceItem( - "gerelateerde_zaak", - _("Gerelateerde zaak"), - description=_( - "De termijn start op de datum waarop de gerelateerde " - "zaak is afgehandeld (`ZAAK.Einddatum` of " - "`ZAAK.Gerelateerde_zaak.Einddatum` in het RGBZ). " - "`ResultaatType.ZaakType` heeft gerelateerd `ZaakType`" - ), - ) - hoofdzaak = ChoiceItem( - "hoofdzaak", - _("Hoofdzaak"), - description=_( - "De termijn start op de datum waarop de gerelateerde " - "zaak is afgehandeld, waarvan de zaak een deelzaak is " - "(`ZAAK.Einddatum` van de hoofdzaak in het RGBZ). " - "ResultaatType.ZaakType is deelzaaktype van ZaakType." - ), - ) - ingangsdatum_besluit = ChoiceItem( - "ingangsdatum_besluit", - _("Ingangsdatum besluit"), - description=_( - "De termijn start op de datum waarop het besluit van " - "kracht wordt (`BESLUIT.Ingangsdatum` in het RGBZ). " - "ResultaatType.ZaakType heeft relevant BesluitType" - ), - ) - termijn = ChoiceItem( - "termijn", - _("Termijn"), - description=_( - "De termijn start een vast aantal jaren na de datum " - "waarop de zaak is afgehandeld (`ZAAK.Einddatum` in het " - "RGBZ)." - ), - ) - vervaldatum_besluit = ChoiceItem( - "vervaldatum_besluit", - _("Vervaldatum besluit"), - description=_( - "De termijn start op de dag na de datum waarop het " - "besluit vervalt (`BESLUIT.Vervaldatum` in het RGBZ). " - "ResultaatType.ZaakType heeft relevant BesluitType" - ), - ) - zaakobject = ChoiceItem( - "zaakobject", - _("Zaakobject"), - description=_( - "De termijn start op de einddatum geldigheid van het " - "zaakobject waarop de zaak betrekking heeft " - "(bijvoorbeeld de overlijdendatum van een Persoon). " - "M.b.v. de attribuutsoort `Objecttype` wordt vastgelegd " - "om welke zaakobjecttype het gaat; m.b.v. de " - "attribuutsoort `Datumkenmerk` wordt vastgelegd welke " - "datum-attribuutsoort van het zaakobjecttype het betreft." - ), - ) +class VertrouwelijkheidsAanduiding(models.TextChoices): + openbaar = "openbaar", _("Openbaar") + beperkt_openbaar = "beperkt_openbaar", _("Beperkt openbaar") + intern = "intern", _("Intern") + zaakvertrouwelijk = "zaakvertrouwelijk", _("Zaakvertrouwelijk") + vertrouwelijk = "vertrouwelijk", _("Vertrouwelijk") + confidentieel = "confidentieel", _("Confidentieel") + geheim = "geheim", _("Geheim") + zeer_geheim = "zeer_geheim", _("Zeer geheim") + @classmethod + def get_order_expression(cls, field_name): + whens = [] + for order, value in enumerate(cls.values): + whens.append( + models.When(**{field_name: value, "then": models.Value(order)}) + ) + return models.Case(*whens, output_field=models.IntegerField()) -class ZaakobjectTypes(DjangoChoices): - adres = ChoiceItem("adres", "Adres") - besluit = ChoiceItem("besluit", "Besluit") - buurt = ChoiceItem("buurt", "Buurt") - enkelvoudig_document = ChoiceItem("enkelvoudig_document", "Enkelvoudig document") - gemeente = ChoiceItem("gemeente", "Gemeente") - gemeentelijke_openbare_ruimte = ChoiceItem( - "gemeentelijke_openbare_ruimte", "Gemeentelijke openbare ruimte" - ) - huishouden = ChoiceItem("huishouden", "Huishouden") - inrichtingselement = ChoiceItem("inrichtingselement", "Inrichtingselement") - kadastrale_onroerende_zaak = ChoiceItem( - "kadastrale_onroerende_zaak", "Kadastrale onroerende zaak" - ) - kunstwerkdeel = ChoiceItem("kunstwerkdeel", "Kunstwerkdeel") - maatschappelijke_activiteit = ChoiceItem( - "maatschappelijke_activiteit", "Maatschappelijke activiteit" - ) - medewerker = ChoiceItem("medewerker", "Medewerker") - natuurlijk_persoon = ChoiceItem("natuurlijk_persoon", "Natuurlijk persoon") - niet_natuurlijk_persoon = ChoiceItem( - "niet_natuurlijk_persoon", "Niet-natuurlijk persoon" - ) - openbare_ruimte = ChoiceItem("openbare_ruimte", "Openbare ruimte") - organisatorische_eenheid = ChoiceItem( - "organisatorische_eenheid", "Organisatorische eenheid" - ) - pand = ChoiceItem("pand", "Pand") - spoorbaandeel = ChoiceItem("spoorbaandeel", "Spoorbaandeel") - status = ChoiceItem("status", "Status") - terreindeel = ChoiceItem("terreindeel", "Terreindeel") - terrein_gebouwd_object = ChoiceItem( - "terrein_gebouwd_object", "Terrein gebouwd object" - ) - vestiging = ChoiceItem("vestiging", "Vestiging") - waterdeel = ChoiceItem("waterdeel", "Waterdeel") - wegdeel = ChoiceItem("wegdeel", "Wegdeel") - wijk = ChoiceItem("wijk", "Wijk") - woonplaats = ChoiceItem("woonplaats", "Woonplaats") - woz_deelobject = ChoiceItem("woz_deelobject", "Woz deel object") - woz_object = ChoiceItem("woz_object", "Woz object") - woz_waarde = ChoiceItem("woz_waarde", "Woz waarde") - zakelijk_recht = ChoiceItem("zakelijk_recht", "Zakelijk recht") - overige = ChoiceItem("overige", "Overige") - - -class ComponentTypes(DjangoChoices): - ac = ChoiceItem("ac", "Autorisaties API") - nrc = ChoiceItem("nrc", "Notificaties API") - zrc = ChoiceItem("zrc", "Zaken API") - ztc = ChoiceItem("ztc", "Catalogi API") - drc = ChoiceItem("drc", "Documenten API") - brc = ChoiceItem("brc", "Besluiten API") - cmc = ChoiceItem("cmc", "Contactmomenten API") - kc = ChoiceItem("kc", "Klanten API") - vrc = ChoiceItem("vrc", "Verzoeken API") - - -class CommonResourceAction(DjangoChoices): - create = ChoiceItem("create", _("Object aangemaakt")) - list = ChoiceItem("list", _("Lijst van objecten opgehaald")) - retrieve = ChoiceItem("retrieve", _("Object opgehaald")) - destroy = ChoiceItem("destroy", _("Object verwijderd")) - update = ChoiceItem("update", _("Object bijgewerkt")) - partial_update = ChoiceItem("partial_update", _("Object deels bijgewerkt")) - - -class RelatieAarden(DjangoChoices): - hoort_bij = ChoiceItem("hoort_bij", _("Hoort bij, omgekeerd: kent")) - legt_vast = ChoiceItem( - "legt_vast", _("Legt vast, omgekeerd: kan vastgelegd zijn als") - ) + @classmethod + def get_choice_order(cls, value) -> Optional[int]: + orders = { + value: order + for order, value in enumerate(VertrouwelijkheidsAanduiding.values) + } + return orders.get(value) + + +class RolOmschrijving(models.TextChoices): + adviseur = "adviseur", _("Adviseur") + behandelaar = "behandelaar", _("Behandelaar") + belanghebbende = "belanghebbende", _("Belanghebbende") + beslisser = "beslisser", _("Beslisser") + initiator = "initiator", _("Initiator") + klantcontacter = "klantcontacter", _("Klantcontacter") + zaakcoordinator = "zaakcoordinator", _("Zaakcoƶrdinator") + medeinitiator = "mede_initiator", _("Mede-initiator") + + +class RolTypes(models.TextChoices): + natuurlijk_persoon = "natuurlijk_persoon", _("Natuurlijk persoon") + niet_natuurlijk_persoon = "niet_natuurlijk_persoon", _("Niet-natuurlijk persoon") + vestiging = "vestiging", _("Vestiging") + organisatorische_eenheid = "organisatorische_eenheid", _("Organisatorische eenheid") + medewerker = "medewerker", _("Medewerker") + + +class Archiefnominatie(models.TextChoices): + blijvend_bewaren = "blijvend_bewaren", _( + "Het zaakdossier moet bewaard blijven en op de Archiefactiedatum " + "overgedragen worden naar een archiefbewaarplaats." + ) + vernietigen = "vernietigen", _( + "Het zaakdossier moet op of na de Archiefactiedatum vernietigd worden." + ) + + +class Archiefstatus(models.TextChoices): + nog_te_archiveren = "nog_te_archiveren", _( + "De zaak cq. het zaakdossier is nog niet als geheel gearchiveerd." + ) + gearchiveerd = "gearchiveerd", _( + "De zaak cq. het zaakdossier is als geheel niet-wijzigbaar bewaarbaar gemaakt." + ) + gearchiveerd_procestermijn_onbekend = "gearchiveerd_procestermijn_onbekend", _( + "De zaak cq. het zaakdossier is als geheel niet-wijzigbaar bewaarbaar gemaakt " + "maar de vernietigingsdatum kan nog niet bepaald worden." + ) + overgedragen = "overgedragen", _( + "De zaak cq. het zaakdossier is overgebracht naar een archiefbewaarplaats." + ) + + +class BrondatumArchiefprocedureAfleidingswijze(models.TextChoices): + afgehandeld = "afgehandeld", _("Afgehandeld") + ander_datumkenmerk = "ander_datumkenmerk", _("Ander datumkenmerk") + eigenschap = "eigenschap", _("Eigenschap") + gerelateerde_zaak = "gerelateerde_zaak", _("Gerelateerde zaak") + hoofdzaak = "hoofdzaak", _("Hoofdzaak") + ingangsdatum_besluit = "ingangsdatum_besluit", _("Ingangsdatum besluit") + termijn = "termijn", _("Termijn") + vervaldatum_besluit = "vervaldatum_besluit", _("Vervaldatum besluit") + zaakobject = "zaakobject", _("Zaakobject") + + +class ZaakobjectTypes(models.TextChoices): + adres = "adres", _("Adres") + besluit = "besluit", _("Besluit") + buurt = "buurt", _("Buurt") + enkelvoudig_document = "enkelvoudig_document", _("Enkelvoudig document") + gemeente = "gemeente", _("Gemeente") + gemeentelijke_openbare_ruimte = "gemeentelijke_openbare_ruimte", _( + "Gemeentelijke openbare ruimte" + ) + huishouden = "huishouden", _("Huishouden") + inrichtingselement = "inrichtingselement", _("Inrichtingselement") + kadastrale_onroerende_zaak = "kadastrale_onroerende_zaak", _( + "Kadastrale onroerende zaak" + ) + kunstwerkdeel = "kunstwerkdeel", _("Kunstwerkdeel") + maatschappelijke_activiteit = "maatschappelijke_activiteit", _( + "Maatschappelijke activiteit" + ) + medewerker = "medewerker", _("Medewerker") + natuurlijk_persoon = "natuurlijk_persoon", _("Natuurlijk persoon") + niet_natuurlijk_persoon = "niet_natuurlijk_persoon", _("Niet-natuurlijk persoon") + openbare_ruimte = "openbare_ruimte", _("Openbare ruimte") + organisatorische_eenheid = "organisatorische_eenheid", _("Organisatorische eenheid") + pand = "pand", _("Pand") + spoorbaandeel = "spoorbaandeel", _("Spoorbaandeel") + status = "status", _("Status") + terreindeel = "terreindeel", _("Terreindeel") + terrein_gebouwd_object = "terrein_gebouwd_object", _("Terrein gebouwd object") + vestiging = "vestiging", _("Vestiging") + waterdeel = "waterdeel", _("Waterdeel") + wegdeel = "wegdeel", _("Wegdeel") + wijk = "wijk", _("Wijk") + woonplaats = "woonplaats", _("Woonplaats") + woz_deelobject = "woz_deelobject", _("Woz deel object") + woz_object = "woz_object", _("Woz object") + woz_waarde = "woz_waarde", _("Woz waarde") + zakelijk_recht = "zakelijk_recht", _("Zakelijk recht") + overige = "overige", _("Overige") + + +class ComponentTypes(models.TextChoices): + ac = "ac", _("Autorisaties API") + nrc = "nrc", _("Notificaties API") + zrc = "zrc", _("Zaken API") + ztc = "ztc", _("Catalogi API") + drc = "drc", _("Documenten API") + brc = "brc", _("Besluiten API") + cmc = "cmc", _("Contactmomenten API") + kc = "kc", _("Klanten API") + vrc = "vrc", _("Verzoeken API") + + +class CommonResourceAction(models.TextChoices): + create = "create", _("Object aangemaakt") + list = "list", _("Lijst van objecten opgehaald") + retrieve = "retrieve", _("Object opgehaald") + destroy = "destroy", _("Object verwijderd") + update = "update", _("Object bijgewerkt") + partial_update = "partial_update", _("Object deels bijgewerkt") + + +class RelatieAarden(models.TextChoices): + hoort_bij = "hoort_bij", _("Hoort bij, omgekeerd: kent") + legt_vast = "legt_vast", _("Legt vast, omgekeerd: kan vastgelegd zijn als") @classmethod def from_object_type(cls, object_type: str) -> str: - if object_type == ObjectTypes.zaak: + if object_type == "zaak": return cls.hoort_bij - if object_type == ObjectTypes.besluit: + if object_type == "besluit": return cls.legt_vast raise ValueError(f"Unknown object_type '{object_type}'") diff --git a/vng_api_common/inspectors/query.py b/vng_api_common/inspectors/query.py index 579fa6a4..9fdea531 100644 --- a/vng_api_common/inspectors/query.py +++ b/vng_api_common/inspectors/query.py @@ -30,7 +30,7 @@ def get_filter_parameters(self, filter_backend): if fields: queryset = self.view.get_queryset() - filter_class = filter_backend.get_filter_class(self.view, queryset) + filter_class = filter_backend.get_filterset_class(self.view, queryset) for parameter in fields: filter_field = filter_class.base_filters[parameter.name] diff --git a/vng_api_common/middleware.py b/vng_api_common/middleware.py index 58badbac..f31db27f 100644 --- a/vng_api_common/middleware.py +++ b/vng_api_common/middleware.py @@ -161,7 +161,7 @@ def filter_vertrouwelijkheidaanduiding(self, base: QuerySet, value) -> QuerySet: if value is None: return base - order_provided = VertrouwelijkheidsAanduiding.get_choice(value).order + order_provided = VertrouwelijkheidsAanduiding.get_choice_order(value) order_case = VertrouwelijkheidsAanduiding.get_order_expression( "max_vertrouwelijkheidaanduiding" ) diff --git a/vng_api_common/serializers.py b/vng_api_common/serializers.py index 2ab6920b..410dfe3d 100644 --- a/vng_api_common/serializers.py +++ b/vng_api_common/serializers.py @@ -3,11 +3,10 @@ from collections import OrderedDict from typing import Optional, Tuple, Union -from django.db import transaction +from django.db import models, transaction from django.utils.translation import gettext_lazy as _ import isodate -from djchoices import DjangoChoices from rest_framework import fields, serializers from .descriptors import GegevensGroepType @@ -90,23 +89,11 @@ class ValidatieFoutSerializer(FoutSerializer): invalid_params = FieldValidationErrorSerializer(many=True) -def add_choice_values_help_text(choices: Union[DjangoChoices, Tuple[str, str]]) -> str: - items = [] - - is_dj_choices = inspect.isclass(choices) and issubclass(choices, DjangoChoices) +def add_choice_values_help_text(choices: Union[models.Choices, Tuple[str, str]]) -> str: + is_dj_choices = inspect.isclass(choices) and issubclass(choices, models.Choices) _choices = choices.choices if is_dj_choices else choices - for key, value in _choices: - description = ( - getattr(choices.get_choice(key), "description", None) - if is_dj_choices - else None - ) - if description: - item = f"* `{key}` - ({value}) {description}" - else: - item = f"* `{key}` - {value}" - items.append(item) + items = [f"* `{key}` - {value}" for key, value in _choices] return "Uitleg bij mogelijke waarden:\n\n" + "\n".join(items)