{"id":13809895,"url":"https://github.com/Nifled/drf-cheat-sheet","last_synced_at":"2025-05-14T10:31:18.121Z","repository":{"id":41899065,"uuid":"88581385","full_name":"Nifled/drf-cheat-sheet","owner":"Nifled","description":"Cheat sheet / quick reference guide for Django REST Framework.","archived":false,"fork":false,"pushed_at":"2023-01-03T01:57:17.000Z","size":31,"stargazers_count":219,"open_issues_count":2,"forks_count":63,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-08-04T02:06:51.700Z","etag":null,"topics":["django","django-rest-framework","rest-api"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nifled.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-18T04:25:44.000Z","updated_at":"2024-07-22T06:56:56.000Z","dependencies_parsed_at":"2023-02-01T04:45:32.120Z","dependency_job_id":null,"html_url":"https://github.com/Nifled/drf-cheat-sheet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nifled%2Fdrf-cheat-sheet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nifled%2Fdrf-cheat-sheet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nifled%2Fdrf-cheat-sheet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nifled%2Fdrf-cheat-sheet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nifled","download_url":"https://codeload.github.com/Nifled/drf-cheat-sheet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254120624,"owners_count":22018024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["django","django-rest-framework","rest-api"],"created_at":"2024-08-04T02:00:39.014Z","updated_at":"2025-05-14T10:31:18.091Z","avatar_url":"https://github.com/Nifled.png","language":null,"funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"readme":"# drf-cheat-sheet\nA collection of anything from basics to advanced recommended methods and usages with Django REST Framework for creating browsable and awesome web API's. This could also serve as a quick reference guide.\n\nHere is DRF's [official documentation](http://www.django-rest-framework.org/) in case you need everything in detail.\n\n### Why Django REST Framework?\nSummarized from the official docs:\n\n* Web browsable API\n* Serialization that supports ORM and non-ORM data sources.\n* Authentication is easily implemented.\n* Highly customizable in every sense of the word.\n\n## Index\n\n1. [Installation](#installation)\n\n2. [Serialization](#serialization)\n    - [ModelSerializer](#using-modelserializer-class)\n    - [Nested Serialization](#nested-serialization)\n    - [HyperlinkedModelSerializer](#hyperlinkedmodelserializer)\n    - [Dynamically modifying fields in serializer](#dynamically-modifying-fields-in-serializer)\n\n3. [Views](#views)\n    - [Function-based views](#using-function-based-views)\n    - [Class-based views](#using-class-based-views)\n    - [Generic Class-based views](#using-generic-class-based-views)\n    - [Mixins](#using-mixins)\n    - [ViewSets](#using-viewsets)\n        * [Routers](#routers)\n        * [Custom actions](#custom-actions-in-viewsets)\n\n4. [Pagination](#pagination)\n    - [With Generic Class-based views or Viewsets](#with-generic-class-based-views-or-viewsets)\n    - [With APIView or other Non-Generic View](#with-apiview-or-other-non-generic-view)\n    - [Modify Pagination Class](#modify-pagination-class)\n\n5. [Authentication](#authentication)\n    - [SessionAuthentication](#sessionauthentication)\n    - [TokenAuthentication](#tokenauthentication)\n        * [Generating Tokens](#generate-tokens-for-users)\n        * [Obtaining Tokens](#obtaining-tokens)\n    - [OAuth2](#oauth2)\n\n6. [Web Browsable API](#web-browsable-api)\n    - [Overriding the Default Theme](#overriding-the-default-theme)\n    - [Full Customization](#full-customization)\n\n7. [Testing](#testing)\n    - [APIRequestFactory](#apirequestfactory)\n\n\n### Base Example Model\n\nThroughout this cheat-sheet, the examples used will be based on this Post and Comment model.\n\n```python\nclass Post(models.Model):  \n    title = models.CharField(max_length=1024)\n    text = models.TextField()\n    created = models.DateField(auto_now_add=True)\n\n\nclass Comment(models.Model):\n    post = models.ForeignKey(Post, related_name='comments')\n    user = models.ForeignKey(User)\n    text = models.TextField()\n```\n\n\n### Installation\n\nInstall the package via `pip`:\n```\n$ pip install djangorestframework\n```\n\nThen, add `'rest_framework'` to `INSTALLED_APPS` in your `settings.py` file.\n\n```python\nINSTALLED_APPS = [\n    # Rest of your installed apps ...\n    'rest_framework',\n]\n```\n\n\u003cbr/\u003e\n\n### Serialization\n\nSerializers allow complex data like querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML, and other formats.\n\n#### Using ModelSerializer class:\n\nSuppose we wanted to create a PostSerializer for our example [Post](#base-example-model) model and CommentSerializer for our [Comment](#base-example-model) model.\n\n```python\nclass PostSerializer(serializers.ModelSerializer):\n\n    class Meta:\n        model = Post\n        fields = ('id', 'title', 'text', 'created')\n        \n        \nclass CommentSerializer(serializers.ModelSerializer):\n\n    class Meta:\n        model = Comment\n        fields = ('post', 'user', 'text')\n```\nOr you can also use `exclude` to exclude certain fields from being seialized. ModelSerializer has default implementations for the `create()` and `update()` methods.\n\n##### Nested Serialization\n\nBy default, instances are serialized with primary keys to represent relationships. To get nested serialization we could use, *General* or *Explicit* methods.\n\n###### General\n\nUsing `depth` parameter.\n\n```python\nclass CommentSerializer(serializers.ModelSerializer):\n\n    class Meta:\n        model = Comment\n        fields = '__all__'\n        depth = 2\n```\n\n###### Explicit\n\nYou can also define and nest serializers within eachother...\n\n```python\nclass CommentSerializer(serializers.ModelSerializer):\n    post = PostSerializer()\n    \n    class Meta:\n        model = Comment\n        fields = '__all__'\n```\n\nSo here, the comment's `post` field (how we named it in models.py) will serialize however we defined it in `PostSerializer`.\n\n#### HyperlinkedModelSerializer\n\nThis makes your web API a lot more easy to use (in browser) and would be a nice feature to add.\n\nLets say we wanted to see the comments that every post has in each of the [Post](#base-example-model) instances of our API. With `HyperlinkedModelSerializer`, instead of having nested primary keys or nested fields, we get a link to each individual [Comment](#base-example-model) (url).\n\n```python\nclass PostSerializer(serializers.HyperlinkedModelSerializer):\n\n    class Meta:\n        model = Post\n        fields = ('id', 'title', 'text', 'created', 'comments')\n        read_only_fields = ('comments',)\n```\n\n**Note:** without the `read_only_fields`, the `create` form for Posts would always require a `comments` input, which doesn't make sense (comments on a post are normally made AFTER the post is created).\n\nAnother way of hyperlinking is just adding a `HyperlinkedRelatedField` definition to a normal serializer.\n\n```python\nclass PostSerializer(serializers.ModelSerializer):\n    comments = serializers.HyperlinkedRelatedField(many=True, view_name='comment-detail', read_only=True)\n    \n    class Meta:\n        model = Post\n        fields = ('id', 'title', 'text', 'created', 'comments')\n```\n\n#### Dynamically modifying fields in serializer\n\nThis makes your API easier for extracting a limited number of parameters in a response. Let's say you want to set which fields should be used by a serializer at the point of initialization.\n\nJust copy below code and paste it in your serliazer file\n```python\nclass DynamicFieldsModelSerializer(serializers.ModelSerializer):\n\n    def __init__(self, *args, **kwargs):\n        # Don't pass the 'fields' arg up to the superclass\n        fields = kwargs.pop('fields', None)\n\n        # Instantiate the superclass normally\n        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)\n\n        if fields is not None:\n            # Drop any fields that are not specified in the `fields` argument.\n            allowed = set(fields)\n            existing = set(self.fields.keys())\n            for field_name in existing - allowed:\n                self.fields.pop(field_name)\n```\n\nExtend ```DynamicFieldsModelSerializer``` from your serializer class\n```python\nclass UserSerializer(DynamicFieldsModelSerializer):\n    class Meta:\n        model = User\n        fields = ('id', 'username', 'email')\n```\n\nMention the fields name inside ```fields```\n```python\nUserSerializer(user, fields=('id', 'email'))\n```\n\nHere, you will get only ```id```and ```email``` from serializer instead of all.\n\n[Back to Top ↑](#drf-cheat-sheet)\n\n\u003cbr/\u003e\n\n### Views\n\nThere are many options for creating views for your API, it really depends on your API's requirements or personal preference.\n\nWe'll write a `post_list` view with `GET` and `POST` methods for reading and creating new [Post](#base-example-model) instances.\n\n#### Using Function-based views:\n\n```python\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom rest_framework.parsers import JSONParser\nfrom rest_framework import status\nfrom posts.models import Post\nfrom posts.serializers import PostSerializer\n\n@api_view(['GET', 'POST'])\ndef post_list(request, format=None):\n\n    if request.method == 'GET':\n        posts = Post.objects.all()\n        serializer = PostSerializer(posts, many=True)\n        return Response(serializer.data)\n\n    elif request.method == 'POST':\n        data = JSONParser().parse(request)\n        serializer = PostSerializer(data=data)\n\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_201_CREATED)\n        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n```\n\n#### Using Class-based views:\n```python\nfrom rest_framework.response import Response\nfrom rest_framework import status\nfrom rest_framework.views import APIView\nfrom posts.models import Post\nfrom posts.serializers import PostSerializer\n\n\nclass PostList(APIView):\n    def get(self, request, format=None):\n        snippets = Post.objects.all()\n        serializer = PostSerializer(snippets, many=True)\n        return Response(serializer.data)\n\n    def post(self, request, format=None):\n        serializer = PostSerializer(data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n```\n\n\n#### Using Generic Class-based views:\n\n```python\nfrom rest_framework import generics\nfrom posts.models import Post\nfrom posts.serializers import PostSerializer\n\n\nclass PostList(generics.ListCreateAPIView):\n    queryset = Post.objects.all()\n    serializer_class = PostSerializer\n```\n\n#### Using Mixins:\n\n```python\nfrom rest_framework import generics, mixins\nfrom posts.models import Post\nfrom posts.serializers import PostSerializer\n\n\nclass PostList(generics.GenericAPIView,\n               mixins.ListModelMixin,\n               mixins.CreateModelMixin\n               ):\n    queryset = Post.objects.all()\n    serializer_class = PostSerializer\n\n    def get(self, request, *args, **kwargs):\n        return self.list(request, *args, **kwargs)\n\n    def post(self, request, *args, **kwargs):\n        return self.create(request, *args, **kwargs)\n```\n\n#### Using ViewSets:\n\nWith `ModelViewSet` (in this case), you don't have to create separate views for getting list of objects and detail of one object. ViewSet will handle it for you in a consistent way for both methods.\n\n```python\nfrom rest_framework import viewsets\nfrom posts.models import Post\nfrom posts.serializers import PostSerializer\n\n\nclass PostViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    A viewset for viewing and editing post instances.\n    \"\"\"\n    queryset = Post.objects.all()\n    serializer_class = PostSerializer\n```\n\nSo basically, this would not only generate the `list` view, but also the `detail` view for every [Post](#base-example-model) instance.\n\n##### Routers\n\nRouters in ViewSets allow the URL configuration for your API to be automatically generated using naming standards.\n\n```python\nfrom rest_framework.routers import DefaultRouter\nfrom posts.views import PostViewSet\n\nrouter = DefaultRouter()\nrouter.register(r'users', UserViewSet)\nurlpatterns = router.urls\n```\n\n##### Custom Actions in ViewSets\nDRF provides helpers to add custom actions for _ad-hoc_ behaviours with the `@action` decorator. The router will configure its url accordingly.\nFor example, we can add a `comments` action in the our `PostViewSet` to retrieve all the comments of a specific post as follows:\n\n ```python\nfrom rest_framework import viewsets\nfrom rest_framework.decorators import action\nfrom posts.models import Post\nfrom posts.serializers import PostSerializer, CommentSerializer\n\n\nclass PostViewSet(viewsets.ModelViewSet):\n    ...\n    \n    @action(methods=['get'], detail=True)\n    def comments(self, request, pk=None):\n        try:\n            post = Post.objects.get(id=pk)\n        except Post.DoesNotExist:\n            return Response({\"error\": \"Post not found.\"},\n                            status=status.HTTP_400_BAD_REQUEST)\n        comments = post.comments.all()\n        return Response(CommentSerializer(comments, many=True))\n```\n\nUpon registering the view as `router.register(r'posts', PostViewSet)`, this action will then be available at the url `posts/{pk}/comments/`.\n\n[Back to Top ↑](#drf-cheat-sheet)\n\n\u003cbr/\u003e\n\n### Pagination\n\nThis can usually be obtained automatically with `generic class-based views` and `viewsets`. If you're using `APIView`, it must be explicitly applied.\n\n#### With Generic Class-based views or Viewsets\n\nFor these views to use pagination, all we have to do is override `DEFAULT_PAGINATION_CLASS` and `PAGE_SIZE` in our DRF settings in `settings.py`.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',\n    'PAGE_SIZE': 20\n}\n```\n\n#### With APIView or other Non-Generic View\n\nFirst we must define the default behaviour for our pagination, which is done in `settings.py`.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',\n    'PAGE_SIZE': 20\n}\n```\n\nNow we need to handle our pagination through our view (APIView in this case).\n\n```python\nfrom rest_framework.settings import api_settings\nfrom rest_framework.views import APIView\n\nclass PostView(APIView):\n    # ... queryset, serializer_class, etc\n    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS\n\n    # We need to override the get method to achieve pagination\n    def get(self, request):\n        # ...\n        page = self.paginate_queryset(self.queryset)\n        if page is not None:\n            serializer = self.serializer_class(page, many=True)\n            return self.get_paginated_response(serializer.data)\n        # ...\n\n    # Now add the pagination handlers taken from \n    # django-rest-framework/rest_framework/generics.py\n    @property\n    def paginator(self):\n        \"\"\"\n        The paginator instance associated with the view, or `None`.\n        \"\"\"\n     if not hasattr(self, '_paginator'):\n         if self.pagination_class is None:\n             self._paginator = None\n         else:\n             self._paginator = self.pagination_class()\n     return self._paginator\n\n     def paginate_queryset(self, queryset):\n         \"\"\"\n         Return a single page of results, or `None` if pagination is disabled.\n         \"\"\"\n         if self.paginator is None:\n             return None\n         return self.paginator.paginate_queryset(queryset, self.request, view=self)\n\n     def get_paginated_response(self, data):\n         \"\"\"\n         Return a paginated style `Response` object for the given output data.\n         \"\"\"\n         return self.paginator.get_paginated_response(data)\n```\n\n#### Modify Pagination Class\n\nWe can define our own pagination class and override default attributes. Using this new class, you can make one, multiple or all views use this instead of `LimitOffsetPagination` or other ones provided by DRF.\n\n```python\nclass CustomPagination(PageNumberPagination):\n    \"\"\"\n    Client controls the response page size (with query param), limited to a maximum of `max_page_size` and default of `page_size`.\n    \"\"\"\n    page_size = 100\n    page_size_query_param = 'page_size'\n    max_page_size = 10000\n```\n\n[Back to Top ↑](#drf-cheat-sheet)\n\n\u003cbr/\u003e\n\n### Authentication\n\nDRF has ready-to-use and integrated authentication schemes, but if you need something more specific, you can customize [your own scheme](http://www.django-rest-framework.org/api-guide/authentication/#custom-authentication).\n\n#### SessionAuthentication\n\nSessionAuthentication uses the default session backend for authentication provided by Django, which is more practical for us devs. Once a user has been successfully authenticated, a User instance is stored in `request.user`.\n\n\n#### TokenAuthentication\n\nThe recommended use for the`TokenAuthentication` class is when client-server setups like native apps.\n\nFirst, add `'rest_framework.authtoken'` to your `INSTALLED_APPS`:\n```python\nINSTALLED_APPS = [\n    # Rest of your installed apps ...\n    'rest_framework',\n    'rest_framework.authtoken'\n]\n```\n\n##### Generate Tokens for users\n \nUsing signals (in `models.py`).\n\n```python\nfrom django.conf import settings\nfrom django.db.models.signals import post_save\nfrom django.dispatch import receiver\nfrom rest_framework.authtoken.models import Token\n\n# For existing users\nfor user in User.objects.all():\n    Token.objects.get_or_create(user=user)\n\n# For newly created users\n@receiver(post_save, sender=settings.AUTH_USER_MODEL)\ndef create_auth_token(sender, instance=None, created=False, **kwargs):\n    if created:\n        Token.objects.create(user=instance)\n```\n\n##### Obtaining Tokens\nDRF provides a built in view to obtain tokens given username and password.\n\n```python\nfrom rest_framework.authtoken import views\nurlpatterns += [\n    url(r'^api-token-auth/', views.obtain_auth_token)\n]\n```\n\n#### OAuth2\n\nOAuth and OAuth2 were previously integrated in DRF, but the corresponding modules were moved and is now supported as a third-party package. There are also other very cool and handy packages that can be easily implemented.\n\n* [Django Rest Framework OAuth](http://jpadilla.github.io/django-rest-framework-oauth/)\n* [Django OAuth Toolkit](https://github.com/evonove/django-oauth-toolkit) (recommended for OAuth2)\n\nIf that isn't enough, there's a few more [here](http://www.django-rest-framework.org/topics/third-party-packages/#authentication).\n\n[Back to Top ↑](#drf-cheat-sheet)\n\n\u003cbr/\u003e\n\n### Web Browsable API\n\nThe default look DRF gives you for the browsable API is pretty cool on its own, but in case you don't like it, there are provided ways of customization.\n\n#### Overriding the Default theme\n\nFirst thing you must do is create a template in `templates/rest_framework/api.html` that extends `rest_framework/base.html`.\n\n```\n{% extends \"rest_framework/base.html\" %}\n```\n\nNow you can modify the many block components that are included in the `base.html` to your styling. Just as you would do on normal Django templates when you have a `base.html`.\n\n* `body` - Whole HTML body.\n* `bootstrap_theme` - CSS for the Bootstrap theme.\n* `bootstrap_navbar_variant` - CSS for only the Navbar.\n* `branding` - Brand component in Navbar (top left).\n* `script` - Custom Javascript.\n* `style` - Custom CSS.\n\nThose are the common ones, here's [all of 'em](http://www.django-rest-framework.org/topics/browsable-api/#blocks).\n\n```html\n{% block branding %}\n    \u003ca class=\"navbar-brand\" rel=\"nofollow\" href=\"https://erick.netlify.app\"\u003e\n        Nifled's Blog\n    \u003c/a\u003e\n{% endblock %}\n```\n\nThis is how you would modify any other block.\n\n#### Full Customization\n\nIf you don't dig the Bootstrap look, you can just drop the whole default look and fully customize it on your own. There is a context provided that you could work with.\n\n* `api_settings` -  API settings\n* `content` - The content of the API response\n* `request` - The request object\n* `response ` - The response object\n* `view ` - The view handling the request\n\nFull list of context variables [here](http://www.django-rest-framework.org/topics/browsable-api/#context).\n\nTake a look at the actual [base HTML](https://github.com/encode/django-rest-framework/blob/73ad88eaae2f49bfd09508f2dcd6446677800a26/rest_framework/templates/rest_framework/base.html) source code for the API in DRF to get an idea of how it's actually made.\n\n[Back to Top ↑](#drf-cheat-sheet)\n\n\u003cbr/\u003e\n\n### Testing\n\nIt's important to test your API to make sure it works, of course.\n\n#### APIRequestFactory\n\nIt has a similar name to Django's [RequestFactory](https://docs.djangoproject.com/en/1.11/topics/testing/advanced/#the-request-factory) class, because it extends it. You can use the different class methods to send test requests to your API.\n\nLets say we want to write test requests for our example [Post](#base-example-model) model.\n\n```python\nfrom django.test import TestCase\nfrom rest_framework.test import APIRequestFactory\nfrom posts.models import Post\nfrom posts.views import PostList\n\n\nclass PostTest(TestCase):  # Post object, not HTTP method POST.\n    \"\"\"We'll be testing with the PostList view (class-based view).\"\"\"\n\n    def setUp(self):\n        self.factory = APIRequestFactory()\n        self.post = Post.objects.create(title='Post example', text='Lorem Ipsum')\n        \n    # For HTTP method GET\n    def get(self):\n        view = PostList.as_view()\n        request = self.factory.get('/posts/')\n        response = view(request)\n        \n        self.assertEqual(response.status_code, 200)  # 200 = OK\n\n    # For HTTP method POST\n    def post(self):\n        view = PostList.as_view()\n        \n        # Generating the request\n        request = self.factory.post('/posts/', {'title': 'Post example', 'text': 'Lorem Ipsum'})\n        \n        response = view(request)\n        expected = {'title': self.post.title, 'text': self.post.text}\n        \n        self.assertEqual(response.status_code, 201)  # 201 = created\n        self.assertEqual(response.data, expected)\n\n```\n\n[Back to Top ↑](#drf-cheat-sheet)\n\n\u003cbr/\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNifled%2Fdrf-cheat-sheet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNifled%2Fdrf-cheat-sheet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNifled%2Fdrf-cheat-sheet/lists"}