{"id":20345801,"url":"https://github.com/izzypt/drf-guide","last_synced_at":"2026-06-01T03:32:34.385Z","repository":{"id":151595583,"uuid":"609262604","full_name":"izzypt/DRF-Guide","owner":"izzypt","description":"Guide to learn and remember DRF concepts and technicals","archived":false,"fork":false,"pushed_at":"2023-03-08T01:09:14.000Z","size":245,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-08T13:43:40.721Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/izzypt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-03T18:07:48.000Z","updated_at":"2023-03-03T23:43:24.000Z","dependencies_parsed_at":"2023-04-18T21:46:43.417Z","dependency_job_id":null,"html_url":"https://github.com/izzypt/DRF-Guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/izzypt/DRF-Guide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzypt%2FDRF-Guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzypt%2FDRF-Guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzypt%2FDRF-Guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzypt%2FDRF-Guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/izzypt","download_url":"https://codeload.github.com/izzypt/DRF-Guide/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzypt%2FDRF-Guide/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33759178,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T22:09:49.584Z","updated_at":"2026-06-01T03:32:34.353Z","avatar_url":"https://github.com/izzypt.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DRF-Guide\n\nThis repo will cover the following topics and concepts about Django and , specifically, DRF:\n\n- Serializers\n  - serializers.Serializer\n  - serializers.ModelSerializer\n  - serializers.HyperlinkedModelSerializer\n- Function Based View\n  - @api_view()\n- Class Based View\n  - APIView\n  - Generics Views\n  - Mixins\n  - Concrete View Classes\n- Viewsets and Routers\n- Permissions\n  - IsAuthenticated\n  - IsAdminUser\n  - IsAuthenticatedOrReadOnly\n  - CustomPermissions\n- Authentication\n  - Basic Authentication\n  - Token Authentication\n  - JWT Authentication\n- Throttling\n  - AnonRateThrottle\n  - UserRateThrottle\n  - ScopedRateThrottle\n  - Custom Throttle\n- Filtering\n  - Filter\n  - Search\n  - Ordering\n- Pagination\n  - Page Number\n  - Limit Offset\n  - Cursor\n- Automated API Testing\n\n# Index By Topic\n\n- [Installation](#install) \n- [DRF Imtroduction](#drf_intro)\n- [Creating JSON Response with vanilla Django](#response_vanilla_django)\n\n# What we will build ?\n\nIn order to cover all those topics , we will build an API that will serve movie data , like an IMDB or WatchMate clone.\n\n\u003ca id=\"install\"\u003e\u003c/a\u003e\n# Installation\n\nIn order to use DRF , first we need to start a Django project. We can do it very simply, by following those steps:\n\n1) Create a project folder for your app, let's call it ```DRF-Guide```\n2) Create a virtual environment inside it , in order to keep our project packages isolated : ```python -m venv venv```\n3) Activate virtual env by running ```.\\venv\\Scripts\\activate``` or ```source venv/bin/activate``` if you are on Mac.\n4) If the virtual env is installed and we run ```pip freeze``` we will be able to see that there are no packages installed in our virtualenv. So we will install everything from fresh.\n5) So with a blank project and environment , let's install Django:  ```pip install Django```. \n\n# Starting project\n\n1) Let's start our project inside the ```DRF-Guide```, running the command ```django-admin startproject IMDB```\n2) Let's create a new app inside our project, to have our project's code dividide into modules : ```python manage.py startapp watchlist```\n3) Add the newly create app in our ```settings.py``` file, in the ```INSTALLED_APPS```.\n\n![image](https://user-images.githubusercontent.com/73948790/222804249-0373b994-5872-4241-8318-92a4a938fa1b.png)\n\n4) Let's apply our migrations in order to create the first tables in our project DB: ```python manage.py makemigrations``` and then ```python manage.py migrate```\n5) Next , let's create our superuser in order to access the admin panel: ```python manage.py createsuperuser```.\n6) You check everything works fine by acessing the /admin endpoint on your running server.\n\n# Models and Migrations\n\n1) Let's start by creating a simple model in our ```models.py```, inside our ```watchlist``` app:\n\n```\nclass Movie(models.Model):\n    name = models.CharField(max_length=100)\n    description = models.CharField(max_length=255)\n    active = models.BooleanField(default=True)\n\n    def __str__(self):\n        return self.name\n```\n2) Run the migrations for the newly created model : ```python manage.py makemigrations``` and then ```python manage.py migrate```\n3) In order to access our newly created model from the admin panel, we need to register it in the ```admin.py``` file\n```\nfrom django.contrib import admin\nfrom .models import Movie\n\n# Register your models here.\nadmin.site.register(Movie)\n```\n4) We are now able to add new movies from the admin panel.\n\n\u003ca id=\"response_vanilla_django\n# Creating JSON Response with vanilla Django.\n\nIn order to understand why DRF is so usefull , let's first start by serving our movie data with the built-in logic that Django provides. Later , we can see how DRF will build and improve on that-\n\n1) We will create a view in our ```views.py``` to return a list of the movies we have in our DB , this is what it looks like with \"vanilla\" Django:\n```\nfrom .models import Movie\nfrom django.http import JsonResponse\n\ndef movie_list(request):\n    # The next line will fetch all the movies from DB and store them in a queryset\n    movies = Movie.objects.all()\n    \n    # Because we cannot send the response as a querset, we need to transform the queryset.\n    data = {\n        # Using .values() we get the values each object of the queryset as a dictionarie.\n        # And then wrapping the queryset in a list to transform it into a list of dictionaries.\n        'movies': list(movies.values())\n    }\n    \n    #Finally we return our data dictionary as JSON object.\n    return JsonResponse(data, status=200)\n```\n\n2) Now , let's see what it would look like with returning only a single movie of our choice:\n```\ndef movie_detail(request, movie_id):\n    # Filter the movie by movie id\n    movie = Movie.objects.get(pk=movie_id)\n\n    # Convert the movie object into a dictionary from queryset format\n    data = {\n        'name': movie.name,\n        'description': movie.description,\n        'active': movie.active\n    }\n    \n    #Return our data dictionary as JSON object.\n    return JsonResponse(data, status=200)\n```\n\nSo, as you can see, most of our work is to transform the queryset we receive from Django ORM to a JSON object that the user will receive ! What if there was an easier way to do this ? There is... that's why we will use DRF instead.\n\u003ca id=\"drf_intro\"\u003e\u003c/a\u003e\n# DRF Introduction\n\nLet's implement DRF into our project.\n\n1) Start by installing it : ```pip install djangorestframework```.\n2) Add ```rest_framework``` to your ```INSTALLED_APPS``` in ```settings.py```.\n\n2 important concepts to keep about DRF:\n- Serialization\n- Deserialization\n\n\u003e Serialization is the process through which we transfor complex data types (like Model Objects) into Python Native DataType ( like dictionary) and then into JSON. Basically is an easier way of converting QuerySets , like I showed above, into JSON that we can send to the client.\n\n\n\u003e Deserialization is just the opposite. Getting information from the user and then transforming into complex data type like Model Objects.\n\n# Serializers - GET Request\n\nLet's rewrite the views that we have wrote in the previous chapter with Vanilla Django, but this time using serializers.\n\nThere are a couple of different types of serializers , but we will start using the most basic : ```serializers.Serializer```.\n\nOn a file called ```serializers.py```, we will add :\n\n```\nfrom rest_framework import serializers\n\nclass MovieSerializer(serializers.Serializer):\n    id = serializers.IntegerField(read_only=True)\n    name = serializers.CharField()\n    active = serializers.BooleanField()\n```\n\nNow that we have a serializer and we can automatically convert our Model's complex data type into JSON. Let's see how our views will change:\n\n```\nfrom watchlist.models import Movie\nfrom watchlist.api.serializers import MovieSerializer\nfrom rest_framework.response import Response\nfrom rest_framework.decorators import api_view # \u003c- This is the decorator that we need to use in function based views, it takes a list of acceptable methods for that view.\n\n@api_view(['GET'])\ndef movie_list(request):\n    movies = Movie.objects.all()\n    serializer = MovieSerializer(movies, many=True)\n    return Response(serializer.data)\n\n@api_view(['GET'])\ndef movie_detail(request, movie_id):\n    movie = Movie.objects.get(pk=movie_id)\n    serializer = MovieSerializer(movie)\n    return Response(serializer.data)\n```\n\n# Serializers - POST Request\n\nIf we receive a POST request, our serializers needs to handle those methods by  implementing the function \"create(validated_data)\".\n\nWe also need to add the acceptable method to our ```@api_view()``` list.\n\nSo , if we what we are dealing with , is a POST request, we would have to make the following changes to the serializer:\n\n```\nfrom rest_framework import serializers\nfrom watchlist.models import Movie\n\nclass MovieSerializer(serializers.Serializer):\n    id = serializers.IntegerField(read_only=True)\n    name = serializers.CharField()\n    description = serializers.CharField()\n    active = serializers.BooleanField()\n\n    def create(self, validated_data):\n        \"\"\"Create a new movie\"\"\"\n        return Movie.objects.create(**validated_data)\n```\n\nAnd we also would have to change the view logic to something like this :\n\n```\nfrom watchlist.models import Movie\nfrom watchlist.api.serializers import MovieSerializer\nfrom rest_framework.response import Response\nfrom rest_framework.decorators import api_view\nfrom rest_framework import status\n\n@api_view(['GET', 'POST'])\ndef movie_list(request):\n    if request.method == 'GET':\n        movies = Movie.objects.all()\n        serializer = MovieSerializer(movies, many=True)\n        return Response(serializer.data)\n    if request.method == 'POST':\n        serializer = MovieSerializer(data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_201_CREATED)\n        else:\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n```\n\n# Serializers - PUT Request\n\nIf it is a PUT request, we need to do the same thing.\n\nFirst of all, add the PUT request to our list of aceptable methods in out ```@api_view()``` .\n\nSecond , we need to add the \"update(instance, validated_data)\" method to our ```MovieSerializer```.\n\nThis is our modified serializer :\n```\nfrom rest_framework import serializers\nfrom watchlist.models import Movie\n\nclass MovieSerializer(serializers.Serializer):\n    id = serializers.IntegerField(read_only=True)\n    name = serializers.CharField()\n    description = serializers.CharField()\n    active = serializers.BooleanField()\n\n    def create(self, validated_data):\n        \"\"\"Create a new movie\"\"\"\n        return Movie.objects.create(**validated_data)\n        \n    def update(self, instance, validated_data):\n        # instance carries the old values\n        # validated_data carries the new values\n        instance.name = validated_data.get('name', instance.name)\n        instance.description = validated_data.get('description', instance.description)\n        instance.active = validated_data.get('active', instance.active)\n        instance.save()\n        return instance\n```\n\nAnd this is our modified view :\n\n```\n@api_view(['GET', 'PUT'])\ndef movie_detail(request, movie_id):\n    if request.method == 'GET':\n        movie = Movie.objects.get(pk=movie_id)\n        serializer = MovieSerializer(movie)\n        return Response(serializer.data)\n    if request.method == 'PUT':\n        movie = Movie.objects.get(pk=movie_id)\n        serializer = MovieSerializer(movie, data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_200_OK)\n        else:\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n```\n\n# Serializers - DELETE Request\n\nTo handle the DELETE method, take a look at the changes.\n\nNo changes required in the serializer, only change the view to :\n\n```\n@api_view(['GET', 'PUT', 'DELETE'])\ndef movie_detail(request, movie_id):\n    if request.method == 'GET':\n        movie = Movie.objects.get(pk=movie_id)\n        serializer = MovieSerializer(movie)\n        return Response(serializer.data)\n    if request.method == 'PUT':\n        movie = Movie.objects.get(pk=movie_id)\n        serializer = MovieSerializer(movie, data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_200_OK)\n        else:\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n    if request.method == 'DELETE':\n        movie = Movie.objects.get(pk=movie_id)\n        movie.delete()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n```\n# Status codes\n\nYou might have noticed in the examples above, that I have imported ```status``` from ```rest_framework```.\n\n```status``` simply provides an easier and more readable way to write http codes. If you want to look more into in , follow \u003ca href=\"https://www.django-rest-framework.org/api-guide/status-codes/\"\u003ethis link\u003c/a\u003e.\n\nIt provides all the available codes and what they mean.\n\n# APIView Class\n\nDRF has 2 types of views :\n\n- Function Based View (we used above with ```@api_view()```)\n- Class Based Views (there are different types of class Based Views)\n\nWe have already seen a function base view approach, now we'll start with a class based view: ```APIView``` , importing it from ```rest_framework.views```.\n\nWith ```APIView``` , the incoming request is dispatched to an appropriate handler method such as .get() or .post().\n\nRefactoring our previous code :\n\n```\nfrom watchlist.models import Movie\nfrom watchlist.api.serializers import MovieSerializer\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\nfrom rest_framework import status\n\nclass MovieList(APIView):\n    def get(self, request, format=None):\n        movies = Movie.objects.all()\n        serializer = MovieSerializer(movies, many=True)\n        return Response(serializer.data)\n\n    \n    def post(self, request, format=None):\n        serializer = MovieSerializer(data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_201_CREATED)\n        else:\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n        \nclass MovieDetail(APIView):\n    def get(self, request, pk):\n        movie = Movie.objects.get(pk=pk)\n        serializer = MovieSerializer(movie)\n        return Response(serializer.data)\n    \n    def put(self, request, pk):\n        movie = Movie.objects.get(pk=pk)\n        serializer = MovieSerializer(movie, data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_200_OK)\n        else:\n            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n    \n    def delete(self, request, pk):\n        movie = Movie.objects.get(pk=pk)\n        movie.delete()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n```\n\nSo , as you can see we reused the entire logic, we just save some time but not haveing to identify the request method or the acceptable methods, since the class automatically does it for us.\n\n# Serializers Validation\n\nSerializers validation is used to validate the data submitted in a request. The purpose of a validator is to ensure that the data submitted in a request is valid and meets certain requirements or constraints before it is processed further.\n\nThere are 3 different types of validation we can add on our serializers:\n- Field level validation\n- Object level validation\n- Validators\n\nLet's take a look at each one...\n\n## Field Level Validation\n\nField level validation consists on checking only a particular field of our serializers. According to the documentation:\n\n\u003e You can specify custom field-level validation by adding validate_\u003cfield_name\u003e methods to your Serializer subclass.\n\nDocs example :\n\n```\nfrom rest_framework import serializers\n\nclass BlogPostSerializer(serializers.Serializer):\n    title = serializers.CharField(max_length=100)\n    content = serializers.CharField()\n\n    def validate_title(self, value):\n        \"\"\"\n        Check that the blog post is about Django.\n        \"\"\"\n        if 'django' not in value.lower():\n            raise serializers.ValidationError(\"Blog post is not about Django\")\n        return value\n```\n\nSo, if we wanted to add some level validation to our ```MovieSerializer```, let's say the name , we would do something like this :\n\n```\nclass MovieSerializer(serializers.Serializer):\n    id = serializers.IntegerField(read_only=True)\n    name = serializers.CharField()\n    description = serializers.CharField()\n    active = serializers.BooleanField()\n    \n    def validate_name(self, value):\n        if len(value) \u003c 3:\n            raise serializers.ValidationError(\"Name must be at least 3 characters long\")\n        return value\n```\n\n## Object Level Validation\n\nWith object level validation , we are able to check multiple fields at once. According to the documentation :\n\n\u003e To do any other validation that requires access to multiple fields, add a method called ```validate()``` to your Serializer subclass. This method takes a single argument, which is a dictionary of field values. It should raise a serializers.ValidationError if necessary, or just return the validated values. For example:\n\n```\nfrom rest_framework import serializers\n\nclass EventSerializer(serializers.Serializer):\n    description = serializers.CharField(max_length=100)\n    start = serializers.DateTimeField()\n    finish = serializers.DateTimeField()\n\n    def validate(self, data):\n        \"\"\"\n        Check that start is before finish.\n        \"\"\"\n        if data['start'] \u003e data['finish']:\n            raise serializers.ValidationError(\"finish must occur after start\")\n        return data\n```\n\nSo, if we wanted to add some Object level Validation to our ```MovieSerializer```, it would look something like this:\n\n```\nclass MovieSerializer(serializers.Serializer):\n    id = serializers.IntegerField(read_only=True)\n    name = serializers.CharField()\n    description = serializers.CharField()\n    active = serializers.BooleanField()\n    \n    def validate(self, data):\n        if data[\"name\"] == data[\"description\"]:\n            raise serializers.ValidationError(\"Title and description should be different\")\n        else:\n            return data\n```\n\n## Validators\n\nOfficial documentation says about validators :\n\n\u003e Individual fields on a serializer can include validators, by declaring them on the field instance, for example:\n```\ndef multiple_of_ten(value):\n    if value % 10 != 0:\n        raise serializers.ValidationError('Not a multiple of ten')\n\nclass GameRecord(serializers.Serializer):\n    score = IntegerField(validators=[multiple_of_ten])\n    ...\n\n```\n\n# Serializer fields Core Arguments\n\nEach field in a serializer can take several core arguments that further define or constraint the data that the field can receive. For example :\n\n```\nfrom rest_framework import serializers\nfrom myapp.models import MyModel\n\nclass MySerializer(serializers.Serializer):\n    my_field = serializers.CharField(\n        source='my_model_field', \n        max_length=100, \n        required=True, \n        help_text='Enter a value for my_field'\n    )\n\n    def create(self, validated_data):\n        return MyModel.objects.create(**validated_data)\n\n    def update(self, instance, validated_data):\n        instance.my_model_field = validated_data.get('my_field', instance.my_model_field)\n        instance.save()\n        return instance\n```\n\nSome of the most common core arguments for a serializer field , include :\n\n- source \n\u003e Specifies the attribute or method on the model instance that should be used to populate the field. For example, source='my_model_field' would map the field to the my_model_field attribute on the model.\n\n- read_only \n\u003e Specifies whether the field is read-only or not. If set to True, the field cannot be updated via the serializer.\n\n- write_only \n\u003e Specifies whether the field is write-only or not. If set to True, the field cannot be retrieved via the serializer.\n\n- required \n\u003e Specifies whether the field is required or not. If set to True, the field must be included in the input data.\n\n- allow_null \n\u003e Specifies whether the field can be set to None or not.\n\n- default \n\u003e Specifies the default value for the field if no value is provided in the input data.\n\n- validators \n\u003e Specifies a list of validator functions to apply to the field value.\n\n- error_messages \n\u003e Specifies a dictionary of error messages to use for different types of validation errors.\n\n- help_text\n\u003e  Specifies help text for the field that can be displayed in the API documentation.\n\n- label\n\u003e  Specifies a human-readable label for the field that can be displayed in the API documentation.\n\n\u003ca href=\"https://www.django-rest-framework.org/api-guide/fields/#core-arguments\"\u003eClick here to check the official docs on serializer field core arguments.\u003c/a\u003e\n\n# Model Serializer\n\nAs stated above, there are other ways to write Serializers. One of them is extending ```serializer.ModelSerializer```.\n\n```ModelSerializer``` is just like a regular ```Serializer```, except some work is already done for us, like :\n- A set of default fields are automatically populated\n- A set of default validators are automatically populated\n- Default ```create()``` and ```update()``` implementations are already provided.\n\nIf the ModelSerializer doesn't generate the set of fields we need, we should declare the field explicitly on the class, or simply use a ```Serializer``` class.\n\n\nSo, if we want to implement ```ModelSerializer``` into our pre-existing code, we refactor the ```MovieSerializer``` to :\n\n```\nfrom rest_framework import serializers\nfrom watchlist.models import Movie\n\nclass MovieSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Movie\n        fields = \"__all__\"\n        \n    # Field level validation\n    def validate_name(self, value):\n        if len(value) \u003c 3:\n            raise serializers.ValidationError(\"Name must be at least 3 characters long\")\n        else:\n            return value\n\n    # Object level validation\n    def validate(self, data):\n        if data[\"name\"] == data[\"description\"]:\n            raise serializers.ValidationError(\"Title and description should be different\")\n        else:\n            return data\n```\n\nSo, as you can see , we didn't have to specify ```create()``` or ```update()``` and we didn't need to specify the serializer fields, since ModelSerializer is getting those from the model.\n\nIn order to change the fields that you want your serializer to send back , there are 2 ways you can do it:\n\n1) By specifying which fields you want in a field list , something like : ```fields = ['id', 'name', 'description']```\n2) By defining the fields you want to exclude , adding this field to your class ```Meta``` : ```exclude = [\"active\"]```\n\n# Custom Serializer Fields\n\nIf we want to send back some field in our serializer that requires some type of calculation or that does not exist in our models.\n\nLet's say we wanted to send back the length of the movie name. We can achieve that result by declaring a new type of field, a ```serializers.MethodField```.\n\nThis is the official docs description:\n\n\u003e SerializerMethodField is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object.\n\nExample : \n\n```\nclass MovieSerializer(serializers.ModelSerializer):\n    len_name = serializers.SerializerMethodField()\n\n    class Meta:\n        model = Movie\n        exclude = [\"active\"]\n    \n    def get_len_name(self, obj):\n        return len(obj.name)\n```\n\nSo, basically there are 2 important steps to create a ```SerializerMethodField()```\n- Declare the field and the name as we did with ```len_name = serializers.SerializerMethodField()``` inside your serializer.\n- Define the mthod that will return the value for that field. It is important to name the method following the style : ```get_\u003cmethod_field_name\u003e```\n\n# Updating Models\n\nIn order to progress with our IMDB clone and touch on other topics , we need to change our models and create new ones. Let's work with those models from now on:\n\n```\nclass StreamPlatorm(models.Model):\n    name = models.CharField(max_length=100)\n    about = models.CharField(max_length=100)\n    website = models.URLField(max_length=100)\n    \n    def __str__(self):\n        return self.name\n        \nclass WatchList(models.Model):\n    title = models.CharField(max_length=150)\n    storyline = models.CharField(max_length=150)\n    active = models.BooleanField(default=True)\n    created = models.DateTimeField(auto_now_add=True)\n    \n    def __str__(self):\n        return self.title\n```\n\nI have also refactored the code to change from \"Movie\" to watchlist and added views and serializers for the new model ```StreamPlatform```\n\n# Django Models Relationships\n\nIt is important to know the most basic relationships we can establish between Django models. \n\n- One to One RelationShip\n- One to Many\n- Many to Many\n\n\u003ca href=\"https://docs.djangoproject.com/en/3.1/topics/db/examples/\"\u003eClick here to check Django documentation\u003c/a\u003e\n\nLet's also add a relationship to our current ```WatchList``` model :\n\n```\n\nclass WatchList(models.Model):\n    (...)\n    platform = models.ForeignKey(StreamPlatorm, on_delete=models.CASCADE, related_name=\"watchlist\")\n    (...)\n        \n```\n\nAbove we create a ```many-to-one``` relationship between WatchList and StreamPlatform, which means that each WatchList instance can be associated with only one StreamPlatform instance, but each StreamPlatform instance can be associated with many WatchList instances.\n\nNow an explanation on what each parameter of the field means:\n\n- ```StreamPlatform```: This is the model that the platform field is referring to.\n\n- ```on_delete=models.CASCADE```: This parameter specifies what should happen to the WatchList instances if the StreamPlatform instance they are associated with is deleted. In this case, CASCADE means that if a StreamPlatform instance is deleted, all WatchList instances associated with it will also be deleted.\n\n- ```related_name=\"watchlist\"```: This parameter specifies the name of the reverse relation from StreamPlatform to WatchList. This means that each StreamPlatform instance will have a reverse relation to all associated WatchList instances, and the name of this relation will be \"watchlist\".\n\n\u003eEach WatchList instance represents a single movie or TV show, and each WatchList instance is associated with one StreamPlatform instance that represents the platform where that movie or TV show can be streamed. Multiple WatchList instances can be associated with the same StreamPlatform instance, because multiple movies or TV shows can be available on the same streaming platform.\n\n# Nested Serializers\n\nOur goal for this section is to have a field in our serializers  ```StreamPlatformSerializer``` which displays all of the linked ```watchlist```'s for that platform.\n\nWe will do this by adding the following line to our ```StreamPlatformSerializer```:\n```\nclass StreamPlatformSerializer(serializers.ModelSerializer):\n    watchlist = WatchListSerializer(many=True, read_only=True)\n    \n    class Meta:\n        model = StreamPlatorm\n        fields = \"__all__\"\n```\n\n\u003e Important note to keep is that the field name matches the ```related_name``` we defined in the ```WatchList``` model, because we are doing a reverse relation from StreamPlatform to WatchList.\n\nIn the current start, our ```watchlist``` field returns the entire watchlist object, just like it would return a normal a watchlist instance from the ```WatchListSerializer```. But what if we want to customize that return of ```watchlist``` ?\n\nWell, instead of using ```WatchListSerializer``` as a return to our watchlist, we can use Relational fields, which I will discuss in the next section...\n\n# Serializer Relations\n\n\u003ci\u003eDocumentation official description:\u003c/i\u003e\n\n\u003e Relational fields are used to represent model relationships. They can be applied to ForeignKey, ManyToManyField and OneToOneField relationships, as well as to reverse relationships, and custom relationships such as GenericForeignKey.\n\n### \u003cins\u003eInspecting Relationships\u003c/ins\u003e\n\nWhen using the ModelSerializer class, serializer fields and relationships will be automatically generated for you. Inspecting these automatically generated fields can be a useful tool for determining how to customize the relationship style.\n\nTo do so, open the Django shell, using python manage.py shell, then import the serializer class, instantiate it, and print the object representation…\n\n```\n\u003e\u003e\u003e from myapp.serializers import AccountSerializer\n\u003e\u003e\u003e serializer = AccountSerializer()\n\u003e\u003e\u003e print(repr(serializer))\nAccountSerializer():\n    id = IntegerField(label='ID', read_only=True)\n    name = CharField(allow_blank=True, max_length=100, required=False)\n    owner = PrimaryKeyRelatedField(queryset=User.objects.all())\n```\n### \u003cins\u003eExample Models\u003c/ins\u003e\n  Let's take the models and examples from the official docs :\n\n```\nclass Album(models.Model):\n    album_name = models.CharField(max_length=100)\n    artist = models.CharField(max_length=100)\n\nclass Track(models.Model):\n    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)\n    order = models.IntegerField()\n    title = models.CharField(max_length=100)\n    duration = models.IntegerField()\n\n    class Meta:\n        unique_together = ['album', 'order']\n        ordering = ['order']\n        \n    def __str__(self):\n        return '%d: %s' % (self.order, self.title)\n```\n\nThe ```unique_together``` field in Django's Meta class specifies that certain fields in a model should be unique together, meaning that the combination of values for those fields must be unique in the database.\n\nAdditionally, the ordering field in the Meta class specifies the default ordering for the Track model. In this case, it is set to order the tracks by their order field.\n\n### \u003cins\u003eStringRelatedField\u003c/ins\u003e\n\n\u003e ```StringRelatedField``` may be used to represent the target of the relationship using its __str__ method.\n\nFor example, the following serializer:\n\n```\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = serializers.StringRelatedField(many=True)\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nWould serialize to the following representation:\n\n```\n{\n    'album_name': 'Things We Lost In The Fire',\n    'artist': 'Low',\n    'tracks': [\n        '1: Sunflower',\n        '2: Whitetail',\n        '3: Dinosaur Act',\n        ...\n    ]\n}\n```\n\nThis field is read only.\n\n\u003cb\u003eArguments\u003c/b\u003e:\n\n- many - If applied to a to-many relationship, you should set this argument to True.\n\n### \u003cins\u003ePrimaryKeyRelatedField\u003c/ins\u003e\n\n\u003e ```PrimaryKeyRelatedField``` may be used to represent the target of the relationship using its primary key.\n\nFor example, the following serializer:\n\n```\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nWould serialize to a representation like this:\n\n```\n{\n    'album_name': 'Undun',\n    'artist': 'The Roots',\n    'tracks': [\n        89,\n        90,\n        91,\n        ...\n    ]\n}\n```\n\nBy default this field is read-write, although you can change this behavior using the read_only flag.\n\n\u003cb\u003eArguments\u003c/b\u003e:\n\n- queryset - The queryset used for model instance lookups when validating the field input. Relationships must either set a queryset explicitly, or set read_only=True.\n- many - If applied to a to-many relationship, you should set this argument to True.\n- allow_null - If set to True, the field will accept values of None or the empty string for nullable relationships. Defaults to False.\n- pk_field - Set to a field to control serialization/deserialization of the primary key's value. For example, pk_field=UUIDField(format='hex') would serialize a UUID primary key into its compact hex representation.\n\n\n### \u003cins\u003eSlugRelatedField\u003c/ins\u003e\n\n\u003e ```SlugRelatedField``` may be used to represent the target of the relationship using a field on the target.\n\nFor example, the following serializer:\n\n```\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = serializers.SlugRelatedField(\n        many=True,\n        read_only=True,\n        slug_field='title'\n     )\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nWould serialize to a representation like this:\n\n```\n{\n    'album_name': 'Dear John',\n    'artist': 'Loney Dear',\n    'tracks': [\n        'Airport Surroundings',\n        'Everything Turns to You',\n        'I Was Only Going Out',\n        ...\n    ]\n}\n```\n\nBy default this field is read-write, although you can change this behavior using the read_only flag.\n\nWhen using SlugRelatedField as a read-write field, you will normally want to ensure that the slug field corresponds to a model field with unique=True.\n\n\u003cb\u003eArguments\u003c/b\u003e:\n\n- slug_field - The field on the target that should be used to represent it. This should be a field that uniquely identifies any given instance. For example, username. required\n- queryset - The queryset used for model instance lookups when validating the field input. Relationships must either set a queryset explicitly, or set read_only=True.\n- many - If applied to a to-many relationship, you should set this argument to True.\n- allow_null - If set to True, the field will accept values of None or the empty string for nullable relationships. Defaults to False.\n\n### \u003cins\u003e Custom relational fields \u003c/ins\u003e\n\n- To implement a custom relational field, you should override RelatedField, and implement the ```.to_representation(self, value)``` method. \n  - This method takes the target of the field as the value argument, and should return the representation that should be used to serialize the target. The value argument will typically be a model instance.\n  \n- If you want to implement a read-write relational field, you must also implement the ```.to_internal_value(self, data)``` method.\n\nTo provide a dynamic queryset based on the context, you can also override ```.get_queryset(self)``` instead of specifying .queryset on the class or when initializing the field.\n\nFor example:\n\nWe could define a relational field to serialize a track to a custom string representation, using its ordering, title, and duration:\n\n```\nimport time\n\nclass TrackListingField(serializers.RelatedField):\n    def to_representation(self, value):\n        duration = time.strftime('%M:%S', time.gmtime(value.duration))\n        return 'Track %d: %s (%s)' % (value.order, value.name, duration)\n\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = TrackListingField(many=True)\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nThis custom field would then serialize to the following representation:\n\n```\n{\n    'album_name': 'Sometimes I Wish We Were an Eagle',\n    'artist': 'Bill Callahan',\n    'tracks': [\n        'Track 1: Jim Cain (04:39)',\n        'Track 2: Eid Ma Clack Shaw (04:19)',\n        'Track 3: The Wind and the Dove (04:34)',\n        ...\n    ]\n}\n```\n\n# The N+1 Problem in the context of Django and DRF\n\n\u003eThe N+1 Problem is a common performance issue that arises when using an ORM (Object-Relational Mapping) tool like Django's ORM. It occurs when you make N database queries to fetch N objects, where each query fetches data for a single object. This can lead to a significant increase in the number of queries executed by your application and can slow down its performance.\n\n - The N+1 Problem often arises when serializing data that involves relationships between models. \n   - For example, let's say you have two models in your Django application, \"Author\" and \"Book\", with a one-to-many relationship between them. If you want to serialize a list of books with their associated authors, you might end up with an N+1 Problem if you use a serializer that queries the author for each book in the list. This is because the serializer would have to execute one query to fetch the list of books, and then N additional queries to fetch the associated authors.\n\n- To avoid the N+1 Problem in this scenario, you can use the ```select_related``` and ```prefetch_related``` methods in your queryset to tell Django to fetch the related objects in a more efficient way. \n  - ```select_related``` is used to fetch data from a related model in a single query, while ```prefetch_related``` is used to fetch data from a related model in a separate query and cache the results. By using these methods, you can reduce the number of queries executed by your application and improve its performance.\n\n# Identifying the N+1 Problem\n\n- One way is to use Django Debug Toolbar :\n  - It's a third-party package that provides a set of panels displaying various debug information about the current request/response cycle, including the number of database queries executed and the time spent on each query. The SQL panel in the toolbar is especially useful for identifying N+1 problems, as it shows the SQL queries executed by Django's ORM and can help you pinpoint which queries are causing performance issues.\n\n- Another way is to use the ```django.db.connection.queries``` attribute :\n  - This attribute is a list of all the SQL queries executed during the request/response cycle, and you can print it out in your view or middleware to see the queries being executed. By inspecting this list, you can look for patterns that indicate an N+1 problem, such as multiple queries being executed for the same model or related model.\n\n# Generic Views - \u003ca href=\"https://www.django-rest-framework.org/api-guide/generic-views/#genericapiview\"\u003eDocs\u003c/a\u003e\n\nDocumentation description :\n\n\u003e The generic views provided by REST framework allow you to quickly build API views that map closely to your database models.\n\n\u003e If the generic views don't suit the needs of your API, you can drop down to using the regular APIView class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views.\n\n### \u003cins\u003eGenericAPIView\u003c/ins\u003e\n\nBase class for all other generic views.\n\nThis class extends REST framework's APIView class, adding commonly required behavior for standard list and detail views.\n\nEach of the concrete generic views provided is built by combining GenericAPIView, with one or more mixin classes.\n\n```\nclass GenericAPIView(views.APIView):\n # You'll need to either set these attributes,\n    # or override `get_queryset()`/`get_serializer_class()`.\n    # If you are overriding a view method, it is important that you call\n    # `get_queryset()` instead of accessing the `queryset` property directly,\n    # as `queryset` will get evaluated only once, and those results are cached\n    # for all subsequent requests.\n    queryset = None\n    serializer_class = None\n\n    # If you want to use object lookups other than pk, set 'lookup_field'.\n    # For more complex lookup requirements override `get_object()`.\n    lookup_field = 'pk'\n    lookup_url_kwarg = None\n\n    # The filter backend classes to use for queryset filtering\n    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS\n\n    # The style to use for queryset pagination.\n    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS\n```\n\n\n### Attributes\n\nBasic settings:\n\n- ```queryset```\n- ```serializer_class``` \n- ```lookup_field```\n- ```lookup_url_kwarg```\n\nPagination:\n\n- ```pagination_class``` : The pagination class that should be used when paginating list results. Defaults to the same value as the DEFAULT_PAGINATION_CLASS setting, which is 'rest_framework.pagination.PageNumberPagination'. Setting pagination_class=None will disable pagination on this view. \n\nFiltering:\n\n- ```filter_backends``` : A list of filter backend classes that should be used for filtering the queryset. Defaults to the same value as the DEFAULT_FILTER_BACKENDS setting.\n\n### Methods \n\n- ```get_queryset(self)```\n\n\u003e Returns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the queryset attribute. \n\nThis method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.\n\nFor example:\n\n```\ndef get_queryset(self):\n    user = self.request.user\n    return user.accounts.all()\n```\n- ```get_object(self)```\n\n\u003e Returns an object instance that should be used for detail views. Defaults to using the lookup_field parameter to filter the base queryset.\n\nMay be overridden to provide more complex behavior, such as object lookups based on more than one URL kwarg.\n\nFor example:\n\n```\ndef get_object(self):\n    queryset = self.get_queryset()\n    filter = {}\n    for field in self.multiple_lookup_fields:\n        filter[field] = self.kwargs[field]\n\n    obj = get_object_or_404(queryset, **filter)\n    self.check_object_permissions(self.request, obj)\n    return obj\n```\n\n\n# Mixins - \u003ca href=\"https://www.django-rest-framework.org/api-guide/generic-views/#mixins\"\u003eDocs\u003c/a\u003e\n\n\u003e The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as .get() and .post(), directly. This allows for more flexible composition of behavior.\n\nThe mixin classes can be imported from ```rest_framework.mixins```. Each mixin provides a method/action that implements some type of common functionality.\n\nMixins list :\n\n- ListModelMixin\n \u003e Provides a ```.create(request, *args, **kwargs)``` method, that implements creating and saving a new model instance.\n                                                                                          \n- CreateModelMixin\n \u003e Provides a ```.list(request, *args, **kwargs)``` method, that implements listing a queryset.                                                                                        \n- RetrieveModelMixin\n \u003e Provides a ```.retrieve(request, *args, **kwargs)``` method, that implements returning an existing model instance in a response.\n                                                                                          \n- UpdateModelMixin\n\u003e Provides a ```.update(request, *args, **kwargs)``` method, that implements updating and saving an existing model instance.\n                                                                                          \n- DestroyModelMixin\n\u003e Provides a ```destroy(request, *args, **kwargs)``` method, that implements deletion of an existing model instance.                                                                                          \n These mixins are designed to be used together with generics.GenericAPIView to provide common functionality that you can use across multiple views. By combining these mixins with GenericAPIView and your own custom code, you can create powerful and flexible class-based views in DRF.\n                                                                                          \n# Applying Generics and Mixins to our project\n\nTaking into account the previous topics, let's write 2 class that utilize the ```generics.GenericAPIView``` and some mixins.\n                                                                        \n\n# Concrete View Classes - \u003ca href=\"https://www.django-rest-framework.org/api-guide/generic-views/#concrete-view-classes\"\u003eDocs\u003c/a\u003e\n\n\u003eThe following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior.\n\nThe view classes can be imported from ```rest_framework.generics```.\n\n- CreateAPIView -\u003e Used for create-only endpoints.\n- ListAPIView -\u003e Used for read-only endpoints to represent a collection of model instances.\n- RetrieveAPIView -\u003e Used for read-only endpoints to represent a single model instance.\n- DestroyAPIView -\u003e Used for delete-only endpoints for a single model instance.\n- UpdateAPIView -\u003e Used for update-only endpoints for a single model instance.\n- ListCreateAPIView -\u003e Used for read-write endpoints to represent a collection of model instances.\n- RetrieveUpdateAPIView -\u003e Used for read or update endpoints to represent a single model instance.\n- RetrieveDestroyAPIView -\u003e Used for read or delete endpoints to represent a single model instance.\n- RetrieveUpdateDestroyAPIView -\u003e Used for read-write-delete endpoints to represent a single model instance.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizzypt%2Fdrf-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fizzypt%2Fdrf-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizzypt%2Fdrf-guide/lists"}