File: helpers.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 (143 lines) | stat: -rw-r--r-- 5,644 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
131
132
133
134
135
136
137
138
139
140
141
142
143
"""
Rendering utils for admin forms;

This makes sure that admin fieldsets/layout settings are exported to the template.
"""

import json

from django.contrib.admin.helpers import AdminField, InlineAdminForm, InlineAdminFormSet
from django.utils.encoding import force_str
from django.utils.text import capfirst
from django.utils.translation import gettext

from polymorphic.formsets import BasePolymorphicModelFormSet


class PolymorphicInlineAdminForm(InlineAdminForm):
    """
    Expose the admin configuration for a form
    """

    def polymorphic_ctype_field(self):
        return AdminField(self.form, "polymorphic_ctype", False)

    @property
    def is_empty(self):
        return "__prefix__" in self.form.prefix


class PolymorphicInlineAdminFormSet(InlineAdminFormSet):
    """
    Internally used class to expose the formset in the template.
    """

    def __init__(self, *args, **kwargs):
        # Assigned later via PolymorphicInlineSupportMixin later.
        self.request = kwargs.pop("request", None)
        self.obj = kwargs.pop("obj", None)
        super().__init__(*args, **kwargs)

    def __iter__(self):
        """
        Output all forms using the proper subtype settings.
        """
        for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
            # Output the form
            model = original.get_real_instance_class()
            child_inline = self.opts.get_child_inline_instance(model)
            view_on_site_url = self.opts.get_view_on_site_url(original)

            yield PolymorphicInlineAdminForm(
                formset=self.formset,
                form=form,
                fieldsets=self.get_child_fieldsets(child_inline),
                prepopulated_fields=self.get_child_prepopulated_fields(child_inline),
                original=original,
                readonly_fields=self.get_child_readonly_fields(child_inline),
                model_admin=child_inline,
                view_on_site_url=view_on_site_url,
            )

        # Extra rows, and empty prefixed forms.
        for form in self.formset.extra_forms + self.formset.empty_forms:
            model = form._meta.model
            child_inline = self.opts.get_child_inline_instance(model)
            yield PolymorphicInlineAdminForm(
                formset=self.formset,
                form=form,
                fieldsets=self.get_child_fieldsets(child_inline),
                prepopulated_fields=self.get_child_prepopulated_fields(child_inline),
                original=None,
                readonly_fields=self.get_child_readonly_fields(child_inline),
                model_admin=child_inline,
            )

    def get_child_fieldsets(self, child_inline):
        return list(child_inline.get_fieldsets(self.request, self.obj) or ())

    def get_child_readonly_fields(self, child_inline):
        return list(child_inline.get_readonly_fields(self.request, self.obj))

    def get_child_prepopulated_fields(self, child_inline):
        fields = self.prepopulated_fields.copy()
        fields.update(child_inline.get_prepopulated_fields(self.request, self.obj))
        return fields

    def inline_formset_data(self):
        """
        A JavaScript data structure for the JavaScript code
        This overrides the default Django version to add the ``childTypes`` data.
        """
        verbose_name = self.opts.verbose_name
        return json.dumps(
            {
                "name": f"#{self.formset.prefix}",
                "options": {
                    "prefix": self.formset.prefix,
                    "addText": gettext("Add another %(verbose_name)s")
                    % {"verbose_name": capfirst(verbose_name)},
                    "childTypes": [
                        {
                            "type": model._meta.model_name,
                            "name": force_str(model._meta.verbose_name),
                        }
                        for model in self.formset.child_forms.keys()
                    ],
                    "deleteText": gettext("Remove"),
                },
            }
        )


class PolymorphicInlineSupportMixin:
    """
    A Mixin to add to the regular admin, so it can work with our polymorphic inlines.

    This mixin needs to be included in the admin that hosts the ``inlines``.
    It makes sure the generated admin forms have different fieldsets/fields
    depending on the polymorphic type of the form instance.

    This is achieved by overwriting :func:`get_inline_formsets` to return
    an :class:`PolymorphicInlineAdminFormSet` instead of a standard Django
    :class:`~django.contrib.admin.helpers.InlineAdminFormSet` for the polymorphic formsets.
    """

    def get_inline_formsets(self, request, formsets, inline_instances, obj=None, *args, **kwargs):
        """
        Overwritten version to produce the proper admin wrapping for the
        polymorphic inline formset. This fixes the media and form appearance
        of the inline polymorphic models.
        """
        inline_admin_formsets = super().get_inline_formsets(
            request, formsets, inline_instances, obj=obj
        )

        for admin_formset in inline_admin_formsets:
            if isinstance(admin_formset.formset, BasePolymorphicModelFormSet):
                # This is a polymorphic formset, which belongs to our inline.
                # Downcast the admin wrapper that generates the form fields.
                admin_formset.__class__ = PolymorphicInlineAdminFormSet
                admin_formset.request = request
                admin_formset.obj = obj
        return inline_admin_formsets