From 2bab7749d6fc23e9a3f973d38df14d6d8b5bf098 Mon Sep 17 00:00:00 2001 From: Thiago Bellini Ribeiro Date: Wed, 21 Jun 2023 15:50:39 -0300 Subject: [PATCH] fix: fix joining strings when they are promises --- django_choices_field/fields.py | 34 +++++++++++++++++++++++++++------- pyproject.toml | 2 +- tests/models.py | 14 ++++++++++++++ tests/test_fields.py | 5 ++++- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/django_choices_field/fields.py b/django_choices_field/fields.py index 4ea9258..583cecf 100644 --- a/django_choices_field/fields.py +++ b/django_choices_field/fields.py @@ -1,6 +1,6 @@ import functools import itertools -from typing import ClassVar, Dict, Optional, Type +from typing import Callable, ClassVar, Dict, Optional, Sequence, Type, cast from django.core.exceptions import ValidationError from django.db import models @@ -8,6 +8,22 @@ from .types import IntegerChoicesFlag +def _get_flag_description(descs: Sequence[str]) -> str: + return "|".join(str(desc) for desc in descs) + + +try: + from django.utils.functional import Promise, lazy +except ImportError: # pragma: nocover + Promise = None + _get_flag_description_lazy = None +else: + _get_flag_description_lazy = cast( + Callable[[Sequence[str]], str], + lazy(_get_flag_description, str), + ) + + class TextChoicesField(models.CharField): description: ClassVar[str] = "TextChoices" default_error_messages: ClassVar[Dict[str, str]] = { @@ -122,12 +138,16 @@ def __init__( kwargs["choices"] = default_choices[:] for i in range(1, len(default_choices)): for combination in itertools.combinations(default_choices, i + 1): - kwargs["choices"].append( - ( - functools.reduce(lambda a, b: a | b[0], combination, 0), - "|".join(c[1] for c in combination), - ), - ) + value = functools.reduce(lambda a, b: a | b[0], combination, 0) + + descs = [c[1] for c in combination] + if Promise is not None and any(isinstance(desc, Promise) for desc in descs): + assert _get_flag_description_lazy is not None + desc = _get_flag_description_lazy(descs) + else: + desc = _get_flag_description(descs) + + kwargs["choices"].append((value, desc)) super().__init__(verbose_name=verbose_name, name=name, **kwargs) diff --git a/pyproject.toml b/pyproject.toml index 5148100..ede5ca3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "django-choices-field" -version = "2.2.0" +version = "2.2.1" description = "Django field that set/get django's new TextChoices/IntegerChoices enum." authors = ["Thiago Bellini Ribeiro "] license = "MIT" diff --git a/tests/models.py b/tests/models.py index d515654..e35502e 100644 --- a/tests/models.py +++ b/tests/models.py @@ -2,6 +2,7 @@ import sys from django.db import models +from django.utils.translation import gettext_lazy as _ from django_choices_field import IntegerChoicesField, TextChoicesField from django_choices_field.fields import IntegerChoicesFlagField @@ -22,6 +23,11 @@ class IntegerFlagEnum(IntegerChoicesFlag): IF_BAR = enum.auto() if sys.version_info >= (3, 11) else 2, "IF Bar Description" IF_BIN = enum.auto() if sys.version_info >= (3, 11) else 4, "IF Bin Description" + class IntegerFlagEnumTranslated(IntegerChoicesFlag): + IF_FOO = enum.auto() if sys.version_info >= (3, 11) else 1, _("IF Foo Description") + IF_BAR = enum.auto() if sys.version_info >= (3, 11) else 2, _("IF Bar Description") + IF_BIN = enum.auto() if sys.version_info >= (3, 11) else 4, _("IF Bin Description") + objects = models.Manager["MyModel"]() c_field = TextChoicesField( @@ -48,3 +54,11 @@ class IntegerFlagEnum(IntegerChoicesFlag): choices_enum=IntegerFlagEnum, null=True, ) + ift_field = IntegerChoicesFlagField( + choices_enum=IntegerFlagEnumTranslated, + default=IntegerFlagEnumTranslated.IF_FOO, + ) + ift_field_nullable = IntegerChoicesFlagField( + choices_enum=IntegerFlagEnumTranslated, + null=True, + ) diff --git a/tests/test_fields.py b/tests/test_fields.py index 649ee54..cc387f2 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -24,7 +24,10 @@ def test_field_choices_integer(fname: str): ] -@pytest.mark.parametrize("fname", ["if_field", "if_field_nullable"]) +@pytest.mark.parametrize( + "fname", + ["if_field", "if_field_nullable", "ift_field", "ift_field_nullable"], +) def test_field_choices_integer_flags(fname: str): f = MyModel._meta.get_field(fname) assert f.choices == [