Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/polymorphic/admin/helpers.py: 40%

47 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2Rendering utils for admin forms; 

3 

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

5""" 

6import json 

7 

8from django.contrib.admin.helpers import AdminField, InlineAdminForm, InlineAdminFormSet 

9from django.utils.encoding import force_str 

10from django.utils.text import capfirst 

11from django.utils.translation import gettext 

12 

13from polymorphic.formsets import BasePolymorphicModelFormSet 

14 

15 

16class PolymorphicInlineAdminForm(InlineAdminForm): 

17 """ 

18 Expose the admin configuration for a form 

19 """ 

20 

21 def polymorphic_ctype_field(self): 

22 return AdminField(self.form, "polymorphic_ctype", False) 

23 

24 @property 

25 def is_empty(self): 

26 return "__prefix__" in self.form.prefix 

27 

28 

29class PolymorphicInlineAdminFormSet(InlineAdminFormSet): 

30 """ 

31 Internally used class to expose the formset in the template. 

32 """ 

33 

34 def __init__(self, *args, **kwargs): 

35 # Assigned later via PolymorphicInlineSupportMixin later. 

36 self.request = kwargs.pop("request", None) 

37 self.obj = kwargs.pop("obj", None) 

38 super().__init__(*args, **kwargs) 

39 

40 def __iter__(self): 

41 """ 

42 Output all forms using the proper subtype settings. 

43 """ 

44 for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()): 

45 # Output the form 

46 model = original.get_real_instance_class() 

47 child_inline = self.opts.get_child_inline_instance(model) 

48 view_on_site_url = self.opts.get_view_on_site_url(original) 

49 

50 yield PolymorphicInlineAdminForm( 

51 formset=self.formset, 

52 form=form, 

53 fieldsets=self.get_child_fieldsets(child_inline), 

54 prepopulated_fields=self.get_child_prepopulated_fields(child_inline), 

55 original=original, 

56 readonly_fields=self.get_child_readonly_fields(child_inline), 

57 model_admin=child_inline, 

58 view_on_site_url=view_on_site_url, 

59 ) 

60 

61 # Extra rows, and empty prefixed forms. 

62 for form in self.formset.extra_forms + self.formset.empty_forms: 

63 model = form._meta.model 

64 child_inline = self.opts.get_child_inline_instance(model) 

65 yield PolymorphicInlineAdminForm( 

66 formset=self.formset, 

67 form=form, 

68 fieldsets=self.get_child_fieldsets(child_inline), 

69 prepopulated_fields=self.get_child_prepopulated_fields(child_inline), 

70 original=None, 

71 readonly_fields=self.get_child_readonly_fields(child_inline), 

72 model_admin=child_inline, 

73 ) 

74 

75 def get_child_fieldsets(self, child_inline): 

76 return list(child_inline.get_fieldsets(self.request, self.obj) or ()) 

77 

78 def get_child_readonly_fields(self, child_inline): 

79 return list(child_inline.get_readonly_fields(self.request, self.obj)) 

80 

81 def get_child_prepopulated_fields(self, child_inline): 

82 fields = self.prepopulated_fields.copy() 

83 fields.update(child_inline.get_prepopulated_fields(self.request, self.obj)) 

84 return fields 

85 

86 def inline_formset_data(self): 

87 """ 

88 A JavaScript data structure for the JavaScript code 

89 This overrides the default Django version to add the ``childTypes`` data. 

90 """ 

91 verbose_name = self.opts.verbose_name 

92 return json.dumps( 

93 { 

94 "name": "#%s" % self.formset.prefix, 

95 "options": { 

96 "prefix": self.formset.prefix, 

97 "addText": gettext("Add another %(verbose_name)s") 

98 % {"verbose_name": capfirst(verbose_name)}, 

99 "childTypes": [ 

100 { 

101 "type": model._meta.model_name, 

102 "name": force_str(model._meta.verbose_name), 

103 } 

104 for model in self.formset.child_forms.keys() 

105 ], 

106 "deleteText": gettext("Remove"), 

107 }, 

108 } 

109 ) 

110 

111 

112class PolymorphicInlineSupportMixin: 

113 """ 

114 A Mixin to add to the regular admin, so it can work with our polymorphic inlines. 

115 

116 This mixin needs to be included in the admin that hosts the ``inlines``. 

117 It makes sure the generated admin forms have different fieldsets/fields 

118 depending on the polymorphic type of the form instance. 

119 

120 This is achieved by overwriting :func:`get_inline_formsets` to return 

121 an :class:`PolymorphicInlineAdminFormSet` instead of a standard Django 

122 :class:`~django.contrib.admin.helpers.InlineAdminFormSet` for the polymorphic formsets. 

123 """ 

124 

125 def get_inline_formsets(self, request, formsets, inline_instances, obj=None, *args, **kwargs): 

126 """ 

127 Overwritten version to produce the proper admin wrapping for the 

128 polymorphic inline formset. This fixes the media and form appearance 

129 of the inline polymorphic models. 

130 """ 

131 inline_admin_formsets = super().get_inline_formsets( 

132 request, formsets, inline_instances, obj=obj 

133 ) 

134 

135 for admin_formset in inline_admin_formsets: 

136 if isinstance(admin_formset.formset, BasePolymorphicModelFormSet): 

137 # This is a polymorphic formset, which belongs to our inline. 

138 # Downcast the admin wrapper that generates the form fields. 

139 admin_formset.__class__ = PolymorphicInlineAdminFormSet 

140 admin_formset.request = request 

141 admin_formset.obj = obj 

142 return inline_admin_formsets