@@ -108,7 +108,16 @@ def __init__(
108
108
kwargs .setdefault ('choices' , enum .choices if enum else [])
109
109
super ().__init__ (* args , ** kwargs )
110
110
111
- def _try_coerce (self , value : Any , force : bool = False ) -> Union [Choices , Any ]:
111
+ def _try_coerce (
112
+ self ,
113
+ value : Any ,
114
+ force : bool = False
115
+ ) -> Union [Choices , Any ]:
116
+ """
117
+ Attempt coercion of value to enumeration type instance, if unsuccessful
118
+ and non-strict, coercion to enum's primitive type will be done,
119
+ otherwise a ValueError is raised.
120
+ """
112
121
if (
113
122
(self .coerce or force )
114
123
and self .enum is not None
@@ -133,15 +142,33 @@ def _try_coerce(self, value: Any, force: bool = False) -> Union[Choices, Any]:
133
142
134
143
def deconstruct (self ) -> Tuple [str , str , List , dict ]:
135
144
"""
136
- Preserve enum class for migrations. Strict is omitted because
137
- reconstructed fields are *always* non-strict sense enum is null.
145
+ Preserve field migrations. Strict and coerce are omitted because
146
+ reconstructed fields are *always* non-strict and coerce is always
147
+ False.
148
+
149
+ .. warning::
150
+
151
+ Do not add enum to kwargs! It is important that migration files not
152
+ reference enum classes that might be removed from the code base in
153
+ the future as this would break older migration files! We simply use
154
+ the choices tuple, which is plain old data and entirely sufficient
155
+ to de/reconstruct our field.
138
156
139
157
See deconstruct_
140
158
"""
141
159
name , path , args , kwargs = super ().deconstruct ()
142
160
if self .enum is not None :
143
161
kwargs ['choices' ] = self .enum .choices
144
162
163
+ if 'default' in kwargs :
164
+ # ensure default in deconstructed fields is always the primitive
165
+ # value type
166
+ kwargs ['default' ] = getattr (
167
+ self .get_default (),
168
+ 'value' ,
169
+ self .get_default ()
170
+ )
171
+
145
172
return name , path , args , kwargs
146
173
147
174
def get_prep_value (self , value : Any ) -> Any :
@@ -210,6 +237,15 @@ def to_python(self, value: Any) -> Union[Choices, Any]:
210
237
f"{ self .enum .__name__ if self .enum else '' } ."
211
238
) from err
212
239
240
+ def get_default (self ) -> Any :
241
+ """Wrap get_default in an enum type coercion attempt"""
242
+ if self .has_default ():
243
+ try :
244
+ return self .to_python (super ().get_default ())
245
+ except ValidationError :
246
+ return super ().get_default ()
247
+ return super ().get_default ()
248
+
213
249
def validate (self , value : Any , model_instance : Model ):
214
250
"""
215
251
Validates the field as part of model clean routines. Runs super class
@@ -237,6 +273,9 @@ def validate(self, value: Any, model_instance: Model):
237
273
params = {'value' : value }
238
274
) from err
239
275
276
+ # def formfield(self, form_class=None, choices_form_class=None, **kwargs):
277
+ # pass
278
+
240
279
241
280
class EnumCharField (EnumMixin , CharField ):
242
281
"""
@@ -302,7 +341,10 @@ class _EnumFieldMetaClass(type):
302
341
303
342
SUPPORTED_PRIMITIVES = {int , str , float }
304
343
305
- def __new__ (mcs , enum : Choices ) -> Field : # pylint: disable=R0911
344
+ def __new__ ( # pylint: disable=R0911
345
+ mcs ,
346
+ enum : Type [Choices ]
347
+ ) -> Type [EnumMixin ]:
306
348
"""
307
349
Construct a new Django Field class given the Enumeration class. The
308
350
correct Django field class to inherit from is determined based on the
@@ -347,7 +389,7 @@ def EnumField( # pylint: disable=C0103
347
389
enum : Type [Choices ],
348
390
* field_args ,
349
391
** field_kwargs
350
- ) -> Field :
392
+ ) -> EnumMixin :
351
393
"""
352
394
*This is a function, not a type*. Some syntactic sugar that wraps the enum
353
395
field metaclass so that we can cleanly create enums like so:
0 commit comments