File: generic.py

package info (click to toggle)
django-polymorphic 4.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 892 kB
  • sloc: python: 6,784; javascript: 263; makefile: 137
file content (130 lines) | stat: -rw-r--r-- 4,184 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
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
120
121
122
123
124
125
126
127
128
129
130
from django.contrib.contenttypes.forms import (
    BaseGenericInlineFormSet,
    generic_inlineformset_factory,
)
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.forms.models import ModelForm

from .models import (
    BasePolymorphicModelFormSet,
    PolymorphicFormSetChild,
    polymorphic_child_forms_factory,
)


class GenericPolymorphicFormSetChild(PolymorphicFormSetChild):
    """
    Formset child for generic inlines
    """

    def __init__(self, *args, **kwargs):
        self.ct_field = kwargs.pop("ct_field", "content_type")
        self.fk_field = kwargs.pop("fk_field", "object_id")
        super().__init__(*args, **kwargs)

    def get_form(self, ct_field="content_type", fk_field="object_id", **kwargs):
        """
        Construct the form class for the formset child.
        """
        exclude = list(self.exclude)
        extra_exclude = kwargs.pop("extra_exclude", None)
        if extra_exclude:
            exclude += list(extra_exclude)

        # Make sure the GFK fields are excluded by default
        # This is similar to what generic_inlineformset_factory() does
        # if there is no field called `ct_field` let the exception propagate
        opts = self.model._meta
        ct_field = opts.get_field(self.ct_field)

        if (
            not isinstance(ct_field, models.ForeignKey)
            or ct_field.remote_field.model != ContentType
        ):
            raise Exception(f"fk_name '{ct_field}' is not a ForeignKey to ContentType")

        fk_field = opts.get_field(self.fk_field)  # let the exception propagate
        exclude.extend([ct_field.name, fk_field.name])
        kwargs["exclude"] = exclude

        return super().get_form(**kwargs)


class BaseGenericPolymorphicInlineFormSet(BaseGenericInlineFormSet, BasePolymorphicModelFormSet):
    """
    Polymorphic formset variation for inline generic formsets
    """


def generic_polymorphic_inlineformset_factory(
    model,
    formset_children,
    form=ModelForm,
    formset=BaseGenericPolymorphicInlineFormSet,
    ct_field="content_type",
    fk_field="object_id",
    # Base form
    # TODO: should these fields be removed in favor of creating
    # the base form as a formset child too?
    fields=None,
    exclude=None,
    extra=1,
    can_order=False,
    can_delete=True,
    max_num=None,
    formfield_callback=None,
    validate_max=False,
    for_concrete_model=True,
    min_num=None,
    validate_min=False,
    child_form_kwargs=None,
):
    """
    Construct the class for a generic inline polymorphic formset.

    All arguments are identical to :func:`~django.contrib.contenttypes.forms.generic_inlineformset_factory`,
    with the exception of the ``formset_children`` argument.

    :param formset_children: A list of all child :class:`PolymorphicFormSetChild` objects
                             that tell the inline how to render the child model types.
    :type formset_children: Iterable[PolymorphicFormSetChild]
    :rtype: type
    """
    kwargs = {
        "model": model,
        "form": form,
        "formfield_callback": formfield_callback,
        "formset": formset,
        "ct_field": ct_field,
        "fk_field": fk_field,
        "extra": extra,
        "can_delete": can_delete,
        "can_order": can_order,
        "fields": fields,
        "exclude": exclude,
        "min_num": min_num,
        "max_num": max_num,
        "validate_min": validate_min,
        "validate_max": validate_max,
        "for_concrete_model": for_concrete_model,
        # 'localized_fields': localized_fields,
        # 'labels': labels,
        # 'help_texts': help_texts,
        # 'error_messages': error_messages,
        # 'field_classes': field_classes,
    }
    if child_form_kwargs is None:
        child_form_kwargs = {}

    child_kwargs = {
        # 'exclude': exclude,
        "ct_field": ct_field,
        "fk_field": fk_field,
    }
    if child_form_kwargs:
        child_kwargs.update(child_form_kwargs)

    FormSet = generic_inlineformset_factory(**kwargs)
    FormSet.child_forms = polymorphic_child_forms_factory(formset_children, **child_kwargs)
    return FormSet