Skip to content

Commit fb17dfd

Browse files
committed
merge changes from 1.3 upstream adding support for 5.0
2 parents ee79aca + 7c5d162 commit fb17dfd

File tree

16 files changed

+1709
-196
lines changed

16 files changed

+1709
-196
lines changed

.github/workflows/test.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ jobs:
1212
python-version: [ '3.7', '3.12' ]
1313
django-version:
1414
- 'Django~=3.2.0' # LTS April 2024
15-
- 'Django~=4.1.0' # December 2023
1615
- 'Django~=4.2.0' # LTS April 2026
16+
- 'Django~=5.0.0' # April 2025
1717
exclude:
1818
- python-version: '3.7'
19-
django-version: 'Django~=4.1.0'
19+
django-version: 'Django~=5.0.0'
2020
- python-version: '3.7'
2121
django-version: 'Django~=4.2.0'
2222
- python-version: '3.12'
@@ -112,6 +112,7 @@ jobs:
112112
steps:
113113
- uses: actions/checkout@v3
114114
- name: Set up Python ${{ matrix.python-version }}
115+
uses: actions/setup-python@v4
115116
uses: actions/setup-python@v4
116117
with:
117118
python-version: ${{ matrix.python-version }}
@@ -163,8 +164,10 @@ jobs:
163164
run: |
164165
poetry run pytest
165166
- name: Upload coverage to Codecov
167+
uses: codecov/codecov-action@v3
166168
uses: codecov/codecov-action@v3
167169
with:
170+
token: ${{ secrets.CODECOV_TOKEN }}
168171
file: ./coverage.xml
169172

170173
sqlite:

django_enum/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,4 @@
4848
__version__ = '.'.join(str(i) for i in VERSION)
4949
__author__ = 'Brian Kohan'
5050
__license__ = 'MIT'
51-
__copyright__ = 'Copyright 2022-2023 Brian Kohan'
51+
__copyright__ = 'Copyright 2022-2024 Brian Kohan'

django_enum/choices.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88
from django.db.models import Choices
99
from django.db.models import IntegerChoices as DjangoIntegerChoices
1010
from django.db.models import TextChoices as DjangoTextChoices
11-
from django.db.models.enums import ChoicesMeta
1211
from django_enum.utils import choices, names
1312

13+
try:
14+
from django.db.models.enums import ChoicesType
15+
except ImportError: # pragma: no cover
16+
from django.db.models.enums import ChoicesMeta as ChoicesType
17+
18+
1419
DEFAULT_BOUNDARY = getattr(enum, 'KEEP', None)
1520

1621

@@ -22,7 +27,7 @@
2227
)
2328

2429

25-
class DjangoEnumPropertiesMeta(EnumPropertiesMeta, ChoicesMeta):
30+
class DjangoEnumPropertiesMeta(EnumPropertiesMeta, ChoicesType):
2631
"""
2732
A composite meta class that combines Django's Choices metaclass with
2833
enum-properties metaclass. This metaclass will add Django's expected
@@ -36,15 +41,15 @@ def names(cls):
3641
For some eccentric enums list(Enum) is empty, so we override names
3742
if empty
3843
"""
39-
return ChoicesMeta.names.fget(cls) or names(cls, override=True)
44+
return ChoicesType.names.fget(cls) or names(cls, override=True)
4045

4146
@property
4247
def choices(cls):
4348
"""
4449
For some eccentric enums list(Enum) is empty, so we override
4550
choices if empty
4651
"""
47-
return ChoicesMeta.choices.fget(cls) or choices(cls, override=True)
52+
return ChoicesType.choices.fget(cls) or choices(cls, override=True)
4853

4954

5055
class DjangoSymmetricMixin(SymmetricMixin):
@@ -55,7 +60,7 @@ class DjangoSymmetricMixin(SymmetricMixin):
5560
_symmetric_builtins_ = ['name', 'label']
5661

5762

