from typing import Any from django.http import JsonResponse from django.template.response import TemplateResponse from django.utils.decorators import method_decorator from django.views.decorators.http import require_POST from django.views.generic.base import TemplateResponseMixin, View from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import FormMixin, ModelFormMixin, ProcessFormView # ############################################################################# # Views for use with AJAX # ############################################################################# class JsonMixin: success = True errors = {} def get_data(self, **kwargs): data = {"success": self.success, "errors": self.errors} data.update(kwargs) return data def render_to_json(self, **kwargs): return JsonResponse(self.get_data(**kwargs)) class JsonFormMixin(JsonMixin, FormMixin): def form_valid(self, form): """If the form is valid, return success""" return self.render_to_json() def form_invalid(self, form): """If the form is invalid, return the errors and no success""" return self.render_to_json(success=False, errors=form.errors) class JsonModelFormMixin(JsonFormMixin, ModelFormMixin): def form_valid(self, form): """Override form_valid to return a JSON response""" self.object = form.save() return super().form_valid(form) class JsonMessageMixin: message = "" def get_message(self): return {"content": self.message, "class": "success"} def get_data(self, **kwargs): kwargs.update(message=self.get_message()) return super().get_data(**kwargs) # pyright: ignore class TypedResponseMixin(TemplateResponseMixin): def render_to_response( self, context: dict[str, Any], **response_kwargs: Any ) -> TemplateResponse: return super().render_to_response(context, **response_kwargs) # pyright: ignore class JsonDetailView(JsonMixin, SingleObjectMixin, TypedResponseMixin, View): def get(self, request, *args, **kwargs): self.object = self.get_object() context = self.get_context_data(object=self.object) return self.render_to_json( html=self.render_to_response(context).rendered_content ) class JsonDeleteView(JsonMessageMixin, JsonDetailView): def get(self, request, *args, **kwargs): obj = self.get_object() obj.delete() return self.render_to_json(action="delete") @method_decorator(require_POST, name="dispatch") class JsonCreateView( JsonMessageMixin, JsonModelFormMixin, TypedResponseMixin, ProcessFormView ): def render_to_json(self, **kwargs): context = self.get_context_data(object=self.object) kwargs.update( html=self.render_to_response(context).rendered_content, action="create" ) return super().render_to_json(**kwargs) @method_decorator(require_POST, name="dispatch") class JsonUpdateView( JsonMessageMixin, JsonModelFormMixin, TypedResponseMixin, ProcessFormView ): def post(self, request, *args, **kwargs): self.object = self.get_object() return super().post(request, *args, **kwargs) def render_to_json(self, **kwargs): context = self.get_context_data(object=self.object) kwargs.update( html=self.render_to_response(context).rendered_content, action="update" ) return super().render_to_json(**kwargs)