@@ -108,7 +108,16 @@ def __init__(
108108 kwargs .setdefault ('choices' , enum .choices if enum else [])
109109 super ().__init__ (* args , ** kwargs )
110110
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+ """
112121 if (
113122 (self .coerce or force )
114123 and self .enum is not None
@@ -133,15 +142,33 @@ def _try_coerce(self, value: Any, force: bool = False) -> Union[Choices, Any]:
133142
134143 def deconstruct (self ) -> Tuple [str , str , List , dict ]:
135144 """
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.
138156
139157 See deconstruct_
140158 """
141159 name , path , args , kwargs = super ().deconstruct ()
142160 if self .enum is not None :
143161 kwargs ['choices' ] = self .enum .choices
144162
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+
145172 return name , path , args , kwargs
146173
147174 def get_prep_value (self , value : Any ) -> Any :
@@ -210,6 +237,15 @@ def to_python(self, value: Any) -> Union[Choices, Any]:
210237 f"{ self .enum .__name__ if self .enum else '' } ."
211238 ) from err
212239
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+
213249 def validate (self , value : Any , model_instance : Model ):
214250 """
215251 Validates the field as part of model clean routines. Runs super class
@@ -237,6 +273,9 @@ def validate(self, value: Any, model_instance: Model):
237273 params = {'value' : value }
238274 ) from err
239275
276+ # def formfield(self, form_class=None, choices_form_class=None, **kwargs):
277+ # pass
278+
240279
241280class EnumCharField (EnumMixin , CharField ):
242281 """
@@ -302,7 +341,10 @@ class _EnumFieldMetaClass(type):
302341
303342 SUPPORTED_PRIMITIVES = {int , str , float }
304343
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 ]:
306348 """
307349 Construct a new Django Field class given the Enumeration class. The
308350 correct Django field class to inherit from is determined based on the
@@ -347,7 +389,7 @@ def EnumField( # pylint: disable=C0103
347389 enum : Type [Choices ],
348390 * field_args ,
349391 ** field_kwargs
350- ) -> Field :
392+ ) -> EnumMixin :
351393 """
352394 *This is a function, not a type*. Some syntactic sugar that wraps the enum
353395 field metaclass so that we can cleanly create enums like so:
0 commit comments