123 lines
3.6 KiB
Python
Executable File
123 lines
3.6 KiB
Python
Executable File
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
|