Typorithm · medium

A DRF request is more than a Django request

pythondjangodrfindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

A DRF request is more than a Django request

0%

Concept and contract

Follow parsing, dispatch and rendering through an owned API request.

Django REST Framework adds an API-oriented request and response boundary to Django. request.data represents parsed content, while Response carries data that a renderer will turn into a representation. A dictionary returned by an ordinary Python function does not exercise those HTTP decisions.

The fixture wraps one POST operation with api_view and uses JSONParser explicitly. APIRequestFactory constructs an in-process Django request, and the decorated view performs DRF dispatch. No listening socket, real account or external feed is involved. The shared lab bootstrap configures only an owned in-memory project.

Mechanism and worked example

The Content-Type header selects how the body is parsed.

The Content-Type header selects how the body is parsed. A valid JSON mapping reaches the view, malformed JSON becomes a parse failure, and an unsupported media type is rejected when request.data is accessed. The helper renders the response and compares the decoded public JSON.

Rendering matters: response.data is a Python representation, whereas a client receives encoded bytes and a status. The test reports both. The endpoint deliberately echoes a tiny fixture title; later serializer lessons add the validation boundary that this introductory route does not yet claim.

Complete worked example

python

from drf_lab import setup
setup()

import json
from rest_framework.decorators import api_view, parser_classes
from rest_framework.parsers import JSONParser, FormParser
from rest_framework.response import Response
from rest_framework.test import APIRequestFactory

@api_view(["POST"])
@parser_classes([JSONParser])
def create(request):
    return Response({"title": request.data.get("title", "")}, status=201)

def inspect_example(payload):
    raw = payload.get("raw", json.dumps(payload.get("body", {})))
    request = APIRequestFactory().post("/roles/", data=raw,
                                     content_type=payload.get("type", "application/json"))
    response = create(request)
    body = {"error": response.data["detail"].code} if response.status_code >= 400 else response.data
    response.render()
    return {"status": response.status_code, "body": body}
Reasoning
The Content-Type header selects how the body is parsed. A valid JSON mapping reaches the view, malformed JSON becomes a parse failure, and an unsupported media type is rejected when request.data is accessed. The helper renders the response and compares the decoded public JSON. Rendering matters: response.data is a Python representation, whereas a client receives encoded bytes and a status. The test reports both. The endpoint deliberately echoes a tiny fixture title; later serializer lessons add the validation boundary that this introductory route does not yet claim.

Focused decision: context is in the complete example

python

@api_view(["POST"])
@parser_classes([JSONParser])
def create(request):
    return Response({"title": request.data.get("title", "")}, status=201)
Focused decision
Reconstruct this focused decision after the warm-up. The complete imports, model or application, and test client remain in the worked example. Typing records practice; only the downloadable computer lab executes Python.

Counterexamples and common mistakes

The counterexample replaces JSONParser with FormParser.

The counterexample replaces JSONParser with FormParser. The same JSON request is no longer accepted, even though its body still looks readable to a developer. Parsing is a configured contract, not a guess based on punctuation inside a payload.

Do not read a body twice through unrelated APIs or turn every parse failure into an empty successful object. Distinguish malformed content from a media type the endpoint does not support. Avoid returning raw exception traces or submitted sensitive values as a convenient debugging response.

Controlled mistake: observe the actual wrong behavior

python

from drf_lab import setup
setup()

import json
from rest_framework.decorators import api_view, parser_classes
from rest_framework.parsers import JSONParser, FormParser
from rest_framework.response import Response
from rest_framework.test import APIRequestFactory

@api_view(["POST"])
@parser_classes([FormParser])
def create(request):
    return Response({"title": request.data.get("title", "")}, status=201)

def inspect_example(payload):
    raw = payload.get("raw", json.dumps(payload.get("body", {})))
    request = APIRequestFactory().post("/roles/", data=raw,
                                     content_type=payload.get("type", "application/json"))
    response = create(request)
    body = {"error": response.data["detail"].code} if response.status_code >= 400 else response.data
    response.render()
    return {"status": response.status_code, "body": body}
Reasoning
The counterexample replaces JSONParser with FormParser. The same JSON request is no longer accepted, even though its body still looks readable to a developer. Parsing is a configured contract, not a guess based on punctuation inside a payload. Do not read a body twice through unrelated APIs or turn every parse failure into an empty successful object. Distinguish malformed content from a media type the endpoint does not support. Avoid returning raw exception traces or submitted sensitive values as a convenient debugging response.

Independent practice

