1from django.core.validators import RegexValidator
2from django.db import models
3
4from utilities.ordering import naturalize
5from .forms import ColorSelect
6
7ColorValidator = RegexValidator(
8    regex='^[0-9a-f]{6}$',
9    message='Enter a valid hexadecimal RGB color code.',
10    code='invalid'
11)
12
13
14# Deprecated: Retained only to ensure successful migration from early releases
15# Use models.CharField(null=True) instead
16class NullableCharField(models.CharField):
17    description = "Stores empty values as NULL rather than ''"
18
19    def to_python(self, value):
20        if isinstance(value, models.CharField):
21            return value
22        return value or ''
23
24    def get_prep_value(self, value):
25        return value or None
26
27
28class ColorField(models.CharField):
29    default_validators = [ColorValidator]
30    description = "A hexadecimal RGB color code"
31
32    def __init__(self, *args, **kwargs):
33        kwargs['max_length'] = 6
34        super().__init__(*args, **kwargs)
35
36    def formfield(self, **kwargs):
37        kwargs['widget'] = ColorSelect
38        return super().formfield(**kwargs)
39
40
41class NaturalOrderingField(models.CharField):
42    """
43    A field which stores a naturalized representation of its target field, to be used for ordering its parent model.
44
45    :param target_field: Name of the field of the parent model to be naturalized
46    :param naturalize_function: The function used to generate a naturalized value (optional)
47    """
48    description = "Stores a representation of its target field suitable for natural ordering"
49
50    def __init__(self, target_field, naturalize_function=naturalize, *args, **kwargs):
51        self.target_field = target_field
52        self.naturalize_function = naturalize_function
53        super().__init__(*args, **kwargs)
54
55    def pre_save(self, model_instance, add):
56        """
57        Generate a naturalized value from the target field
58        """
59        original_value = getattr(model_instance, self.target_field)
60        naturalized_value = self.naturalize_function(original_value, max_length=self.max_length)
61        setattr(model_instance, self.attname, naturalized_value)
62
63        return naturalized_value
64
65    def deconstruct(self):
66        kwargs = super().deconstruct()[3]  # Pass kwargs from CharField
67        kwargs['naturalize_function'] = self.naturalize_function
68        return (
69            self.name,
70            'utilities.fields.NaturalOrderingField',
71            [self.target_field],
72            kwargs,
73        )
74