Function-Based Views
The simplest way to write views. An FBV is just an async def that takes a request and returns a response.
Basic view
Section titled “Basic view”from buraq.shortcuts import render
async def post_list(request): posts = await Post.objects.filter(is_published=True) return await render(request, "posts/list.html", {"posts": posts})Path parameters
Section titled “Path parameters”Type-annotate path params — Buraq injects them from the URL:
async def post_detail(request, slug: str): post = await get_object_or_404(Post, slug=slug) return await render(request, "posts/detail.html", {"post": post})
async def post_by_id(request, pk: int): post = await get_object_or_404(Post, id=pk) return await render(request, "posts/detail.html", {"post": post})Handling methods
Section titled “Handling methods”async def post_form(request, pk: int = None): if request.method == "POST": form = PostForm(data=dict(await request.form())) if await form.is_valid(): await form.save() return redirect("/posts/") else: instance = await Post.objects.get(id=pk) if pk else None form = PostForm(instance=instance)
return await render(request, "posts/form.html", {"form": form})Request object
Section titled “Request object”async def my_view(request): # Method request.method # "GET", "POST", etc.
# Path & URL request.url # full URL object request.url.path # "/posts/1/edit"
# Query string request.query_params.get("page", 1) request.query_params.getlist("tags")
# Headers request.headers.get("content-type")
# Body body = await request.body() # raw bytes json = await request.json() # parsed JSON form = dict(await request.form()) # form data
# Auth request.user # current user (or None)
# Session request.session # dict-like session request.session["key"] = "value"
# Client request.client.host # client IPReturning responses
Section titled “Returning responses”from buraq.shortcuts import render, redirectfrom starlette.responses import JSONResponse, Response
async def my_view(request): # HTML response return await render(request, "template.html", {"key": "value"})
# Redirect return redirect("/posts/") return redirect("/posts/", permanent=True) # 301
# JSON return JSONResponse({"key": "value"}) return JSONResponse({"error": "not found"}, status_code=404)
# Plain text return Response("OK", media_type="text/plain")Shortcuts
Section titled “Shortcuts”from buraq.shortcuts import get_object_or_404
# Raises HTTP 404 if not foundpost = await get_object_or_404(Post, slug=slug)post = await get_object_or_404(Post, id=pk, is_published=True)Decorators
Section titled “Decorators”Decorators are importable two ways. buraq.decorators is a single flat
namespace holding all of them, and there are per-concern modules mirroring
Django so existing imports keep working:
| Concern | Django-compatible path |
|---|---|
| Auth | buraq.contrib.auth.decorators |
| HTTP methods | buraq.views.decorators.http |
| Caching | buraq.views.decorators.cache |
| CSRF | buraq.views.decorators.csrf |
| Vary headers | buraq.views.decorators.vary |
| CSP | buraq.views.decorators.csp |
Both styles return the same objects — pick whichever suits your project.
from buraq.decorators import login_required, permission_required
@login_requiredasync def create_post(request): ...
@login_required(redirect_url="/auth/login")async def edit_post(request, pk: int): ...
@permission_required("posts.publish")async def publish_post(request, pk: int): ...csrf_exempt
Section titled “csrf_exempt”from buraq.decorators import csrf_exempt
@csrf_exemptasync def webhook(request): """Third-party webhooks don't send CSRF tokens.""" payload = await request.json() ...csrf_exempt marks the view with _csrf_exempt = True. The CSRF middleware skips validation for such views.
never_cache
Section titled “never_cache”from buraq.decorators import never_cache
@never_cacheasync def user_dashboard(request): ...Adds Cache-Control: no-store, Pragma: no-cache, and Expires: 0 headers to every response — prevents browsers and proxies from caching sensitive pages.