skip to content

Views & Responses

A Django view is a callable that takes an HttpRequest and returns an HttpResponse, with shortcuts, decorators and upload handling around it. Interviewers check you know that contract cold.

part ofDjangooverview, primer and where to startread it →
on this pageshow

explore

questions

24

In Django, what do require_POST, require_GET, require_safe and require_http_methods do, and what does a request with another method receive?

level: juniorimportance: must knowfreq 55%

answer

  1. django.views.decorators.http
  2. a whitelist of methods
  3. 405 with an Allow header
  4. safe means GET and HEAD

basics

~10 s

They are view decorators from django.views.decorators.http that let only the listed HTTP methods reach the view. Any other method gets an HttpResponseNotAllowed: a 405 response with an Allow header naming the permitted methods.

solid answer

~30 s

`require_http_methods(["GET", "POST"])` wraps a function view and compares `request.method` with the list before the view runs. `require_GET`, `require_POST` and `require_safe` are prebuilt instances for `["GET"]`, `["POST"]` and `["GET", "HEAD"]`. A non-matching request never reaches the view: the decorator returns `HttpResponseNotAllowed(methods)`, a **405** whose `Allow` header lists the permitted methods, and logs a "Method Not Allowed" warning through `django.request`. Method names must be uppercase, because `request.method` is. `require_safe` is preferred over `require_GET` for read-only views, since clients and monitors send `HEAD`. Since Django 5.0 the decorators also wrap `async def` views.

code

python · 17 lines
python
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_http_methods, require_POST

from .models import Product


@require_POST
def archive_product(request, pk):
    product = get_object_or_404(Product, pk=pk)
    product.archived = True
    product.save(update_fields=["archived"])
    return redirect("catalogue:index")


@require_http_methods(["GET", "HEAD", "POST"])  # uppercase, as request.method is
def product_review(request, pk):
    ...

go deeper

for a junior

Know the four decorators, that they live in django.views.decorators.http, and that a wrong method gets a 405 with an Allow header.

for a middle

Explain that the shortcuts are require_http_methods instances, why method names must be uppercase, and why require_safe beats require_GET.

for a senior

Use method guards on every state-changing function view and keep them distinct from CSRF, authentication and permission checks in reviews.

for a principal

Set a codebase convention for method restriction across function and class-based views so 405 behaviour is consistent for API clients.

