• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

liqd / adhocracy-plus / 18908688697

29 Oct 2025 12:59PM UTC coverage: 44.622% (-44.5%) from 89.135%
18908688697

Pull #2986

github

web-flow
Merge 1dfde8ee7 into 445e1d498
Pull Request #2986: Draft: Speed up Github Ci Tests

3012 of 6750 relevant lines covered (44.62%)

0.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.71
/apps/contrib/forms.py
1
# flake8: noqa
2

3
from django.core.exceptions import ImproperlyConfigured
1✔
4
from django.db import transaction
1✔
5
from django.forms import models as model_forms
1✔
6
from django.forms.formsets import all_valid
1✔
7
from django.http import HttpResponseRedirect
1✔
8
from django.utils.encoding import force_str
1✔
9
from django.views import generic
1✔
10

11

12
class MultiFormMixin(generic.base.ContextMixin):
1✔
13
    forms = {}
1✔
14
    success_url = None
1✔
15
    prefix = None
1✔
16

17
    def forms_invalid(self, forms):
1✔
18
        """
19
        If any form is invalid, re-render the context data with the
20
        data-filled forms and errors.
21
        """
22
        return self.render_to_response(self.get_context_data(forms=forms))
×
23

24
    def forms_valid(self, forms):
1✔
25
        """
26
        If all forms are valid, redirect to the supplied URL.
27
        """
28
        return HttpResponseRedirect(self.get_success_url())
×
29

30
    def get_context_data(self, **kwargs):
1✔
31
        """
32
        Insert the forms into the context dict.
33
        """
34
        if "forms" not in kwargs:
×
35
            kwargs["forms"] = self.get_forms()
×
36
        return super().get_context_data(**kwargs)
×
37

38
    def get_forms(self):
1✔
39
        """
40
        Returns instances of the forms to be used in this view.
41
        """
42
        forms = {}
×
43
        for name in self.forms.keys():
×
44
            form_class = self.get_form_class(name)
×
45
            if form_class:
×
46
                forms[name] = form_class(**self.get_form_kwargs(name))
×
47
        return forms
×
48

49
    def _get_from_name(self, name, key, default=None):
1✔
50
        form = self.forms.get(name)
×
51
        if form:
×
52
            return form.get(key, default)
×
53

54
    def get_form_class(self, name):
1✔
55
        """
56
        Returns the form class to be used with the named form.
57
        """
58
        return self._get_from_name(name, "form_class")
×
59

60
    def get_initial(self, name):
1✔
61
        """
62
        Returns the initial data to use for the named form.
63
        """
64
        initial = self._get_from_name(name, "initial", {})
×
65
        return initial.copy()
×
66

67
    def get_prefix(self, name):
1✔
68
        """
69
        Returns the prefix to use for the named form.
70
        """
71
        if self.prefix:
×
72
            return "{}_{}".format(self.prefix, name)
×
73
        return name
×
74

75
    def get_form_kwargs(self, name):
1✔
76
        """
77
        Returns the keyword arguments for instantiating the named form.
78
        """
79
        kwargs = {
×
80
            "initial": self.get_initial(name),
81
            "prefix": self.get_prefix(name),
82
        }
83
        if self.request.method in ("POST", "PUT"):
×
84
            kwargs.update(
×
85
                {
86
                    "data": self.request.POST,
87
                    "files": self.request.FILES,
88
                }
89
            )
90
        return kwargs
×
91

92
    def get_success_url(self):
1✔
93
        """
94
        Returns the supplied success URL.
95
        """
96
        if self.success_url:
×
97
            # Forcing possible reverse_lazy evaluation
98
            url = force_str(self.success_url)
×
99
        else:
100
            raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
×
101
        return url
×
102

103

104
class MultiModelFormMixin(MultiFormMixin):
1✔
105
    objects = {}
1✔
106

107
    def forms_valid(self, forms):
1✔
108
        """
109
        If all forms are valid, save the associated models.
110
        """
111
        self.objects = self.forms_save(forms)
×
112
        self.forms_save_m2m(forms)
×
113
        return super().forms_valid(forms)
×
114

115
    def forms_save(self, forms, commit=True):
1✔
116
        """
117
        Save all the forms in one transaction.
118
        """
119
        objects = {}
×
120
        with transaction.atomic():
×
121
            for name in self.forms.keys():
×
122
                if hasattr(forms[name], "save"):
×
123
                    objects[name] = forms[name].save(commit)
×
124
        return objects
×
125

126
    def forms_save_m2m(self, forms):
1✔
127
        """
128
        Calls save_m2m on every form where it is available.
129
        Has to be called after the forms have been saved.
130
        """
131
        for form in forms.values():
×
132
            if hasattr(form, "save_m2m"):
×
133
                form.save_m2m()
×
134

135
    def get_form_class(self, name):
1✔
136
        """
137
        Returns the form class to be used with the named form.
138
        """
139
        fields = self._get_from_name(name, "fields")
×
140
        form_class = self._get_from_name(name, "form_class")
×
141
        model = self._get_from_name(name, "model")
×
142

143
        if fields is not None and form_class:
×
144
            raise ImproperlyConfigured(
×
145
                "Specifying both 'fields' and 'form_class' is not permitted."
146
            )
147

148
        if form_class:
×
149
            return form_class
×
150
        elif model is not None:
×
151
            if fields is None:
×
152
                raise ImproperlyConfigured(
×
153
                    "Using MultiModelFormMixin (base class of %s) without "
154
                    "the 'fields' attribute is prohibited." % self.__class__.__name__
155
                )
156
            return model_forms.modelform_factory(model, fields=fields)
×
157

158
    def get_form_kwargs(self, name):
1✔
159
        """
160
        Returns the keyword arguments for instantiating the named form.
161
        """
162
        kwargs = super().get_form_kwargs(name)
×
163
        instance = self.get_instance(name)
×
164
        if instance:
×
165
            kwargs.update({"instance": instance})
×
166
        return kwargs
×
167

168
    def get_instance(self, name):
1✔
169
        """
170
        Returns the instance object used for instantiating the named form.
171
        If no instance (None) is returned the django BaseModelForm
172
        creates a default instance of the provided model.
173
        """
174
        pass
×
175

176

177
class ProcessMultiFormView(generic.View):
1✔
178
    def get(self, request, *args, **kwargs):
1✔
179
        """
180
        Handles GET requests and instantiates a blank version of the form.
181
        """
182
        return self.render_to_response(self.get_context_data())
×
183

184
    def post(self, request, *args, **kwargs):
1✔
185
        """
186
        Handles POST requests, instantiating a form instance with the passed
187
        POST variables and then checked for validity.
188
        """
189
        forms = self.get_forms()
×
190

191
        if all_valid(forms.values()):
×
192
            return self.forms_valid(forms)
×
193
        else:
194
            return self.forms_invalid(forms)
×
195

196
    def put(self, *args, **kwargs):
1✔
197
        return self.post(*args, **kwargs)
×
198

199

200
class BaseMultiFormView(MultiFormMixin, ProcessMultiFormView):
1✔
201
    """
202
    A base view for displaying multiple forms.
203
    """
204

205

206
class BaseMultiModelFormView(MultiModelFormMixin, ProcessMultiFormView):
1✔
207
    """
208
    A base view for displaying multiple forms that may contain ModelForms.
209
    """
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc