from functools import partial from django import forms from django.db import models from django_extensions.db.fields import ( CreationDateTimeField as BaseCreationDateTimeField, ModificationDateTimeField as BaseModificationDateTimeField, ) from django_fsm import FSMFieldMixin from outbo.apps.core.forms import EnumChoiceField class CreationDateTimeField(BaseCreationDateTimeField): """ CreationDateTimeField By default, sets editable=False, blank=True, auto_now_add=True """ class ModificationDateTimeField(BaseModificationDateTimeField): """ ModificationDateTimeField By default, sets editable=False, blank=True, auto_now=True Sets value to now every time the object is saved. """ class RemovalDateTimeField(models.DateTimeField): """ RemovalDateTimeField By default, sets editable=False, blank=True, null=True """ def __init__(self, *args, **kwargs): kwargs.setdefault("editable", False) kwargs.setdefault("blank", True) kwargs.setdefault("null", True) models.DateTimeField.__init__(self, *args, **kwargs) def get_internal_type(self): return "DateTimeField" def deconstruct(self): name, path, args, kwargs = super().deconstruct() if self.editable is not False: kwargs["editable"] = True if self.blank is not True: kwargs["blank"] = False if self.null is not True: kwargs["null"] = False return name, path, args, kwargs class FSMKeyField(FSMFieldMixin, models.ForeignKey): def get_state(self, instance): return instance.__dict__[self.attname] def set_state(self, instance, state): instance.__dict__[self.attname] = self.to_python(state) class EnumCharField(models.CharField): def __init__(self, *args, **kwargs): enum = kwargs.pop("enum", None) if enum is not None: if kwargs.get("choices", None) is not None: raise ValueError("Cannot provide choices for EnumCharField") kwargs["choices"] = [(item, item.value) for item in enum] super().__init__(*args, **kwargs) self.validators.pop() # lengthvalidator not necessary on this one self.enum = enum def get_internal_type(self): return "CharField" def formfield(self, **kwargs): enum_form_class = partial(EnumChoiceField, enum=self.enum) defaults = { "widget": forms.Select, "form_class": enum_form_class, "choices": [(str(item), item.value) for item in self.enum], } defaults.update(kwargs) return super().formfield(**defaults) def deconstruct(self): name, path, args, kwargs = super().deconstruct() kwargs["enum"] = self.enum if "choices" in kwargs: del kwargs["choices"] return name, path, args, kwargs def to_python(self, value): value = super().to_python(value) if value is not None: if not isinstance(value, self.enum): enum_name, enum_value = value.split(".", 1) if enum_name == self.enum.__name__: return self.enum[enum_value] return value def get_prep_value(self, value): value = super().get_prep_value(value) if value is None: return value if isinstance(value, self.enum): return str(value) return value def from_db_value(self, value, *args): if value is not None: enum_name, enum_value = value.split(".", 1) if enum_name == self.enum.__name__: return self.enum[enum_value] return value