Skip to content

Reference

hope_flex_fields.models.FieldDefinition

This class is the equivalent django.forms.Field class, used to create reusable field types

Source code in src/hope_flex_fields/models/definition.py
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
class FieldDefinition(AbstractField):
    """This class is the equivalent django.forms.Field class, used to create reusable field types"""

    field_type = StrategyClassField(registry=field_registry)
    content_type = models.ForeignKey(
        ContentType, on_delete=models.CASCADE, null=True, blank=True
    )
    system_data = models.JSONField(default=dict, blank=True, editable=False, null=True)
    # protected = models.BooleanField(default=False, help_text="If true the field can be deleted only by superusers")

    objects = FieldDefinitionManager()

    class Meta:
        verbose_name = _("Field Definition")
        verbose_name_plural = _("Field Definitions")
        constraints = (
            UniqueConstraint(fields=("name",), name="fielddefinition_unique_name"),
            UniqueConstraint(fields=("slug",), name="fielddefinition_unique_slug"),
        )

    def __str__(self):
        return self.name

    def natural_key(self):
        return (self.name,)

    def clean(self):
        self.set_default_arguments()
        self.name = str(self.name)
        try:
            self.get_field()
        except TypeError:
            self.attrs = {}
            self.set_default_arguments()

    def set_default_arguments(self):
        if not isinstance(self.attrs, dict) or not self.attrs:
            self.attrs = get_default_attrs()
        if self.field_type:
            attrs = get_kwargs_from_field_class(self.field_type)
            attrs.update(**self.attrs)
            self.attrs = attrs

    @property
    def required(self):
        return self.attrs.get("required", False)

    def get_field(self, override_attrs=None):
        try:
            if override_attrs is not None:
                kwargs = dict(override_attrs)
            else:
                kwargs = dict(self.attrs)
            validators = []
            if self.validation:
                validators.append(JsValidator(self.validation))
            if self.regex:
                validators.append(ReValidator(self.regex))

            kwargs["validators"] = validators
            field_class = type(
                f"{self.name}Field", (FlexFormMixin, self.field_type), {}
            )
            fld = field_class(**kwargs)
        except Exception as e:  # pragma: no cover
            logger.exception(e)
            raise TypeError(
                f"Error creating field for FieldDefinition {self.name}: {e}"
            )
        return fld

hope_flex_fields.models.Fieldset

hope_flex_fields.models.FlexField

Source code in src/hope_flex_fields/models/flexfield.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class FlexField(AbstractField):
    fieldset = models.ForeignKey(
        Fieldset, on_delete=models.CASCADE, related_name="fields"
    )
    field = models.ForeignKey(
        FieldDefinition, on_delete=models.CASCADE, related_name="instances"
    )

    objects = FieldsetFieldManager()

    class Meta:
        verbose_name = _("Flex Field")
        verbose_name_plural = _("flex Fields")
        constraints = (
            UniqueConstraint(fields=("name", "fieldset"), name="flexfield_unique_name"),
            UniqueConstraint(fields=("slug", "fieldset"), name="flexfield_unique_slug"),
        )

    def __str__(self):
        return self.name

    def base_type(self):
        return self.field.field_type.__name__

    def validate_attrs(self):
        try:
            self.get_field()
        except Exception as e:
            raise ValidationError(e)

    def clean(self):
        self.name = namefy(str(self.name))
        self.validate_attrs()

    def natural_key(self):
        return self.name, self.fieldset.name

    def get_merged_attrs(self):
        attrs = dict(**self.field.attrs)
        if isinstance(self.attrs, dict):
            attrs.update(self.attrs)
        return attrs

    def get_field(self, override_attrs=None, **extra) -> "FlexFormMixin":
        try:
            if override_attrs is not None:
                kwargs = dict(override_attrs)
            else:
                kwargs = self.get_merged_attrs()
                kwargs.update(extra)
            validators = []
            if self.validation:
                validators.append(JsValidator(self.validation))
            elif self.field.validation:
                validators.append(JsValidator(self.field.validation))

            if self.regex:
                validators.append(ReValidator(self.regex))
            elif self.field.regex:
                validators.append(ReValidator(self.field.regex))

            kwargs["validators"] = validators
            field_class = type(
                f"{self.name}Field",
                (FlexFormMixin, self.field.field_type),
                {"flex_field": self},
            )
            fld = field_class(**kwargs)
        except Exception as e:  # pragma: no cover
            logger.exception(e)
            raise TypeError(f"Error creating field for FlexField {self.name}: {e}")
        return fld

hope_flex_fields.models.DataChecker

Used for complex validations to combine different fieldsets

Source code in src/hope_flex_fields/models/datachecker.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
class DataChecker(ValidatorMixin, models.Model):
    """Used for complex validations to combine different fieldsets"""

    last_modified = models.DateTimeField(auto_now=True)
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(blank=True)
    fieldsets = models.ManyToManyField(Fieldset, through=DataCheckerFieldset)
    objects = DataCheckerManager()

    class Meta:
        verbose_name = _("DataChecker")
        verbose_name_plural = _("DataCheckers")

    def __str__(self):
        return self.name

    def natural_key(self):
        return (self.name,)

    def get_fields(self):
        for fs in self.members.all():
            for field in fs.fieldset.fields.filter():
                yield field

    def get_form(self) -> "type[FlexForm]":
        fields: dict[str, forms.Field] = {}
        field: "FlexField"
        for fs in self.members.all():
            for field in fs.fieldset.fields.filter():
                fld: FlexFormMixin = field.get_field()
                fld.label = f"{fs.prefix}{field.name}"
                if "%s" in fs.prefix:
                    full_name = fs.prefix % field.name
                else:
                    full_name = f"{fs.prefix}{field.name}"

                fields[full_name] = fld
        form_class_attrs = {"DataChecker": self, **dict(sorted(fields.items()))}
        return type(f"{self.name}DataChecker", (FlexForm,), form_class_attrs)