"""View functions for categories.""" from typing import Optional from django.http import Http404, HttpResponse from django.shortcuts import get_object_or_404 from django.template.loader import select_template from django.utils.translation import gettext_lazy as _ from django.views.generic import DetailView, ListView from .models import Category def category_detail( request, path, template_name="categories/category_detail.html", extra_context: Optional[dict] = None ): """Render the detail page for a category.""" extra_context = extra_context or {} path_items = path.strip("/").split("/") if len(path_items) >= 2: category = get_object_or_404( Category, slug__iexact=path_items[-1], level=len(path_items) - 1, parent__slug__iexact=path_items[-2] ) else: category = get_object_or_404(Category, slug__iexact=path_items[-1], level=len(path_items) - 1) templates = [] while path_items: templates.append("categories/%s.html" % "_".join(path_items)) path_items.pop() templates.append(template_name) context = {"category": category} if extra_context: context.update(extra_context) return HttpResponse(select_template(templates).render(context)) def get_category_for_path(path, queryset=Category.objects.all()): """Return the category for a path.""" path_items = path.strip("/").split("/") if len(path_items) >= 2: queryset = queryset.filter( slug__iexact=path_items[-1], level=len(path_items) - 1, parent__slug__iexact=path_items[-2] ) else: queryset = queryset.filter(slug__iexact=path_items[-1], level=len(path_items) - 1) return queryset.get() class CategoryDetailView(DetailView): """Detail view for a category.""" model = Category path_field = "path" def get_object(self, **kwargs): """Get the category.""" if self.path_field not in self.kwargs: raise AttributeError( "Category detail view %s must be called with " "a %s." % (self.__class__.__name__, self.path_field) ) if self.queryset is None: queryset = self.get_queryset() try: return get_category_for_path(self.kwargs[self.path_field], self.model.objects.all()) except Category.DoesNotExist: raise Http404( _("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name} ) def get_template_names(self): """Get the potential template names.""" names = [] path_items = self.kwargs[self.path_field].strip("/").split("/") while path_items: names.append("categories/%s.html" % "_".join(path_items)) path_items.pop() names.extend(super(CategoryDetailView, self).get_template_names()) return names class CategoryRelatedDetail(DetailView): """Detailed view for a category relation.""" path_field = "category_path" object_name_field = None def get_object(self, **kwargs): """Get the object to render.""" if self.path_field not in self.kwargs: raise AttributeError( "Category detail view %s must be called with " "a %s." % (self.__class__.__name__, self.path_field) ) queryset = super(CategoryRelatedDetail, self).get_queryset() try: category = get_category_for_path(self.kwargs[self.path_field]) except Category.DoesNotExist: raise Http404( _("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name} ) return queryset.get(category=category) def get_template_names(self): """Get all template names.""" names = [] opts = self.object._meta path_items = self.kwargs[self.path_field].strip("/").split("/") if self.object_name_field: path_items.append(getattr(self.object, self.object_name_field)) while path_items: names.append( "%s/category_%s_%s%s.html" % (opts.app_label, "_".join(path_items), opts.object_name.lower(), self.template_name_suffix) ) path_items.pop() names.append("%s/category_%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix)) names.extend(super(CategoryRelatedDetail, self).get_template_names()) return names class CategoryRelatedList(ListView): """List related category items.""" path_field = "category_path" def get_queryset(self): """Get the list of items.""" if self.path_field not in self.kwargs: raise AttributeError( "Category detail view %s must be called with " "a %s." % (self.__class__.__name__, self.path_field) ) queryset = super(CategoryRelatedList, self).get_queryset() category = get_category_for_path(self.kwargs[self.path_field]) return queryset.filter(category=category) def get_template_names(self): """Get the template names.""" names = [] if hasattr(self.object_list, "model"): opts = self.object_list.model._meta path_items = self.kwargs[self.path_field].strip("/").split("/") while path_items: names.append( "%s/category_%s_%s%s.html" % (opts.app_label, "_".join(path_items), opts.object_name.lower(), self.template_name_suffix) ) path_items.pop() names.append( "%s/category_%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix) ) names.extend(super(CategoryRelatedList, self).get_template_names()) return names