## What the decorators are The module `django.views.decorators.http` provides a small family of guards for **function-based views**: | Decorator | Allowed methods | |---|---| | `require_http_methods(list)` | exactly the methods in `list` | | `require_GET` | `GET` | | `require_POST` | `POST` | | `require_safe` | `GET`, `HEAD` | The last three are not separate implementations; they are simply `require_http_methods([...])` called once at import time. That is why you write `@require_POST` without parentheses but `@require_http_methods(["GET", "POST"])` with them. ## What happens on a request The wrapper runs before your view body: 1. It reads `request.method` (always uppercase in Django). 2. If the method is in the list, it calls your view with the original arguments and returns whatever the view returns. 3. If not, it builds `HttpResponseNotAllowed(request_method_list)`, logs `"Method Not Allowed (%s): %s"` with the method and path through `log_response()`, and returns the response without calling the view. `HttpResponseNotAllowed` has status **405** and sets the `Allow` header to the comma-joined list, so a client can see what would have worked. ## Why use them - **Correctness.** A view that deletes a product or submits an order should not run on `GET`: links, prefetchers and crawlers issue `GET` freely. `@require_POST` makes that impossible instead of relying on the template to use a form. - **Clear errors.** A 405 with `Allow` is more honest than a view that silently treats a `PUT` as a `GET`. - **Less branching.** The view body no longer needs `if request.method != "POST": return ...` boilerplate. They are not a security control on their own. CSRF protection, authentication and permission checks are separate mechanisms, and `require_POST` does not replace any of them. ## Common mistakes - **Lowercase names.** `require_http_methods(["get", "post"])` rejects everything, because `request.method` is `"GET"`, not `"get"`. The docstring says method names should be uppercase. - **`require_GET` on read-only pages.** `HEAD` requests then get 405. Health checks and link checkers often use `HEAD`, so `require_safe` is usually the better choice. The view can build its normal response: HTTP forbids a body on a `HEAD` response, and Django's development server and test client drop it, as production servers do. - **Forgetting `OPTIONS`.** The decorators do not answer `OPTIONS` themselves; if a client needs it, include it in the list and handle it. - **Putting them straight on a class-based view method.** They expect `request` as the first argument, so on a method they receive `self` instead; class-based views need `method_decorator` or the view's own `http_method_names` attribute. ## Async views Before Django 5.0 these decorators always produced a synchronous wrapper, which hid an `async def` view from Django's coroutine detection. Since 5.0 each decorator checks `iscoroutinefunction(func)` and builds an `async` wrapper for coroutine views, so `@require_POST` on `async def submit(request)` works as expected. ## Stacking and testing Decorators run from the outside in: the one written on top sees the request first. A method guard is cheap, so it is usually placed near the top, where it rejects wrong methods before more expensive decorators run. The exception is anything that must also shape the 405 itself, such as a decorator that adds headers to every response, which then goes above it. The behaviour is easy to pin down in a test: - `response = client.get(url)` on a `@require_POST` view should give `response.status_code == 405`; - `response["Allow"]` should equal `"POST"`; - a `client.head(url)` against a `@require_safe` view should return 200. A test like this also catches the lowercase-list mistake immediately, because the allowed method starts failing too. ## Typical usage in a product catalogue ```python from django.views.decorators.http import require_POST, require_safe @require_safe def product_detail(request, slug): ... @require_POST def add_to_basket(request, product_id): ... ``` The read view accepts `GET` and `HEAD`; the state-changing view accepts only `POST` and answers anything else with a 405 listing `POST`.

  • Why is require_safe usually better than require_GET for a read-only page?
    `require_GET` rejects `HEAD` with a 405, but health checks, link checkers and some caches send `HEAD` to probe a URL. `require_safe` allows both `GET` and `HEAD`, and the view can return its normal response because no body is sent for `HEAD`, so the same view serves both.
  • What exactly does a client receive when it sends PUT to a view decorated with @require_POST?
    An `HttpResponseNotAllowed(["POST"])`: status 405 with an `Allow: POST` header and an empty body. The view function is never called, and Django logs a `Method Not Allowed (PUT): /path/` warning through the `django.request` logger.

saying these in an interview costs you the question

  • require_POST also performs CSRF validation
  • A disallowed method gets a 404 so the endpoint stays hidden
  • Method names in require_http_methods are case-insensitive
  • require_GET also allows HEAD requests
  • @require_POST can be put directly on a class-based view's post() method
open as a page

In Django, what must a function-based view accept and return, and what happens when it returns None?

level: juniorimportance: must knowfreq 58%

basics

~20 s

A Django function view takes the HttpRequest first, plus the URL's captured values as keyword arguments, and returns an HttpResponse or raises an exception Django maps to one. Returning None makes Django raise ValueError, surfacing as a 500.

open as a page

In Django, why is request.FILES empty after a user submits a form with a file input, and what must the request look like?

level: juniorimportance: must knowfreq 60%

basics

~10 s

Django fills request.FILES only for a POST request whose form used enctype="multipart/form-data" and actually sent a file. Without that enctype the browser sends only the file name as text, so FILES stays empty.

open as a page

In Django, why can't you put a function view decorator like require_POST directly on a class-based view method, and how does method_decorator fix it?

level: middleimportance: must knowfreq 52%

basics

~20 s

Function view decorators expect request as the first argument, but a method receives self first, so the decorator inspects the wrong object. method_decorator adapts the decorator to methods; apply it to dispatch() or to the class with name="dispatch".

