# Function-based — explicit, reads top to bottom
def order_list(request):
orders = Order.objects.select_related("customer").all()
return render(request, "orders/list.html", {"orders": orders})
def order_create(request):
if request.method == "POST":
form = OrderForm(request.POST)
if form.is_valid():
form.save()
return redirect("order-list")
else:
form = OrderForm()
return render(request, "orders/form.html", {"form": form})
# Class-based — less code for standard CRUD patterns
class OrderListView(ListView):
model = Order
template_name = "orders/list.html"
paginate_by = 20
def get_queryset(self):
return Order.objects.select_related("customer").filter(status="pending")
class OrderCreateView(CreateView):
model = Order
form_class = OrderForm
success_url = reverse_lazy("order-list")
Class-based views trade explicitness for convention — a ListView gives you pagination, queryset filtering hooks, and template context for free, at the cost of needing to know Django's conventions (which method to override, what context variable name it uses by default) to see what's actually happening. A practical rule of thumb: standard CRUD (list, detail, create, update, delete) is usually less code as a class-based generic view; a view with genuinely custom logic that doesn't map cleanly onto "list of X" or "form for X" is often clearer as a plain function.
get_context_data — the standard CBV extension pointclass OrderListView(ListView):
model = Order
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) # always call super() first
context["pending_count"] = Order.objects.filter(status="pending").count()
return context
Forgetting super().get_context_data(**kwargs) is a common CBV mistake — it's what actually populates the base context (object_list, pagination info) that the template expects; overwriting it entirely breaks the built-in template rendering.