Skip to content

Generic Class-Based Views

Buraq provides Django-style generic views for the most common patterns.

Display a list of objects:

from buraq.views.generic import ListView
class PostListView(ListView):
model = Post
template_name = "posts/list.html"
paginate_by = 10 # enables pagination
ordering = ["-created_at"]
# Template context: object_list, post_list, paginator, page_obj, is_paginated

Custom queryset:

class PublishedPostListView(ListView):
model = Post
template_name = "posts/list.html"
async def get_queryset(self):
return await Post.objects.filter(is_published=True).order_by("-created_at")

Display a single object by pk or slug:

class PostDetailView(DetailView):
model = Post
template_name = "posts/detail.html"
pk_url_kwarg = "pk" # URL param name (default: "pk")
# Template context: object, post (model name lowercased)

Display a form and create a new object on POST:

class PostCreateView(CreateView):
model = Post
form_class = PostForm
template_name = "posts/form.html"
success_url = "/posts/"

Display a pre-filled form and update an existing object on POST:

class PostUpdateView(UpdateView):
model = Post
form_class = PostForm
template_name = "posts/form.html"
success_url = "/posts/"

Confirm and delete an object:

class PostDeleteView(DeleteView):
model = Post
template_name = "posts/confirm_delete.html"
success_url = "/posts/"

Render a template with optional extra context:

from buraq.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
extra_context = {"title": "About Us"}
from buraq.views.generic import RedirectView
urlpatterns = [
get("/old-url/", RedirectView.as_view(url="/new-url/", permanent=True)),
]

By default a redirect responds with 302 Found (or 301 Moved Permanently), which instructs the browser to repeat the request as GET regardless of the original method. Set preserve_request = True to return 307 Temporary Redirect (or 308 Permanent Redirect) instead, preserving the original HTTP method:

urlpatterns = [
post(
"/submit/",
RedirectView.as_view(
url="/new-submit/",
preserve_request=True, # 307 — client repeats the POST
),
),
]
permanent preserve_request Status
False (default) False (default) 302 Found
True False 301 Moved Permanently
False True 307 Temporary Redirect
True True 308 Permanent Redirect

put(), patch(), and delete() handlers are also supported so that non-GET verbs receive the correct redirect response.

Handle a form’s GET/POST cycle without tying it to a model. See FormView for the full reference.

from buraq.views.generic import FormView
class ContactView(FormView):
template_name = "contact.html"
form_class = ContactForm
success_url = "/thanks/"
async def form_valid(self, request, form):
await send_contact_email(form.cleaned_data)
return redirect(self.success_url)

Filter objects by date field. All archive views share date_field (default "created_at") and allow_future.

from buraq.views.generic import (
YearArchiveView, MonthArchiveView, WeekArchiveView,
DayArchiveView, TodayArchiveView, ArchiveIndexView, DateDetailView,
)
class PostYearView(YearArchiveView):
model = Post
date_field = "published_on"
class PostMonthView(MonthArchiveView):
model = Post
date_field = "published_on"
urlpatterns = [
get("/<int:year>", PostYearView.as_view()),
get("/<int:year>/<int:month>", PostMonthView.as_view()),
]

List objects for an ISO week number.

class PostWeekView(WeekArchiveView):
model = Post
date_field = "published_on"
# URL: /2024/week/12
get("/<int:year>/week/<int:week>", PostWeekView.as_view())
class PostDayView(DayArchiveView):
model = Post
date_field = "published_on"
# URL: /2024/3/15
get("/<int:year>/<int:month>/<int:day>", PostDayView.as_view())

No URL parameters needed — always uses today’s date.

class TodayPostsView(TodayArchiveView):
model = Post
date_field = "published_on"
get("/today", TodayPostsView.as_view())

Top-level archive — provides a list of all distinct years in date_list.

class PostArchiveView(ArchiveIndexView):
model = Post
date_field = "published_on"
template_name = "posts/archive.html"
# context: date_list (list of year dates)
get("/archive", PostArchiveView.as_view())

Retrieve a single object by year/month/day + pk or slug.

class PostDateDetailView(DateDetailView):
model = Post
date_field = "published_on"
template_name = "posts/detail.html"
get("/<int:year>/<int:month>/<int:day>/<int:pk>", PostDateDetailView.as_view())

Add access control to any CBV by mixing in before the view class. See Auth Mixins for the full reference.

from buraq.views.mixins import LoginRequiredMixin, PermissionRequiredMixin
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
form_class = PostForm
success_url = "/posts/"
class PostPublishView(PermissionRequiredMixin, UpdateView):
model = Post
permission_required = "blog.publish_post"
success_url = "/posts/"

class PostDetailView(DetailView):
model = Post
template_name = "posts/detail.html"
async def get_context_data(self, **kwargs):
ctx = await super().get_context_data(**kwargs)
ctx["related"] = await Post.objects.filter(is_published=True).limit(3)
return ctx
urlpatterns = [
get("/", PostListView.as_view(), name="post_list"),
get("/<str:slug>", PostDetailView.as_view(), name="post_detail"),
get("/new", PostCreateView.as_view(), name="post_create"),
post("/new", PostCreateView.as_view()),
get("/<int:pk>/edit", PostUpdateView.as_view(), name="post_update"),
post("/<int:pk>/edit", PostUpdateView.as_view()),
get("/<int:pk>/delete", PostDeleteView.as_view(), name="post_delete"),
post("/<int:pk>/delete", PostDeleteView.as_view()),
]