open as a page

In Django, when does a plain function-based view beat a class-based view, and when does a generic class-based view earn its keep?

level: middleimportance: must knowfreq 72%

basics

~20 s

Function views win for bespoke flows, one-off endpoints and readability; generic class-based views win when a page matches a standard list, detail or form pattern or when many views share behaviour through mixins. Both satisfy the same request-in, response-out contract.

open as a page

In Django, why is request.POST empty when a client posts a JSON body, and how should the view read it?

level: middleimportance: must knowfreq 62%

basics

~10 s

request.POST only holds form data: Django parses it for POST requests with form-urlencoded or multipart bodies. A JSON body lands in request.body as bytes, which the view parses with json.loads() after checking request.content_type.

open as a page

In Django, why does JsonResponse raise a TypeError when you pass it a list, and what does safe=False change?

level: middleimportance: must knowfreq 58%

basics

~10 s

JsonResponse accepts only a dict by default (safe=True) because of an old JSON-array hijacking attack. Passing safe=False allows a list or any other JSON-serializable value as the top-level body.

open as a page

In Django, how does a QueryDict handle a repeated key such as ?status=paid&status=refunded, and why can't a view modify request.GET?

level: juniorimportance: should knowfreq 46%

basics

~20 s

A QueryDict stores a list per key: indexing and get() return the last value, getlist() returns all of them. request.GET and request.POST are immutable, so changes raise AttributeError; call copy() to get a mutable version.

open as a page

In Django, what do vary_on_headers() and vary_on_cookie() add to a response, and when must a view use them explicitly?

level: middleimportance: should knowfreq 30%

basics

~20 s

They add header names to the response's Vary header after the view runs, merging with any existing value. A view needs them when its output depends on a request header or cookie that no Django middleware already declares.

open as a page

In Django, what do the shortcuts render(), redirect() and get_object_or_404() do under the hood, and what can each accept?

level: middleimportance: should knowfreq 50%

basics

~20 s

render() runs render_to_string() with the request and wraps it in an HttpResponse; redirect() resolves a model, URL name or URL into a 302; get_object_or_404() calls .get() on a Model, Manager or QuerySet and turns DoesNotExist into Http404.

open as a page

In Django, how do request.headers and request.META differ, and how do you read a webhook's X-Signature header from each?

level: middleimportance: should knowfreq 40%

basics

~10 s

request.META is the raw server environment: headers appear as HTTP_X_SIGNATURE alongside variables like REMOTE_ADDR. request.headers is a case-insensitive mapping of HTTP headers only, read as request.headers['X-Signature'].

open as a page

In Django, what does FileResponse do for a file download that a plain HttpResponse or StreamingHttpResponse does not?

level: middleimportance: should knowfreq 38%

basics

~20 s

FileResponse streams a binary file-like object in blocks, closes it afterwards, and sets Content-Length, Content-Type and Content-Disposition from the file where it can. as_attachment=True and filename control whether the browser downloads it and under what name.

open as a page

In Django, how does returning a TemplateResponse differ from returning the HttpResponse built by render(), and why does its deferred rendering matter?

level: middleimportance: should knowfreq 34%

basics

~10 s

render() produces an HttpResponse whose body is already rendered, while TemplateResponse keeps the template and context and renders only after the view returns. Decorators, middleware and tests can change or inspect them before rendering.

open as a page

In Django, why should a view write an uploaded file with UploadedFile.chunks() rather than read(), and how do chunks behave for in-memory uploads?

level: middleimportance: should knowfreq 42%

basics

~20 s

UploadedFile.read() loads the whole file into memory, while chunks() yields it in 64 KiB pieces, so writing a large upload chunk by chunk keeps memory flat. For an InMemoryUploadedFile, chunks() yields the whole content once, since it is already in memory.

open as a page

