Hello! Welcome to the next phase of our journey into the Django REST Framework.
In the previous module, we mastered serialization. You learned how to define the "shape" of your API's data, converting complex model instances into JSON and validating incoming data. We built the blueprints for our data, but we haven't yet built the doors to let that data in and out.
This module, "API Views," is all about building those doors. We'll create the actual endpoints that clients will interact with. Today, we'll start with the most direct approach. Your learning outcome is to create simple function-based API views using the @api_view decorator. This is the foundational building block for creating API endpoints in DRF.
From Django Views to DRF API Views
Your existing knowledge of Python functions and HTTP requests provides a perfect starting point. In standard Django, you might write a view that returns JSON like this:
from django.http import JsonResponse
def my_plain_django_view(request):
data = {'message': 'Hello, world!'}
return JsonResponse(data)
This works, but it has limitations. You have to manually ensure your data is dictionary-formatted, you don't have an easy way to handle different content types (like XML), and managing HTTP status codes can be cumbersome.
DRF provides a set of powerful tools to upgrade these simple functions into robust API endpoints. The video below demonstrates this transition perfectly, starting with a plain Django view and then refactoring it using DRF's core components.
Creating API Views (Function Based API Views) | Learn Django REST Framework #2
Watch this video from Ssali Jonathan to see a practical comparison between a standard Django view and a DRF function-based API view.
First, watch from 00:22 to 08:56 to understand how a basic API can be built with just Django's JsonResponse and the challenges this presents. Then, watch from 10:37 to 16:36. This is the core of our lesson. Pay close attention to these four key elements he introduces: The @api_view decorator. The rest_framework.request.Request object. The rest_framework.response.Response object. The rest_framework.status module.
The Core Components of a DRF Function-Based View
As you saw in the video, DRF enhances Django's request-response cycle with a few key components. Let's formalize them.
1. The @api_view Decorator
This is the heart of function-based views in DRF. It's a Python decorator that you apply to your view function. Its main job is to wrap your function, providing it with DRF's functionality. Its most important argument is a list of HTTP methods that the view is allowed to handle.
@api_view(['GET', 'POST'])
If a request comes in with a method not in this list (e.g., PUT), DRF will automatically return a 405 Method Not Allowed response.
2. The Request Object
Your decorated view no longer receives Django's standard HttpRequest. Instead, it gets an instance of DRF's Request object. This object's most useful feature is request.data, which automatically parses the incoming request body (JSON, form data, etc.) and gives you a clean Python dictionary to work with. No more JSONParser().parse(request)!
3. The Response Object
Instead of JsonResponse, you now return a Response object. This is more powerful because it handles content negotiation. Based on the client's Accept header, DRF will automatically render your response data into the correct format (e.g., JSON, or even the browsable API page). You simply pass it Python data.
4. The status Module
While you can use numeric status codes like 200 or 404, DRF provides a status module with descriptive constants like status.HTTP_200_OK and status.HTTP_404_NOT_FOUND. Using these makes your code much more readable and less prone to errors.
Putting It All Together: A GET/POST View
Now, let's combine these concepts with what we learned about serializers to build a view that can both list existing items (GET) and create a new one (POST). The official DRF tutorial provides a clear and canonical example.
Tutorial 2: Requests and Responses
The DRF documentation provides the definitive guide. Let's examine their example for a view that handles multiple request methods.
Read the section titled 'Pulling it all together'. Focus on the snippet_list function. Notice the structure: The @api_view decorator lists both 'GET' and 'POST'. The code uses if request.method == 'GET': to separate the logic for each verb. The POST branch uses a SnippetSerializer to validate request.data and save a new object, just as we learned in the previous module.
This pattern is fundamental:
- Decorate the view with
@api_viewand list the allowed methods. - Use an
if/elifblock onrequest.methodto handle each case. - For
GETrequests, you typically query the database, serialize the data, and return it in aResponse. - For
POSTrequests, you passrequest.datato a serializer, validate it, save it, and return the new object's data with a201 CREATEDstatus.
This image provides a great side-by-side comparison. For today's lesson, focus on the right-hand side (Function-Based View).

Test your understanding!
Imagine you are building a simple "contact us" API endpoint. Write a function-based view named contact_form_view that only accepts POST requests.
The view should expect JSON data with two fields: email and message. It should return a JSON response confirming receipt, like this:{"status": "success", "message": "Thanks for your message, we'll contact you at your-email@example.com"}
Use DRF's Request, Response, and status components.
Show answer
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
@api_view(['POST'])
def contact_form_view(request):
# Access the parsed data from the request
email = request.data.get('email')
message = request.data.get('message')
# Basic check to ensure data is present
if not email or not message:
return Response(
{"error": "Both email and message are required."},
status=status.HTTP_400_BAD_REQUEST
)
# Construct the success response
response_data = {
"status": "success",
"message": f"Thanks for your message, we'll contact you at {email}"
}
return Response(response_data, status=status.HTTP_200_OK)
Note: In a real application, you would use a serializer to perform more robust validation on request.data.
Conclusion
Excellent work! You have now built your first proper API endpoint. You've bridged the gap between defining your data with serializers and exposing it to the world through views.
Key Takeaways:
- Function-based views are simple Python functions enhanced by the
@api_viewdecorator. - The
@api_viewdecorator is used to specify which HTTP methods a view can handle. - DRF's
Requestobject providesrequest.data, which contains the parsed body of an incoming request. - DRF's
Responseobject automatically handles content negotiation, rendering your data into the format requested by the client. - You can handle multiple HTTP methods in a single view by checking the
request.methodattribute.
Preview of the Next Lesson:
Function-based views are clear and explicit, which is great for simple cases. However, as your endpoints become more complex (handling GET, POST, PUT, DELETE for both lists and individual items), you might notice a lot of repeated code. In the next lesson, we will explore an alternative, class-based approach by learning how to build flexible API endpoints using the APIView class, which helps organize related logic and reduce boilerplate.