import uuid from django.http import Http404, HttpResponseBadRequest from django.urls import reverse from django.views.generic.detail import DetailView from django.views.generic.edit import CreateView, DeleteView, UpdateView from django.views.generic.list import ListView from rest_framework.parsers import FormParser from core.util import logs log = logs.get_logger(__name__) class ObjectNameMixin(object): def __init__(self, *args, **kwargs): self.title_singular = self.model._meta.verbose_name.title() # Hook self.context_object_name_singular = self.title_singular.lower() # hook self.title = self.model._meta.verbose_name_plural.title() # Hooks self.context_object_name = self.title.lower() # hooks super().__init__(*args, **kwargs) class ObjectList(ObjectNameMixin, ListView): allowed_types = ["modal", "widget", "window", "page"] window_content = "window-content/objects.html" list_template = None page_title = None page_subtitle = None list_url_name = None # WARNING: TAKEN FROM locals() list_url_args = ["type"] submit_url_name = None delete_all_url_name = None # copied from BaseListView def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() allow_empty = self.get_allow_empty() type = kwargs.get("type", None) if not type: return HttpResponseBadRequest("No type specified") if type not in self.allowed_types: return HttpResponseBadRequest("Invalid type specified") self.template_name = f"wm/{type}.html" unique = str(uuid.uuid4())[:8] list_url_args = {} for arg in self.list_url_args: list_url_args[arg] = locals()[arg] orig_type = type if type == "page": type = "modal" if not allow_empty: # When pagination is enabled and object_list is a queryset, # it's better to do a cheap query than to load the unpaginated # queryset in memory. if self.get_paginate_by(self.object_list) is not None and hasattr( self.object_list, "exists" ): is_empty = not self.object_list.exists() else: is_empty = not self.object_list if is_empty: raise Http404("Empty list") submit_url = reverse(self.submit_url_name, kwargs={"type": type}) list_url = reverse(self.list_url_name, kwargs=list_url_args) context = self.get_context_data() context["title"] = self.title + f" ({type})" context["title_singular"] = self.title_singular context["unique"] = unique context["window_content"] = self.window_content context["list_template"] = self.list_template context["page_title"] = self.page_title context["page_subtitle"] = self.page_subtitle context["type"] = type context["submit_url"] = submit_url context["list_url"] = list_url context["context_object_name"] = self.context_object_name context["context_object_name_singular"] = self.context_object_name_singular if self.delete_all_url_name: context["delete_all_url"] = reverse(self.delete_all_url_name) # Return partials for HTMX if self.request.htmx: if orig_type == "page": self.template_name = self.list_template else: context["window_content"] = self.list_template return self.render_to_response(context) class ObjectCreate(ObjectNameMixin, CreateView): allowed_types = ["modal", "widget", "window", "page"] window_content = "window-content/object-form.html" parser_classes = [FormParser] model = None submit_url_name = None list_url_name = None # WARNING: TAKEN FROM locals() list_url_args = ["type"] request = None def post_save(self, obj): pass def form_valid(self, form): obj = form.save(commit=False) if self.request is None: raise Exception("Request is None") obj.user = self.request.user obj.save() form.save_m2m() self.post_save(obj) context = {"message": "Object created", "class": "success"} response = self.render_to_response(context) response["HX-Trigger"] = f"{self.context_object_name_singular}Event" return response def get(self, request, *args, **kwargs): self.request = request type = kwargs.get("type", None) if not type: return HttpResponseBadRequest("No type specified") if type not in self.allowed_types: return HttpResponseBadRequest("Invalid type specified") self.template_name = f"wm/{type}.html" unique = str(uuid.uuid4())[:8] list_url_args = {} for arg in self.list_url_args: list_url_args[arg] = locals()[arg] if type == "page": type = "modal" self.object = None submit_url = reverse(self.submit_url_name, kwargs={"type": type}) list_url = reverse(self.list_url_name, kwargs=list_url_args) context = self.get_context_data() context["unique"] = unique context["window_content"] = self.window_content context["context_object_name"] = self.context_object_name context["context_object_name_singular"] = self.context_object_name_singular context["submit_url"] = submit_url context["list_url"] = list_url context["type"] = type return self.render_to_response(context) def post(self, request, *args, **kwargs): self.request = request self.template_name = "partials/notify.html" return super().post(request, *args, **kwargs) class ObjectRead(ObjectNameMixin, DetailView): allowed_types = ["modal", "widget", "window", "page"] window_content = "window-content/object.html" model = None class ObjectUpdate(ObjectNameMixin, UpdateView): allowed_types = ["modal", "widget", "window", "page"] window_content = "window-content/object-form.html" parser_classes = [FormParser] model = None submit_url_name = None request = None def post_save(self, obj): pass def form_valid(self, form): obj = form.save(commit=False) if self.request is None: raise Exception("Request is None") obj.save() form.save_m2m() self.post_save(obj) context = {"message": "Object updated", "class": "success"} response = self.render_to_response(context) response["HX-Trigger"] = f"{self.context_object_name_singular}Event" return response def get(self, request, *args, **kwargs): self.request = request type = kwargs.get("type", None) pk = kwargs.get("pk", None) if not type: return HttpResponseBadRequest("No type specified") if not pk: return HttpResponseBadRequest("No pk specified") if type not in self.allowed_types: return HttpResponseBadRequest("Invalid type specified") self.template_name = f"wm/{type}.html" unique = str(uuid.uuid4())[:8] if type == "page": type = "modal" self.object = self.get_object() submit_url = reverse(self.submit_url_name, kwargs={"type": type, "pk": pk}) context = self.get_context_data() context["unique"] = unique context["window_content"] = self.window_content context["context_object_name"] = self.context_object_name context["context_object_name_singular"] = self.context_object_name_singular context["submit_url"] = submit_url context["type"] = type return self.render_to_response(context) def post(self, request, *args, **kwargs): self.request = request self.template_name = "partials/notify.html" return super().post(request, *args, **kwargs) class ObjectDelete(ObjectNameMixin, DeleteView): model = None template_name = "partials/notify.html" # Overriden to prevent success URL from being used def delete(self, request, *args, **kwargs): """ Call the delete() method on the fetched object and then redirect to the success URL. """ self.object = self.get_object() # success_url = self.get_success_url() self.object.delete() context = {"message": "Object deleted", "class": "success"} response = self.render_to_response(context) response["HX-Trigger"] = f"{self.context_object_name_singular}Event" return response # This will be used in newer Django versions, until then we get a warning def form_valid(self, form): """ Call the delete() method on the fetched object. """ self.object = self.get_object() self.object.delete() context = {"message": "Object deleted", "class": "success"} response = self.render_to_response(context) response["HX-Trigger"] = f"{self.context_object_name_singular}Event" return response