Django REST framework (DRF)

repository·main·Indexed 12 days ago

https://github.com/encode/django-rest-framework

A powerful and flexible toolkit for building Web APIs with Django. It features a browsable API, customizable authentication schemes (including Token, Session, and Basic authentication), and robust serialization using Serializers, ViewSets, and Routers for both ORM and non-ORM data sources.

Tokens
112.9K
Snippets
344
Records
479
Agent score
96%

What's inside Django REST framework

  1. Overview of third-party authentication packages

    main

    For specialized authentication needs, several third-party libraries are available:

    • Token-based & Mobile/SPA:
      • django-rest-knox: Secure, extensible token-based authentication with per-client tokens and server-enforced logout.
      • djangorestframework-simplejwt: JSON Web Token (JWT) authentication that does not require database lookups for validation.
      • drf-social-oauth2: Authenticates with social vendors (Facebook, Google, etc.) using JWT.
    • User Management & Registration:
      • Djoser: Provides views for registration, login, logout, password reset, and account activation.
      • dj-rest-auth: A fork of django-rest-auth providing endpoints for user management (registration, social auth, password reset).
      • django-rest-authemail: Uses email addresses instead of usernames for authentication.
      • drfpasswordless: Adds passwordless login via email or mobile number.
    • Advanced & Specialized Protocols:
      • Django OAuth Toolkit: Recommended for OAuth 2.0 support.
      • HawkREST: Implements Hawk HTTP authentication (signed requests/responses).
      • drf-httpsig: Implements HTTP Signature authentication for stateless, per-request authentication.
      • django-pyoidc: Adds OpenID Connect (OIDC) support for Single-Sign-On (SSO).
      • DRF Auth Kit: Modern solution with JWT cookies, social login, and MFA.
  2. New features in Django REST framework 3.0

    main

    The 3.0 release introduced several improvements to the API and implementation:

    • Printable representations: Serializers allow inspection of exactly which fields are present on an instance.
    • Simplified Model Serializers: Easier to understand and debug, with a smoother transition between ModelSerializer and the explicit Serializer class.
    • BaseSerializer class: A new base class for writing serializers for alternative storage backends or custom validation logic.
    • Enhanced Fields API: Includes new classes like ListField and MultipleChoiceField.
    • Generic View Mixins: Super simple default implementations for generic views.
    • Validation Error Customization: Support for overriding how validation errors are handled.
    • Metadata API: Allows customization of how OPTIONS requests are handled.
    • Compact JSON: Unicode style encoding is enabled by default for more compact output.
    • Templated HTML Forms: Support for templated-based HTML form rendering for serializers (finalized in 3.1).
  3. Handle nested representations and writable nesting

    main

    You can represent relationships by nesting one serializer inside another.

    • Single object: user = UserSerializer()
    • Optional object: user = UserSerializer(required=False)
    • List of objects: edits = EditItemSerializer(many=True)

    Writable Nested Representations

    When a serializer contains nested serializers, the default .create() and .update() methods of ModelSerializer do not support writing to the nested objects. You must implement these methods explicitly to handle the logic of creating or updating related models.

    Example: Creating a User with a Profile

    def create(self, validated_data):
        profile_data = validated_data.pop('profile')
        user = User.objects.create(**validated_data)
        Profile.objects.create(user=user, **profile_data)
        return user
    class UserSerializer(serializers.ModelSerializer):
        profile = ProfileSerializer()
    
        class Meta:
            model = User
            fields = ['username', 'email', 'profile']
    
        def create(self, validated_data):
            profile_data = validated_data.pop('profile')
            user = User.objects.create(**validated_data)
            Profile.objects.create(user=user, **profile_data)
            return user
  4. How content negotiation works in Django REST framework

    main

    Content negotiation is the process of selecting the best representation (media type) to return to a client based on client preferences (the Accept: header) and server configuration (available renderers).

    DRF uses a hybrid approach to determine the renderer:

    1. Specificity: More specific media types in the Accept header are preferred over less specific ones.
    2. Server Preference: If multiple media types have the same specificity, DRF selects the one that appears earliest in the renderer_classes list (for a specific view) or the DEFAULT_RENDERER_CLASSES setting (globally).

    Note on 'q' values: DRF does not take q values (quality values) from the Accept header into account. This is a deliberate design choice to avoid complexity and negative impacts on caching.

  5. Building Hypermedia APIs with Django REST framework

    main

    Django REST framework is an agnostic Web API toolkit designed to help you build well-connected APIs. While it does not strictly enforce a specific design style, it provides the necessary building blocks to implement Hypermedia and HATEOAS (Hypermedia as the Engine of Application State).

    Capabilities

    • Browsable API: Built on HTML, providing a human-readable hypermedia interface for interacting with your API.
    • Media Type Support: Through serializers, parsers, and renderers, you can design and support custom media types.
    • Hyperlinked Relations: Use hyperlinked fields to create well-connected systems where resources point to one another via URIs.
    • Content Negotiation: Built-in support for negotiating the representation of resources based on client requirements.

    Limitations

    Django REST framework does not provide machine-readable hypermedia formats (such as HAL, Collection+JSON, or JSON API) by default, nor does it automatically generate fully HATEOAS-compliant APIs with hypermedia-based form descriptions. These design choices are left to the developer to ensure the framework remains unopinionated.

  6. How parsers are determined in Django REST framework

    main

    Django REST framework uses a list of parser classes for each view to determine how to handle incoming request data. When request.data is accessed, the framework examines the Content-Type header of the HTTP request and selects the appropriate parser from the view's parser_classes list.

    Important: When building client applications, you must explicitly set the Content-Type header. If omitted, many clients default to application/x-www-form-urlencoded, which may cause parsing errors if you are actually sending JSON or other structured data.

  7. Use nested relationships in serializers

    main

    Instead of just referencing an entity, you can embed (nest) the related entity's data directly within the parent object's representation. This is achieved by using a serializer as a field within another serializer.

    To represent a to-many relationship (e.g., an Album having many Tracks), you must include the many=True flag on the serializer field.

    By default, nested serializers are read-only. If you need to support creating or updating nested data, you must explicitly implement the create() and/or update() methods in the parent serializer to handle the child relationship logic.

    class TrackSerializer(serializers.ModelSerializer):
        class Meta:
            model = Track
            fields = ['order', 'title', 'duration']
    
    class AlbumSerializer(serializers.ModelSerializer):
        # Use many=True for to-many relationships
        tracks = TrackSerializer(many=True, read_only=True)
    
        class Meta:
            model = Album
            fields = ['album_name', 'artist', 'tracks']
  8. Manage compatibility code in Django REST framework

    main
    When code must behave differently across various versions of Django, Python, or third-party libraries, isolate the branching logic into the compat.py module. This module should provide a single, unified interface that the rest of the codebase consumes, hiding the underlying version-specific implementation details.
  9. Identify client IP addresses for throttling

    main

    REST framework identifies clients using the X-Forwarded-For header or the REMOTE_ADDR WSGI variable.

    If your API runs behind application proxies, you must configure the NUM_PROXIES setting to ensure the correct client IP is identified.

    • If NUM_PROXIES is non-zero, the client IP is identified as the last IP in the X-Forwarded-For header after excluding the specified number of proxy IPs.
    • If NUM_PROXIES is zero, REMOTE_ADDR is always used.

    Note: Clients behind a single NAT'd gateway will be treated as a single client.