""" Stejně jako je `django.views.generic` jsou zde generické Views a pár mixinů, které upravují chování Views. """ import http import shutil import subprocess import tempfile from pathlib import Path import django.views from django.http import HttpResponse from django.shortcuts import render from django.template.loader import render_to_string from django.views import generic def viewMethodSwitch(get, post): """ Vrátí view, který zavolá různé jiné views podle toho, kterou metodou je zavolán. Inspirováno https://docs.djangoproject.com/en/3.1/topics/class-based-views/mixins/#an-alternative-better-solution, jen jsem to udělal genericky. Parametry: post view pro metodu POST get view pro metodu GET V obou případech se míní už view jakožto funkce, takže u class-based views se už má použít .as_view() TODO: Podpora i pro metodu HEAD? A možná i pro FILES? """ theGetView = get thePostView = post class NewView(django.views.View): def get(self, request, *args, **kwargs): return theGetView(request, *args, **kwargs) def post(self, request, *args, **kwargs): return thePostView(request, *args, **kwargs) return NewView.as_view() class NeprazdnyListView(generic.ListView): """ Použití jako generic.ListView, jen při prázdném listu vyhodí M&M stránku s titlem `self.if_prazdny_title` a textem `self.if_prazdny_text` a způsob renderování (např. CSV) lze změnit přepsáním metody render. """ allow_empty = False # Interní djangová věc if_prazdny_title = "V seznamu nic není" if_prazdny_text = "V seznamu nic není. Zkus to napravit v adminu, nebo se zeptej webařů." # Skoro copy-paste generic.list.ListView.get, # protože nemůžu chytat 404, neboť může nastat i v get_context_data def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() 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: return render(request, 'universal.html', { 'title': self.if_prazdny_title, 'text': self.if_prazdny_text, }, status=http.HTTPStatus.NOT_FOUND) return self.render(request, *args, **kwargs) # Tohle jsem vyčlenil, aby šlo generovat i něco jiného než template def render(self, request, *args, **kwargs): context = self.get_context_data() return self.render_to_response(context) class TeXResponseMixin: """ Mixin pro TemplateView, aby výsledek projel TeXem a vrátil rovnou PDF. Obrázky a jiné soubory lze přidat nastavením `dalsi_potrebne_soubory` (např. na `[django.contrib.staticfiles.finders.find('bla')]`, nebo jiný seznam absolutních cest). """ dalsi_potrebne_soubory = [] tex = "pdflatex" def render_to_response(self, context, **response_kwargs): zdrojak = render_to_string(self.get_template_names(), context) with tempfile.TemporaryDirectory() as tempdirfn: tempdir = Path(tempdirfn) with open(tempdir / "main.tex", "w") as texfile: texfile.write(zdrojak) for file in self.dalsi_potrebne_soubory: shutil.copy(file, tempdir) subprocess.call([self.tex, "main.tex"], cwd=tempdir, stdout=subprocess.DEVNULL) with open(tempdir / "main.pdf", "rb") as pdffile: response = HttpResponse(pdffile.read(), content_type='application/pdf', **response_kwargs) return response