CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-djangorestframework

Web APIs for Django, made easy.

Pending
Overview
Eval results
Files

request-response.mddocs/

Request and Response

Enhanced request and response objects providing consistent data access and content negotiation across different formats in Django REST Framework.

Capabilities

Request Object

Enhanced request object wrapping Django's HttpRequest with additional API functionality.

class Request:
    """
    Enhanced request object with additional API features.
    """
    def __init__(self, request, parsers=None, authenticators=None, 
                 negotiator=None, parser_context=None):
        """
        Initialize DRF Request object.
        
        Args:
            request: Django HttpRequest object
            parsers (list): List of parser instances
            authenticators (list): List of authenticator instances  
            negotiator: Content negotiation instance
            parser_context (dict): Additional context for parsers
        """
    
    @property
    def data(self):
        """
        Parsed request body data.
        
        Returns:
            dict or list: Parsed data from request body
        """
    
    @property
    def query_params(self):
        """
        Query parameters from URL (more semantic than request.GET).
        
        Returns:
            QueryDict: URL query parameters
        """
    
    @property
    def user(self):
        """
        Authenticated user for this request.
        
        Returns:
            User instance or AnonymousUser
        """
    
    @property
    def auth(self):
        """
        Authentication credentials for this request.
        
        Returns:
            Authentication token/credentials or None
        """
    
    @property
    def successful_authenticator(self):
        """
        Authenticator instance that successfully authenticated the request.
        
        Returns:
            Authentication instance or None
        """
    
    @property
    def accepted_renderer(self):
        """
        Renderer selected by content negotiation.
        
        Returns:
            Renderer instance
        """
    
    @property
    def accepted_media_type(self):
        """
        Media type selected by content negotiation.
        
        Returns:
            str: Media type string
        """
    
    @property
    def version(self):
        """
        API version for this request.
        
        Returns:
            str: Version string or None
        """
    
    @property
    def versioning_scheme(self):
        """
        Versioning scheme instance for this request.
        
        Returns:
            Versioning scheme instance or None
        """

Response Object

Enhanced response object for API responses with content negotiation support.

class Response(SimpleTemplateResponse):
    """
    Enhanced response object for API responses.
    """
    def __init__(self, data=None, status=None, template_name=None, 
                 headers=None, exception=False, content_type=None):
        """
        Initialize API response.
        
        Args:
            data: Response data (will be serialized)
            status (int): HTTP status code
            template_name (str): Template for HTML rendering
            headers (dict): Additional response headers
            exception (bool): Whether response is for exception
            content_type (str): Response content type
        """
    
    @property
    def data(self):
        """
        Response data to be serialized.
        
        Returns:
            Response data
        """
    
    @data.setter
    def data(self, value):
        """
        Set response data.
        
        Args:
            value: Data to serialize in response
        """
    
    @property
    def status_code(self):
        """
        HTTP status code for response.
        
        Returns:
            int: HTTP status code
        """
    
    @status_code.setter
    def status_code(self, value):
        """
        Set HTTP status code.
        
        Args:
            value (int): HTTP status code
        """
    
    @property
    def status_text(self):
        """
        HTTP status text for response.
        
        Returns:
            str: HTTP status text
        """
    
    @property
    def rendered_content(self):
        """
        Rendered response content.
        
        Returns:
            bytes: Rendered content
        """

Request Utilities

Utility classes and functions for request handling.

class ForcedAuthentication:
    """
    Force specific user authentication for testing.
    """
    def __init__(self, user, token=None):
        """
        Args:
            user: User instance to authenticate as
            token: Optional authentication token
        """
        self.user = user
        self.token = token

class Empty:
    """
    Placeholder for empty request data.
    """
    pass

def is_form_media_type(media_type):
    """
    Check if media type represents form data.
    
    Args:
        media_type (str): Media type to check
        
    Returns:
        bool: True if form media type
    """

def clone_request(request, method):
    """
    Clone request with different HTTP method.
    
    Args:
        request: Original request object
        method (str): New HTTP method
        
    Returns:
        Request: Cloned request with new method
    """

class override_method:
    """
    Context manager for temporarily overriding request method.
    """
    def __init__(self, request, method):
        """
        Args:
            request: Request object to modify
            method (str): Temporary method to use
        """
        self.request = request
        self.method = method
        self.original_method = None
    
    def __enter__(self):
        """Enter context with overridden method."""
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """Restore original method."""

Usage Examples

Accessing Request Data

from rest_framework.views import APIView
from rest_framework.response import Response

class BookView(APIView):
    def post(self, request):
        # Access parsed request data (JSON, form data, etc.)
        title = request.data.get('title')
        author = request.data.get('author')
        
        # Access query parameters
        format_type = request.query_params.get('format', 'json')
        
        # Access authenticated user
        user = request.user
        
        # Access authentication token/credentials
        auth_token = request.auth
        
        # Create response
        response_data = {
            'message': f'Book "{title}" by {author} created',
            'user': user.username if user.is_authenticated else 'anonymous',
            'format': format_type
        }
        
        return Response(response_data, status=201)

Different Response Types

from rest_framework.response import Response
from rest_framework import status

