CRUDView Reference¶
- class neapolitan.views.CRUDView(**kwargs)¶
CRUDView is Neapolitan’s core. It provides the standard list, detail, create, edit, and delete views for a model, as well as the hooks you need to be able to customise any part of that.
Request Handlers¶
The core of a class-based view are the request handlers — methods that convert an HTTP request into an HTTP response. The request handlers are the essence of the Django view.
Neapolitan’s CRUDView
provides handlers the standard list, detail, create,
edit, and delete views for a model.
List and Detail Views¶
- CRUDView.list(request, *args, **kwargs)¶
GET handler for the list view.
def list(self, request, *args, **kwargs): """GET handler for the list view.""" queryset = self.get_queryset() filterset = self.get_filterset(queryset) if filterset is not None: queryset = filterset.qs if not self.allow_empty and not queryset.exists(): raise Http404 paginate_by = self.get_paginate_by() if paginate_by is None: # Unpaginated response self.object_list = queryset context = self.get_context_data( page_obj=None, is_paginated=False, paginator=None, filterset=filterset, ) else: # Paginated response page = self.paginate_queryset(queryset, paginate_by) self.object_list = page.object_list context = self.get_context_data( page_obj=page, is_paginated=page.has_other_pages(), paginator=page.paginator, filterset=filterset, ) return self.render_to_response(context)
- CRUDView.detail(request, *args, **kwargs)¶
GET handler for the detail view.
def detail(self, request, *args, **kwargs): """GET handler for the detail view.""" self.object = self.get_object() context = self.get_context_data() return self.render_to_response(context)
Create and Update Views¶
- CRUDView.show_form(request, *args, **kwargs)¶
GET handler for the create and update form views.
def show_form(self, request, *args, **kwargs): """GET handler for the create and update form views.""" if self.role is Role.UPDATE: self.object = self.get_object() form = self.get_form(instance=self.object) context = self.get_context_data(form=form) return self.render_to_response(context)
- CRUDView.process_form(request, *args, **kwargs)¶
POST handler for the create and update form views.
def process_form(self, request, *args, **kwargs): """POST handler for the create and update form views.""" if self.role is Role.UPDATE: self.object = self.get_object() form = self.get_form( data=request.POST, files=request.FILES, instance=self.object, ) if form.is_valid(): return self.form_valid(form) return self.form_invalid(form)
Delete View¶
- CRUDView.confirm_delete(request, *args, **kwargs)¶
GET handler for the delete confirmation view.
def confirm_delete(self, request, *args, **kwargs): """GET handler for the delete confirmation view.""" self.object = self.get_object() context = self.get_context_data() return self.render_to_response(context)
- CRUDView.process_deletion(request, *args, **kwargs)¶
POST handler for the delete confirmation view.
def process_deletion(self, request, *args, **kwargs): """POST handler for the delete confirmation view.""" self.object = self.get_object() self.object.delete() return HttpResponseRedirect(self.get_success_url())
QuerySet and object lookup¶
- CRUDView.get_queryset()¶
Returns the base queryset for the view.
Either used as a list of objects to display, or as the queryset from which to perform the individual object lookup.
def get_queryset(self): """ Returns the base queryset for the view. Either used as a list of objects to display, or as the queryset from which to perform the individual object lookup. """ if self.queryset is not None: return self.queryset._clone() if self.model is not None: return self.model._default_manager.all() msg = ( "'%s' must either define 'queryset' or 'model', or override " + "'get_queryset()'" ) raise ImproperlyConfigured(msg % self.__class__.__name__)
- CRUDView.get_object()¶
Returns the object the view is displaying.
def get_object(self): """ Returns the object the view is displaying. """ queryset = self.get_queryset() lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field try: lookup = {self.lookup_field: self.kwargs[lookup_url_kwarg]} except KeyError: msg = "Lookup field '%s' was not provided in view kwargs to '%s'" raise ImproperlyConfigured( msg % (lookup_url_kwarg, self.__class__.__name__) ) return get_object_or_404(queryset, **lookup)
Form handling¶
- CRUDView.get_form_class()¶
Returns the form class to use in this view.
def get_form_class(self): """ Returns the form class to use in this view. """ if self.form_class is not None: return self.form_class if self.model is not None and self.fields is not None: return model_forms.modelform_factory(self.model, fields=self.fields) msg = ( "'%s' must either define 'form_class' or both 'model' and " "'fields', or override 'get_form_class()'" ) raise ImproperlyConfigured(msg % self.__class__.__name__)
- CRUDView.get_form(data=None, files=None, **kwargs)¶
Returns a form instance.
def get_form(self, data=None, files=None, **kwargs): """ Returns a form instance. """ cls = self.get_form_class() return cls(data=data, files=files, **kwargs)
- CRUDView.form_valid(form)¶
def form_valid(self, form): self.object = form.save() return HttpResponseRedirect(self.get_success_url())
- CRUDView.form_invalid(form)¶
def form_invalid(self, form): context = self.get_context_data(form=form) return self.render_to_response(context)
- CRUDView.get_success_url()¶
def get_success_url(self): assert self.model is not None, ( "'%s' must define 'model' or override 'get_success_url()'" % self.__class__.__name__ ) if self.role is Role.DELETE: success_url = reverse(f"{self.url_base}-list") else: success_url = reverse( f"{self.url_base}-detail", kwargs={"pk": self.object.pk} ) return success_url
Pagination and filtering¶
- CRUDView.get_paginate_by()¶
Returns the size of pages to use with pagination.
def get_paginate_by(self): """ Returns the size of pages to use with pagination. """ return self.paginate_by
- CRUDView.get_paginator(queryset, page_size)¶
Returns a paginator instance.
def get_paginator(self, queryset, page_size): """ Returns a paginator instance. """ return Paginator(queryset, page_size)
- CRUDView.paginate_queryset(queryset, page_size)¶
Paginates a queryset, and returns a page object.
def paginate_queryset(self, queryset, page_size): """ Paginates a queryset, and returns a page object. """ paginator = self.get_paginator(queryset, page_size) page_kwarg = self.kwargs.get(self.page_kwarg) page_query_param = self.request.GET.get(self.page_kwarg) page_number = page_kwarg or page_query_param or 1 try: page_number = int(page_number) except ValueError: if page_number == "last": page_number = paginator.num_pages else: msg = "Page is not 'last', nor can it be converted to an int." raise Http404(_(msg)) try: return paginator.page(page_number) except InvalidPage as exc: msg = "Invalid page (%s): %s" raise Http404(_(msg) % (page_number, str(exc)))
- CRUDView.get_filterset(queryset=None)¶
def get_filterset(self, queryset=None): filterset_class = getattr(self, "filterset_class", None) filterset_fields = getattr(self, "filterset_fields", None) if filterset_class is None and filterset_fields: filterset_class = filterset_factory(self.model, fields=filterset_fields) if filterset_class is None: return None return filterset_class( self.request.GET, queryset=queryset, request=self.request, )
Response rendering¶
- CRUDView.get_context_object_name(is_list=False)¶
Returns a descriptive name to use in the context in addition to the default ‘object’/’object_list’.
def get_context_object_name(self, is_list=False): """ Returns a descriptive name to use in the context in addition to the default 'object'/'object_list'. """ if self.context_object_name is not None: return self.context_object_name elif self.model is not None: fmt = "%s_list" if is_list else "%s" return fmt % self.model._meta.object_name.lower() return None
- CRUDView.get_context_data(**kwargs)¶
def get_context_data(self, **kwargs): kwargs["view"] = self kwargs["object_verbose_name"] = self.model._meta.verbose_name kwargs["object_verbose_name_plural"] = self.model._meta.verbose_name_plural kwargs["create_view_url"] = reverse(f"{self.url_base}-create") if getattr(self, "object", None) is not None: kwargs["object"] = self.object context_object_name = self.get_context_object_name() if context_object_name: kwargs[context_object_name] = self.object if getattr(self, "object_list", None) is not None: kwargs["object_list"] = self.object_list context_object_name = self.get_context_object_name(is_list=True) if context_object_name: kwargs[context_object_name] = self.object_list return kwargs
- CRUDView.get_template_names()¶
Returns a list of template names to use when rendering the response.
If .template_name is not specified, uses the “{app_label}/{model_name}{template_name_suffix}.html” model template pattern, with the fallback to the “neapolitan/object{template_name_suffix}.html” default templates.
def get_template_names(self): """ Returns a list of template names to use when rendering the response. If `.template_name` is not specified, uses the "{app_label}/{model_name}{template_name_suffix}.html" model template pattern, with the fallback to the "neapolitan/object{template_name_suffix}.html" default templates. """ if self.template_name is not None: return [self.template_name] if self.model is not None and self.template_name_suffix is not None: return [ f"{self.model._meta.app_label}/" f"{self.model._meta.object_name.lower()}" f"{self.template_name_suffix}.html", f"neapolitan/object{self.template_name_suffix}.html", ] msg = ( "'%s' must either define 'template_name' or 'model' and " "'template_name_suffix', or override 'get_template_names()'" ) raise ImproperlyConfigured(msg % self.__class__.__name__)
- CRUDView.render_to_response(context)¶
Given a context dictionary, returns an HTTP response.
def render_to_response(self, context): """ Given a context dictionary, returns an HTTP response. """ return TemplateResponse( request=self.request, template=self.get_template_names(), context=context )
URLs and view callables¶
- classmethod CRUDView.get_urls(roles=None)¶
Classmethod to generate URL patterns for the view.
This is the usual entry-point for routing all CRUD URLs in a single pass:
urlpatterns = [ *BookmarkView.get_urls(), ]
Optionally, you may provide an appropriate set of roles in order to limit the handlers exposed:
urlpatterns = [ *BookmarkView.get_urls(roles={Role.LIST, Role.DETAIL}), ]
Subclasses may wish to override
get_urls()
in order to encapsulate such logic.@classonlymethod def get_urls(cls, roles=None): """Classmethod to generate URL patterns for the view.""" if roles is None: roles = iter(Role) return [role.get_url(cls) for role in roles]
- classmethod CRUDView.as_view(role: Role, **initkwargs)¶
Main entry point for a request-response process.
This is the lower-level method used to manually route individual URLs.
It’s extends the Django View.as_view() method, and should be passed a an appropriate
Role
giving the handlers to be exposed:path( "bookmarks/", BookmarkCRUDView.as_view(role=Role.LIST), name="bookmark-list", )