Change the successful creation response from 201 to an explicitly documented 202 acceptance response.

Change the successful creation response from 201 to an explicitly documented 202 acceptance response. Keep malformed JSON and unsupported text rejected. This is a small status-contract exercise; explain whether deferred work in a real product would justify acceptance rather than completed creation.

Run the valid JSON case, malformed JSON and text/plain. Predict each status first. Do not change the inspection helper to rewrite statuses, and do not add a permissive parser merely to hide an invalid request. The working baseline lets you focus on one declared decision.

Step 1: Use status 202 for the accepted response without widening parsers or hiding malformed input. Step 2: Retain the working imports and application structure. Change the stated policy, run the supplied practice cases, then explain the newly accepted or rejected boundary.

Practice starter: solve before reading the solution

python

from drf_lab import setup
setup()

import json
from rest_framework.decorators import api_view, parser_classes
from rest_framework.parsers import JSONParser, FormParser
from rest_framework.response import Response
from rest_framework.test import APIRequestFactory

@api_view(["POST"])
@parser_classes([JSONParser])
def create(request):
    return Response({"title": request.data.get("title", "")}, status=201)

def inspect_example(payload):
    raw = payload.get("raw", json.dumps(payload.get("body", {})))
    request = APIRequestFactory().post("/roles/", data=raw,
                                     content_type=payload.get("type", "application/json"))
    response = create(request)
    body = {"error": response.data["detail"].code} if response.status_code >= 400 else response.data
    response.render()
    return {"status": response.status_code, "body": body}

# Independent task: Use status 202 for the accepted response without widening parsers or hiding malformed input.
Reasoning
Use status 202 for the accepted response without widening parsers or hiding malformed input.
  • Hint 1: Find the one field or branch that owns this policy.
  • Hint 2: Compare the first changed acceptance case with the retained baseline.

Solution, tests and trade-offs

The solution changes the Response status while preserving the parser and error behavior.

The solution changes the Response status while preserving the parser and error behavior. It does not pretend that returning 202 creates a queue or makes processing durable. The observed body remains the same accepted fixture title.

A production API also needs authentication, authorization, input limits and appropriate operation semantics. Current DRF patches harden parsing and content negotiation, but upgrading a package does not replace a bounded request policy. Keep framework versions and real runtime tests together when reviewing that boundary.

Practice solution and acceptance checks

python

from drf_lab import setup
setup()

import json
from rest_framework.decorators import api_view, parser_classes
from rest_framework.parsers import JSONParser, FormParser
from rest_framework.response import Response
from rest_framework.test import APIRequestFactory

@api_view(["POST"])
@parser_classes([JSONParser])
def create(request):
    return Response({"title": request.data.get("title", "")}, status=202)

def inspect_example(payload):
    raw = payload.get("raw", json.dumps(payload.get("body", {})))
    request = APIRequestFactory().post("/roles/", data=raw,
                                     content_type=payload.get("type", "application/json"))
    response = create(request)
    body = {"error": response.data["detail"].code} if response.status_code >= 400 else response.data
    response.render()
    return {"status": response.status_code, "body": body}
Reasoning
The solution changes the Response status while preserving the parser and error behavior. It does not pretend that returning 202 creates a queue or makes processing durable. The observed body remains the same accepted fixture title. A production API also needs authentication, authorization, input limits and appropriate operation semantics. Current DRF patches harden parsing and content negotiation, but upgrading a package does not replace a bounded request policy. Keep framework versions and real runtime tests together when reviewing that boundary.

Interview reasoning and next step

Explain the difference between request.data, Response.data and the rendered JSON body.

Explain the difference between request.data, Response.data and the rendered JSON body. Then show the tests that distinguish parse failure from unsupported media type. These observations are more useful than memorizing decorator names without knowing which work they trigger.

Next, declare serializer fields. A successfully parsed JSON object can still contain missing, malformed or forbidden values. Parsing establishes a representation; validation decides whether its values satisfy the application contract.

Optional stretch — after the core task works: Add one independently reasoned boundary case and explain its expected value before running it.

Computer practice: https://applydjinn.com/typorithm/drf-lab-20261105.zip. Install pinned dependencies in an isolated Python environment. Run verify.py, then change the working exercise baseline and run --practice drf-requests. The real DRF clients, serializers, schemas and SQLite transactions use owned fixtures only. No production database, feed, account or paid service is used. This lab executes Python; the PC typing trainer and native reading progress do not execute it.

Follow parsing, dispatch and rendering through an owned API request.