From 1de84e17c9a0557b0dd1aa093f5c6a47dbc762ab Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Thu, 3 Nov 2022 07:20:30 +0000 Subject: [PATCH] Add CRUD template views --- core/views/__init__.py | 257 +++++++++++++++++++++++++++++++++++++++++ core/views/base.py | 1 + 2 files changed, 258 insertions(+) diff --git a/core/views/__init__.py b/core/views/__init__.py index e69de29..a2c2fa5 100644 --- a/core/views/__init__.py +++ b/core/views/__init__.py @@ -0,0 +1,257 @@ +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 ObjectList(ListView): + allowed_types = ["modal", "widget", "window", "page"] + window_content = "window-content/objects.html" + list_template = None + + model = None + context_object_name = "objects" + context_object_name_singular = "object" + page_title = None + page_subtitle = None + + title = "Objects" + title_singular = "Object" + + 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] + + 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: + self.template_name = self.list_template + return self.render_to_response(context) + + +class ObjectCreate(CreateView): + allowed_types = ["modal", "widget", "window", "page"] + window_content = "window-content/object-form.html" + parser_classes = [FormParser] + + model = None + context_object_name = "objects" + 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(DetailView): + allowed_types = ["modal", "widget", "window", "page"] + window_content = "window-content/object.html" + + model = None + context_object_name = "object" + + +class ObjectUpdate(UpdateView): + allowed_types = ["modal", "widget", "window", "page"] + window_content = "window-content/object-form.html" + parser_classes = [FormParser] + + model = None + context_object_name = "objects" + 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(DeleteView): + model = None + context_object_name_singular = "object" + 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 diff --git a/core/views/base.py b/core/views/base.py index 6347332..71f5ed4 100644 --- a/core/views/base.py +++ b/core/views/base.py @@ -96,6 +96,7 @@ class Signup(CreateView): return render(request, "registration/registration_closed.html") super().get(request, *args, **kwargs) + class Portal(LoginRequiredMixin, View): async def get(self, request): if not settings.STRIPE_ENABLED: