Django REST framework JSON:API

repository·main·Indexed 22 days ago

https://github.com/django-json-api/django-rest-framework-json-api

A library providing JSON:API specification support for the Django REST framework. It transforms standard DRF responses into compliant JSON:API structures, featuring automatic key inflection, relationships, links, and pagination. It includes specialized serializers, filter backends like QueryParameterValidationFilter and DjangoFilterBackend, and support for custom resource identifiers and error formats.

Tokens
8.7K
Snippets
22
Records
29
Agent score
77%

What's inside django-rest-framework-json-api

  1. Configure JSON:API Pagination

    main

    DJA pagination follows DRF patterns and returns a meta object with record counts and a links object for navigation. You can set pagination globally in REST_FRAMEWORK['DEFAULT_PAGINATION_CLASS'] or per viewset using the pagination_class attribute.

    JsonApiPageNumberPagination

    Breaks responses into pages based on a page number.

    • page_query_param (default: page[number])
    • page_size_query_param (default: page[size]). Set to None to disable client-side size selection.
    • page_size (default: REST_FRAMEWORK['PAGE_SIZE'])
    • max_page_size (default: 100)

    JsonApiLimitOffsetPagination

    Breaks responses into pages based on an offset and a limit.

    • offset_query_param (default: page[offset])
    • limit_query_param (default: page[limit])
    • default_limit (default: REST_FRAMEWORK['PAGE_SIZE'])
    • max_limit (default: 100)
    from rest_framework_json_api.pagination import JsonApiPageNumberPagination, JsonApiLimitOffsetPagination
    
    class MyPagePagination(JsonApiPageNumberPagination):
        page_query_param = 'page_number'
        page_size_query_param = 'page_length'
        page_size = 3
        max_page_size = 1000
    
    class MyLimitPagination(JsonApiLimitOffsetPagination):
        offset_query_param = 'offset'
        limit_query_param = 'limit'
        default_limit = 3
        max_limit = None
  2. Compare relationship field types

    main

    Depending on your performance and data requirements, choose between these relationship field types:

    1. ResourceRelatedField: Standard behavior. Renders both the relationship links and the data (resource identifiers).
    2. HyperlinkedRelatedField: High performance. Renders only the links, omitting the data object to reduce payload size.
    3. SerializerMethodResourceRelatedField: Flexible. Combines SerializerMethodField logic with ResourceRelatedField capabilities, allowing you to use a custom method to determine the relationship while still rendering data.
  3. JSON:API vs Standard Django REST framework response formats

    main

    Django REST framework (DRF) typically produces a flat response structure. When using django-rest-framework-json-api, the response is transformed to follow the JSON:API specification, which includes links, data (with type, id, and attributes), and meta (for pagination).

    // Standard DRF response format
    {
        "count": 20,
        "next": "https://example.com/api/1.0/identities/?page=3",
        "previous": "https://example.com/api/1.0/identities/?page=1",
        "results": [{
            "id": 3,
            "username": "john",
            "full_name": "John Coltrane"
        }]
    }
    
    // JSON:API compliant response format
    {
        "links": {
            "first": "https://example.com/api/1.0/identities",
            "last": "https://example.com/api/1.0/identities?page=5",
            "next": "https://example.com/api/1.0/identities?page=3",
            "prev": "https://example.com/api/1.0/identities",
        },
        "data": [{
            "type": "identities",
            "id": "3",
            "attributes": {
                "username": "john",
                "full-name": "John Coltrane"
            }
        }],
        "meta": {
            "pagination": {
              "page": "2",
              "pages": "5",
              "count": "20"
            }
        }
    }
  4. Set the resource name for JSON:API types

    main

    The resource_name property determines the type attribute in JSON:API responses. It is automatically set as the plural of the view or model name, but you can manually override it.

    Resolution order: View > Serializer > Model.

    • On a View: Set resource_name = 'name' directly on the view class.
    • On a Serializer: Set resource_name = 'name' on the serializer class.
    • On a Model: Include a JSONAPIMeta class within the model containing the resource_name property.

    Note: Setting resource_name on a view can cause the type to change depending on which endpoint is used to fetch the resource.

    # Example - resource_name on View
    class Me(generics.GenericAPIView):
        resource_name = 'users'
        serializer_class = identity_serializers.IdentitySerializer
        allowed_methods = ['GET']
        permission_classes = (permissions.IsAuthenticated, )
    
    # Example - resource_name on Model
    class Me(models.Model):
        name = models.CharField(max_length=100)
    
    class JSONAPIMeta:
        resource_name = "users"
  5. Use DJA Serializers and Overwrite Resource IDs

    main

    Use DJA Serializers

    For full JSON:API support, always import base serializer classes from rest_framework_json_api instead of rest_framework.

    from rest_framework_json_api import serializers
    
    class MyModelSerializer(serializers.ModelSerializer):
        # ...

    Overwrite Resource ID

    By default, the pk of the instance is used as the resource identifier. To use a different field (like an email) as the ID, define an id field on the serializer.

    class UserSerializer(serializers.ModelSerializer):
        id = serializers.CharField(source='email')
        name = serializers.CharField()
    
        class Meta:
            model = User
  6. Handle Exceptions with JSON:API Error Formats

    main

    You can control how exceptions are formatted using the JSON_API_UNIFORM_EXCEPTIONS setting:

    • If True: All exceptions respond using the standard JSON:API error format.
    • If False (default): Non-JSON:API views use the standard DRF error format.

    To manually raise a custom error that follows the JSON:API structure, use rest_framework.serializers.ValidationError with a dictionary containing detail and source (with a pointer).

    raise serializers.ValidationError(
        {
            "id": "your-id",
            "detail": "your detail message",
            "source": {
                "pointer": "/data/attributes/your-pointer",
            }
        }
    )
  7. Configure Compound Documents using included_serializers

    main

    JSON:API allows including related resources in a single request (Compound Documents) via the included key.

    To enable this in your serializers:

    1. Define included_serializers to tell DJA which serializers to use for related resources.
    2. (Optional) Define included_resources in a JSONAPIMeta class to specify which resources should be included by default.

    Warning: Using included resources without prefetching will cause $N+1$ query performance issues.

    class QuestSerializer(serializers.ModelSerializer):
        included_serializers = {
            'knight': KnightSerializer,
        }
    
        class Meta:
            model = Quest
            fields = ('id', 'title', 'reward', 'knight')
    
    class JSONAPIMeta:
        included_resources = ['knight']
  8. Configure related URLs for resource relationships

    main

    To support JSON:API related URLs (e.g., /orders/3/lineitems/), configure your urls.py to handle a pattern that captures the parent pk and a related_field.

    In your serializer, use ResourceRelatedField and map it to the view name using related_link_view_name. Ensure the related_link_url_kwarg matches the name of the parameter in your URL regex (usually 'pk').

    Note: Related resources are served by the same view as the parent. Therefore, permission checks are performed on the parent object; if the user can access the parent, they can access the related resource.

    # urls.py
    url(r'^orders/(?P<pk>[^/.]+)/$', OrderViewSet.as_view({'get': 'retrieve'}), name='order-detail'),
    url(r'^orders/(?P<pk>[^/.]+)/(?P<related_field>[-\w]+)/$', OrderViewSet.as_view({'get': 'retrieve_related'}), name='order-related'),
    
    # serializers.py
    class OrderSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = Order
    
        related_serializers = {
            'customer': 'example.serializers.CustomerSerializer',
            'line_items': 'example.serializers.LineItemSerializer'
        }
    
        line_items = ResourceRelatedField(
            queryset=LineItem.objects,
            many=True,
            related_link_view_name='order-related',
            related_link_url_kwarg='pk',
            self_link_view_name='order-relationships'
        )
  9. Run the example application

    main

    To run the included example application for testing purposes, follow these steps within a virtual environment:

    1. Clone the repository.
    2. Install the requirements from requirements.txt.
    3. Run migrations using the example.settings.
    4. Load the example data.
    5. Start the development server.

    The list of available collections can be browsed at http://localhost:8000 (note: these may appear in a non-JSON:API format in the browsable API).

    $ git clone https://github.com/django-json-api/django-rest-framework-json-api.git
    $ cd django-rest-framework-json-api
    $ pip install -Ur requirements.txt
    $ django-admin migrate --settings=example.settings --pythonpath .
    $ django-admin loaddata drf_example --settings=example.settings --pythonpath .
    $ django-admin runserver --settings=example.settings --pythonpath .
  10. Requirements for Django REST framework JSON:API

    main

    Ensure your environment meets the following version requirements. The project highly recommends and officially supports the latest patch release of each series.

    • Python: 3.10, 3.11, 3.12, 3.13, 3.14
    • Django: 5.2, 6.0
    • Django REST framework: 3.16, 3.17

    Note: For optional dependencies like django-filter, only the latest release is officially supported.

  11. Work with polymorphic resources

    main

    Polymorphic resources allow a single endpoint to expose different specialized subclasses (e.g., an ArtProject and a ResearchProject under a Project endpoint).

    Note: Support for django-polymorphic is deprecated and will be removed in a future release. There is currently no replacement.

    To implement polymorphic resources:

    1. Install the dependency: pip install djangorestframework-jsonapi['django-polymorphic'].
    2. Use serializers.PolymorphicModelSerializer and define the polymorphic_serializers list.
    3. For relationships, use relations.PolymorphicResourceRelatedField and pass the base polymorphic serializer as the first argument.
    # Polymorphic Serializer
    class ProjectSerializer(serializers.PolymorphicModelSerializer):
        polymorphic_serializers = [ArtProjectSerializer, ResearchProjectSerializer]
    
        class Meta:
            model = models.Project
    
    # Polymorphic Relation
    class CompanySerializer(serializers.ModelSerializer):
        current_project = relations.PolymorphicResourceRelatedField(
            ProjectSerializer, 
            queryset=models.Project.objects.all()
        )
  12. Install djangorestframework-jsonapi

    main

    Install the package using pip. You can also install optional integrations for django-filter or django-polymorphic using extras.

    To use the package in your Django project, add 'rest_framework_json_api' to your INSTALLED_APPS setting, ensuring it is placed below 'rest_framework'.

    $ pip install djangorestframework-jsonapi
    $ # for optional package integrations
    $ pip install djangorestframework-jsonapi['django-filter']
    $ pip install djangorestframework-jsonapi['django-polymorphic']
    INSTALLED_APPS = [
        ...
        'rest_framework',
        'rest_framework_json_api',
        ...
    ]