27
27
SmallIntegerField ,
28
28
)
29
29
from django .db .models .query_utils import DeferredAttribute
30
+
31
+ try :
32
+ from django .db .models .expressions import DatabaseDefault
33
+ except ImportError : # pragma: no cover
34
+ class DatabaseDefault : # type: ignore
35
+ """Spoof DatabaseDefault for Django < 5.0"""
36
+
30
37
from django_enum .choices import choices , values
31
38
from django_enum .forms import EnumChoiceField , NonStrictSelect
32
39
@@ -56,7 +63,11 @@ class ToPythonDeferredAttribute(DeferredAttribute):
56
63
57
64
def __set__ (self , instance : Model , value : Any ):
58
65
try :
59
- instance .__dict__ [self .field .name ] = self .field .to_python (value )
66
+ instance .__dict__ [self .field .name ] = (
67
+ value
68
+ if isinstance (value , DatabaseDefault ) else
69
+ self .field .to_python (value )
70
+ )
60
71
except (ValidationError , ValueError ):
61
72
# Django core fields allow assignment of any value, we do the same
62
73
instance .__dict__ [self .field .name ] = value
@@ -83,19 +94,10 @@ class EnumMixin(
83
94
enum : Optional [Type [Enum ]] = None
84
95
strict : bool = True
85
96
coerce : bool = True
97
+ primitive : Type [Any ]
86
98
87
99
descriptor_class = ToPythonDeferredAttribute
88
100
89
- def _coerce_to_value_type (self , value : Any ) -> Enum :
90
- """Coerce the value to the enumerations value type"""
91
- # note if enum type is int and a floating point is passed we could get
92
- # situations like X.xxx == X - this is acceptable
93
- if self .enum :
94
- return type (values (self .enum )[0 ])(value )
95
- # can't ever reach this - just here to make type checker happy
96
- return value # pragma: no cover
97
-
98
-
99
101
def __init__ (
100
102
self ,
101
103
* args ,
@@ -121,30 +123,42 @@ def _try_coerce(
121
123
and non-strict, coercion to enum's primitive type will be done,
122
124
otherwise a ValueError is raised.
123
125
"""
124
- if (
125
- (self .coerce or force )
126
- and self .enum is not None
127
- and not isinstance (value , self .enum )
128
- ):
126
+ if self .enum is None :
127
+ return value
128
+
129
+ if (self .coerce or force ) and not isinstance (value , self .enum ):
129
130
try :
130
131
value = self .enum (value )
131
132
except (TypeError , ValueError ):
132
133
try :
133
- value = self ._coerce_to_value_type (value )
134
+ value = self .primitive (value )
134
135
value = self .enum (value )
135
136
except (TypeError , ValueError ):
136
137
try :
137
138
value = self .enum [value ]
138
139
except KeyError as err :
139
140
if self .strict or not isinstance (
140
141
value ,
141
- type ( values ( self .enum )[ 0 ])
142
+ self .primitive
142
143
):
143
144
raise ValueError (
144
145
f"'{ value } ' is not a valid "
145
146
f"{ self .enum .__name__ } "
146
147
f"required by field { self .name } ."
147
148
) from err
149
+ elif (
150
+ not self .coerce and
151
+ not isinstance (value , self .primitive ) and
152
+ not isinstance (value , self .enum )
153
+ ):
154
+ try :
155
+ return self .primitive (value )
156
+ except (TypeError , ValueError ) as err :
157
+ raise ValueError (
158
+ f"'{ value } ' is not coercible to { self .primitive .__name__ } "
159
+ f"required by field { self .name } ."
160
+ ) from err
161
+
148
162
return value
149
163
150
164
def deconstruct (self ) -> Tuple [str , str , List , dict ]:
@@ -306,6 +320,8 @@ class EnumCharField(EnumMixin, CharField):
306
320
A database field supporting enumerations with character values.
307
321
"""
308
322
323
+ primitive = str
324
+
309
325
def __init__ (self , * args , enum = None , ** kwargs ):
310
326
kwargs .setdefault (
311
327
'max_length' ,
@@ -320,48 +336,61 @@ def __init__(self, *args, enum=None, **kwargs):
320
336
class EnumFloatField (EnumMixin , FloatField ):
321
337
"""A database field supporting enumerations with floating point values"""
322
338
339
+ primitive = float
340
+
323
341
324
342
class EnumSmallIntegerField (EnumMixin , SmallIntegerField ):
325
343
"""
326
344
A database field supporting enumerations with integer values that fit into
327
345
2 bytes or fewer
328
346
"""
329
347
348
+ primitive = int
349
+
330
350
331
351
class EnumPositiveSmallIntegerField (EnumMixin , PositiveSmallIntegerField ):
332
352
"""
333
353
A database field supporting enumerations with positive (but signed) integer
334
354
values that fit into 2 bytes or fewer
335
355
"""
336
356
357
+ primitive = int
337
358
338
359
class EnumIntegerField (EnumMixin , IntegerField ):
339
360
"""
340
361
A database field supporting enumerations with integer values that fit into
341
362
32 bytes or fewer
342
363
"""
343
364
365
+ primitive = int
366
+
344
367
345
368
class EnumPositiveIntegerField (EnumMixin , PositiveIntegerField ):
346
369
"""
347
370
A database field supporting enumerations with positive (but signed) integer
348
371
values that fit into 32 bytes or fewer
349
372
"""
350
373
374
+ primitive = int
375
+
351
376
352
377
class EnumBigIntegerField (EnumMixin , BigIntegerField ):
353
378
"""
354
379
A database field supporting enumerations with integer values that fit into
355
380
64 bytes or fewer
356
381
"""
357
382
383
+ primitive = int
384
+
358
385
359
386
class EnumPositiveBigIntegerField (EnumMixin , PositiveBigIntegerField ):
360
387
"""
361
388
A database field supporting enumerations with positive (but signed) integer
362
389
values that fit into 64 bytes or fewer
363
390
"""
364
391
392
+ primitive = int
393
+
365
394
366
395
class _EnumFieldMetaClass (type ):
367
396
0 commit comments