class BookAPIView(APIView):
    def get(self, request):
        # Simple data response
        data = {'books': ['Book 1', 'Book 2']}
        return Response(data)
    
    def post(self, request):
        # Response with custom status
        return Response(
            {'message': 'Book created'}, 
            status=status.HTTP_201_CREATED
        )
    
    def put(self, request):
        # Response with custom headers
        headers = {'X-Custom-Header': 'custom-value'}
        return Response(
            {'message': 'Book updated'}, 
            headers=headers
        )
    
    def delete(self, request):
        # Empty response with status code
        return Response(status=status.HTTP_204_NO_CONTENT)

Content Negotiation

class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        
        # Check accepted media type
        if request.accepted_media_type == 'application/json':
            # JSON response
            serializer = BookSerializer(books, many=True)
            return Response(serializer.data)
        elif request.accepted_media_type == 'text/html':
            # HTML response
            return Response(
                {'books': books}, 
                template_name='books/list.html'
            )
        else:
            # Default response
            return Response({'error': 'Unsupported media type'})

API Versioning

API versioning allows maintaining backward compatibility while evolving APIs. Multiple versioning strategies are supported.

Versioning Classes

class BaseVersioning:
    """
    Base class for all versioning schemes.
    """
    default_version = None
    allowed_versions = None
    version_param = 'version'
    
    def determine_version(self, request, *args, **kwargs):
        """
        Determine API version from request.
        Must be implemented by subclasses.
        
        Returns:
            str: Version string
        """
    
    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
        """
        Reverse URL with version information.
        """

class AcceptHeaderVersioning(BaseVersioning):
    """
    Version determined from Accept header media type parameters.
    Example: Accept: application/json; version=1.0
    """

class URLPathVersioning(BaseVersioning):  
    """
    Version determined from URL path keyword arguments.
    Example: /v1/books/
    """

class NamespaceVersioning(BaseVersioning):
    """
    Version determined from URL namespace.
    Example: URL patterns with version namespaces
    """

class HostNameVersioning(BaseVersioning):
    """
    Version determined from request hostname.
    Example: v1.api.example.com
    """

class QueryParameterVersioning(BaseVersioning):
    """
    Version determined from query parameters.
    Example: /books/?version=1.0
    """

Versioning Configuration

Configure versioning in Django settings:

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning',
    'DEFAULT_VERSION': '1.0',
    'ALLOWED_VERSIONS': ['1.0', '1.1', '2.0'],
    'VERSION_PARAM': 'version',
}

URL Path Versioning Example

# urls.py
from django.urls import path, include

urlpatterns = [
    path('v1/', include('myapp.urls', namespace='v1')),
    path('v2/', include('myapp.urls', namespace='v2')),
]

# Using in views
class BookViewSet(ModelViewSet):
    def list(self, request):
        version = request.version
        
        if version == '1.0':
            # Version 1.0 response format
            serializer = BookSerializerV1(books, many=True)
        elif version == '2.0':
            # Version 2.0 response format  
            serializer = BookSerializerV2(books, many=True)
        
        return Response(serializer.data)

Accept Header Versioning Example

# Client request:
# Accept: application/json; version=1.1

class BookAPIView(APIView):
    def get(self, request):
        version = request.version  # '1.1'
        
        books = Book.objects.all()
        if version == '1.0':
            data = [{'id': b.id, 'title': b.title} for b in books]
        else:  # version >= 1.1
            data = [{'id': b.id, 'title': b.title, 'author': b.author} for b in books]
        
        return Response({'books': data, 'version': version})

Custom Versioning Scheme

from rest_framework.versioning import BaseVersioning
from rest_framework import exceptions

class CustomHeaderVersioning(BaseVersioning):
    """
    Custom versioning based on X-API-Version header.
    """
    def determine_version(self, request, *args, **kwargs):
        version = request.META.get('HTTP_X_API_VERSION', self.default_version)
        
        if not self.is_allowed_version(version):
            raise exceptions.NotAcceptable(
                'Invalid API version. Supported versions: {}'.format(
                    ', '.join(self.allowed_versions)
                )
            )
        
        return version

Working with File Uploads

class FileUploadView(APIView):
    def post(self, request):
        # Access uploaded files
        uploaded_file = request.data.get('file')
        
        if uploaded_file:
            # Process the file
            file_content = uploaded_file.read()
            file_name = uploaded_file.name
            file_size = uploaded_file.size
            
            return Response({
                'message': 'File uploaded successfully',
                'filename': file_name,
                'size': file_size
            })
        
        return Response(
            {'error': 'No file provided'}, 
            status=status.HTTP_400_BAD_REQUEST
        )

Request Method Override

from rest_framework.request import override_method

class CustomView(APIView):
    def post(self, request):
        # Temporarily override method for internal processing
        with override_method(request, 'PUT'):
            # Process as if it were a PUT request
            return self.handle_update(request)
    
    def handle_update(self, request):
        # Method sees request.method as 'PUT'
        return Response({'method': request.method})

Testing with Forced Authentication

from rest_framework.request import ForcedAuthentication
from rest_framework.test import APIRequestFactory

# In tests
factory = APIRequestFactory()
user = User.objects.create_user('testuser')

# Create request with forced authentication
request = factory.post('/api/books/', {'title': 'Test Book'})
force_auth = ForcedAuthentication(user)

# Apply authentication
request.user = user
request.auth = None

Install with Tessl CLI

npx tessl i tessl/pypi-djangorestframework

docs

auth-permissions.md

content-negotiation.md

decorators.md

fields-validation.md

generic-views.md

index.md

pagination-filtering.md

request-response.md

routers-urls.md

serializers.md

status-exceptions.md

testing.md

views-viewsets.md

tile.json