58-
class TextChoices(
63+
class TextChoices( # pylint: disable=too-many-ancestors
5964
DjangoSymmetricMixin,
6065
DjangoTextChoices,
6166
metaclass=DjangoEnumPropertiesMeta
@@ -69,7 +74,7 @@ def __hash__(self):
6974
return DjangoTextChoices.__hash__(self)
7075

7176

72-
class IntegerChoices(
77+
class IntegerChoices( # pylint: disable=too-many-ancestors
7378
DjangoSymmetricMixin,
7479
DjangoIntegerChoices,
7580
metaclass=DjangoEnumPropertiesMeta
@@ -97,6 +102,9 @@ class FloatChoices(
97102
def __hash__(self):
98103
return float.__hash__(self)
99104

105+
def __str__(self):
106+
return str(self.value)
107+
100108

101109
# mult inheritance type hint bug
102110
class FlagChoices( # type: ignore
@@ -138,7 +146,7 @@ def __init__(self, *args, **kwargs): # pylint: disable=W0231
138146
DjangoSymmetricMixin = MissingEnumProperties # type: ignore
139147

140148

141-
class DjangoEnumPropertiesMeta(ChoicesMeta): # type: ignore
149+
class DjangoEnumPropertiesMeta(ChoicesType): # type: ignore
142150
"""
143151
Throw error if metaclass is used without enum-properties
144152

django_enum/fields.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
)
3535
from django.db.models.constraints import CheckConstraint
3636
from django.db.models.query_utils import DeferredAttribute
37+
3738
from django.utils.deconstruct import deconstructible
3839
from django.utils.duration import duration_string
3940
from django.utils.functional import cached_property
@@ -58,6 +59,13 @@
5859
with_typehint,
5960
)
6061

62+
try:
63+
from django.db.models.expressions import DatabaseDefault
64+
except ImportError: # pragma: no cover
65+
class DatabaseDefault: # type: ignore
66+
"""Spoof DatabaseDefault for Django < 5.0"""
67+
68+
6169
CONFORM: Union[Enum, Type[NOT_PROVIDED]]
6270
EJECT: Union[Enum, Type[NOT_PROVIDED]]
6371
STRICT: Union[Enum, Type[NOT_PROVIDED]]
@@ -112,7 +120,11 @@ class ToPythonDeferredAttribute(DeferredAttribute):
112120

113121
def __set__(self, instance: Model, value: Any):
114122
try:
115-
instance.__dict__[self.field.name] = self.field.to_python(value)
123+
instance.__dict__[self.field.name] = (
124+
value
125+
if isinstance(value, DatabaseDefault) else
126+
self.field.to_python(value)
127+
)
116128
except (ValidationError, ValueError):
117129
# Django core fields allow assignment of any value, we do the same
118130
instance.__dict__[self.field.name] = value
@@ -488,15 +500,15 @@ def _try_coerce(
488500
and non-strict, coercion to enum's primitive type will be done,
489501
otherwise a ValueError is raised.
490502
"""
491-
if (
492-
(self.coerce or force)
493-
and self.enum is not None
494-
and not isinstance(value, self.enum)
495-
):
503+
if self.enum is None:
504+
return value
505+
506+
if (self.coerce or force) and not isinstance(value, self.enum):
496507
try:
497508
value = self.enum(value) # pylint: disable=E1102
498509
except (TypeError, ValueError):
499510
try:
511+
# value = self.primitive(value)
500512
value = self._coerce_to_value_type(value)
501513
value = self.enum(value) # pylint: disable=E1102
502514
except (TypeError, ValueError, DecimalException):
@@ -520,12 +532,13 @@ def _try_coerce(
520532
f"{self.enum.__name__} required by field "
521533
f"{self.name}."
522534
) from err
535+
523536
elif not self.coerce:
524537
try:
525538
return self._coerce_to_value_type(value)
526539
except (TypeError, ValueError, DecimalException) as err:
527540
raise ValueError(
528-
f"'{value}' is not a valid {self.primitive} "
541+
f"'{value}' is not a valid {self.primitive.__name__} "
529542
f"required by field {self.name}."
530543
) from err
531544
return value

django_enum/tests/db_default/__init__.py

Whitespace-only changes.

django_enum/tests/db_default/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class DBDefaultConfig(AppConfig):
5+
name = 'django_enum.tests.db_default'
6+
label = name.replace('.', '_')

0 commit comments

Comments
 (0)