In Django, how do MemoryFileUploadHandler and TemporaryFileUploadHandler decide where an upload is kept, and what does FILE_UPLOAD_MAX_MEMORY_SIZE control?

level: middleimportance: should knowfreq 45%

basics

~10 s

FILE_UPLOAD_HANDLERS runs MemoryFileUploadHandler first: if the whole request is at most FILE_UPLOAD_MAX_MEMORY_SIZE (2.5 MB), files become InMemoryUploadedFile objects; otherwise TemporaryFileUploadHandler writes TemporaryUploadedFile objects to disk. The setting is a threshold, not a size limit.

open as a page

A Django product-detail view is expensive to render; how would you use the condition() decorator so unchanged pages return 304 without running the view, and what pitfalls apply?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Decorate the view with condition(etag_func=..., last_modified_func=...) using cheap callbacks that take the view's arguments; on a matching conditional GET Django returns 304 without calling the view. Use one condition() rather than stacked etag() and last_modified(), and keep vary or cache_control decorators above it.

open as a page

A Django checkout view for library loans has grown to 150 lines of availability rules, fee checks and emails; how would you make it thin?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Keep only HTTP work in the view: read input, check access, call one domain function, choose the response. Move single-object rules into model or QuerySet methods and multi-model operations into a plain service function that takes no request and raises domain exceptions.

open as a page

Behind a TLS-terminating proxy that strips a /payments prefix, why does Django's request.build_absolute_uri() produce an http:// callback URL without the prefix?

level: seniorimportance: should knowfreq 30%

basics

~20 s

build_absolute_uri() joins request.scheme, get_host() and the path as the app server saw them. The proxy spoke plain HTTP, so is_secure() is False, and without SCRIPT_NAME or FORCE_SCRIPT_NAME Django knows no prefix, so reverse() and request.path omit /payments.

open as a page

A Django view that exports warehouse inventory as CSV runs out of memory and times out on large warehouses; how would you stream it with StreamingHttpResponse, and what do you give up?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Return a StreamingHttpResponse over a generator that yields CSV rows built with csv.writer and QuerySet.iterator(), so memory stays flat and bytes flow early. You lose Content-Length, ETags, caching and the chance to change the status mid-stream.

open as a page

Users upload scanned invoices of up to 20 MB to a Django view; which DATA_UPLOAD_* and FILE_UPLOAD_* settings apply, and how do you actually cap the file size?

level: seniorimportance: should knowfreq 45%

basics

~20 s

No Django setting caps a single file's size: DATA_UPLOAD_MAX_MEMORY_SIZE excludes file parts, and FILE_UPLOAD_MAX_MEMORY_SIZE only picks memory or disk. Cap it at the web server, with a custom upload handler, or by validating size after receipt.

open as a page

In Django 5.2 and later, what does preserve_request=True change on HttpResponseRedirect and HttpResponsePermanentRedirect, and when do you need it?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

preserve_request=True switches HttpResponseRedirect from 302 to 307 and HttpResponsePermanentRedirect from 301 to 308, telling the client to repeat the same method and body. Use it when redirecting a non-GET request, such as a moved POST endpoint.

open as a page

In Django 5.0 and later, what changed for built-in view decorators on async def views, and which trap with condition() callbacks remains?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

Since Django 5.0 the built-in view decorators detect async def views and return an async wrapper, so the view stays a coroutine function. condition() still calls its etag and last-modified callbacks synchronously, so they cannot be async or use the sync ORM.

open as a page

In Django, how do you write a custom FileUploadHandler and install it for one view, and why must that happen before request.POST is accessed?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Subclass FileUploadHandler, implement receive_data_chunk() and file_complete(), and insert an instance into request.upload_handlers at the start of the view. Parsing request.POST or request.FILES freezes the list, and CsrfViewMiddleware reads POST first, so the view needs csrf_exempt plus an inner csrf_protect.

open as a page