from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponseRedirect from django.views.generic.base import ContextMixin, TemplateResponseMixin, View class StaffRequiredMixin(PermissionRequiredMixin): permission_required = "bds.is_team" class MultipleFormMixin(ContextMixin): """Mixin pour gérer plusieurs formulaires dans la même vue. Le fonctionnement est relativement identique à celui de FormMixin, dont la documentation est disponible ici : https://docs.djangoproject.com/en/3.0/ref/class-based-views/mixins-editing/ Les principales différences sont : - au lieu de form_class, il faut donner comme attribut un dict de la forme {: }, avec tous les formulaires à instancier. On peut aussi redéfinir `get_form_classes` - les données initiales se récupèrent pour chaque form via l'attribut `_initial` ou la fonction `get__initial`. De même, si certaines forms sont des `ModelForm`s, on peut définir la fonction `get__instance`. - chaque form a un préfixe rajouté, par défaut , mais qui peut être customisé via `prefixes` ou `get_prefixes`. """ form_classes = {} prefixes = {} initial = {} success_url = None def get_form_classes(self): return self.form_classes def get_initial(self, form_name): initial_attr = "%s_initial" % form_name initial_method = "get_%s_initial" % form_name initial_method = getattr(self, initial_method, None) if hasattr(self, initial_attr): return getattr(self, initial_attr) elif callable(initial_method): return initial_method() else: return self.initial.copy() def get_prefix(self, form_name): return self.prefixes.get(form_name, form_name) def get_instance(self, form_name): # Au cas où certaines des forms soient des ModelForms instance_method = "get_%s_instance" % form_name instance_method = getattr(self, instance_method, None) if callable(instance_method): return instance_method() else: return None def get_form_kwargs(self, form_name): kwargs = { "initial": self.get_initial(form_name), "prefix": self.get_prefix(form_name), "instance": self.get_instance(form_name), } if self.request.method in ("POST", "PUT"): kwargs.update({"data": self.request.POST, "files": self.request.FILES}) return kwargs def get_forms(self): form_classes = self.get_form_classes() return { form_name: form_class(**self.get_form_kwargs(form_name)) for form_name, form_class in form_classes.items() } def get_success_url(self): if not self.success_url: raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.") return str(self.success_url) def form_valid(self, forms): # on garde le nom form_valid pour l'interface avec SuccessMessageMixin return HttpResponseRedirect(self.get_success_url()) def form_invalid(self, forms): """If the form is invalid, render the invalid form.""" return self.render_to_response(self.get_context_data(forms=forms)) class ProcessMultipleFormView(View): """Équivalent de `ProcessFormView` pour plusieurs forms. Note : il faut que *tous* les formulaires soient valides pour qu'ils soient sauvegardés ! """ def get(self, request, *args, **kwargs): forms = self.get_forms() return self.render_to_response(self.get_context_data(forms=forms)) def post(self, request, *args, **kwargs): forms = self.get_forms() if all(form.is_valid() for form in forms.values()): return self.form_valid(forms) else: return self.form_invalid(forms) class BaseMultipleFormView(MultipleFormMixin, ProcessMultipleFormView): pass class MultipleFormView(TemplateResponseMixin, BaseMultipleFormView): pass