Django REST framework

  • Fix Django REST framework AttributeError: 'Request' object has no attribute 'accepted_renderer'

    Below are ways to fix AttributeError: 'Request' object has no attribute 'accepted_renderer' error.   1. Install pyyaml pip install pyyaml   OR   2. Revert Django REST Framework (DRF) pip install djangorestframework==3.8.0   OR   3. Ignore decode on your ViewSet class MyCoolViewset(viewsets.ModelViewSet): def _clean_data(self, data): if isinstance(data, bytes): data = data.decode(errors='ignore') return super(MyCoolViewset, self)._clean_data(data)  
  • Fix Django Rest Framework Tracking (drf-tracking) errors with Django model FileField

    I was working recently on logging Django Rest Framework (DRF) requests and responses and also creating a download endpoint so I can protect files from being accessed by other users with the ability log requests to see who's downloading what. So I came across this awesome DRF plugin called drf-tracking. It so easy to use that you just need to add rest_framework_tracking.mixins.LoggingMixin to your existing DRF views like the code below: from rest_framework import generics from rest_framework.response import Response from rest_framework_tracking.mixins import LoggingMixin class LoggingView(LoggingMixin, generics.GenericAPIView): def get(self, request): return Response('with logging') And you should see the logs in your django admin page. Upon testing, I noticed that I get an exception when I'm sending a POST request with a file and also have the decoding error on my download api. So I have to override the mixin methods. from rest_framework import viewsets, renderers from rest_framework.response import Response from rest_framework.decorators import detail_route from django.http import FileResponse, HttpResponse from rest_framework_tracking.mixins import LoggingMixin from .models import File class BinaryFileRenderer(renderers.BaseRenderer): media_type = 'application/octet-stream' format = None charset = None render_style = 'binary' def render(self, data, media_type=None, renderer_context=None): return data class FileViewSet(LoggingMixin, viewsets.ModelViewSet): queryset = File.objects.all() # Override finalize_response to check if response is FileResponse then I will return the rendered_content response without # processing it to avoid decode exceptions def finalize_response(self, request, response, *args, **kwargs): if response.__class__.__name__ == 'FileResponse': mixinResponse = super(FileViewSet, self).finalize_response(request, response, *args, **kwargs) if hasattr(response, 'rendered_content'): rendered_content = response.rendered_content else: rendered_content = response.getvalue() return HttpResponse(rendered_content) return super(FileViewSet, self).finalize_response(request, response, *args, **kwargs) # Override _clean_data to decode data with ignore instead of replace to ignore # errors in decode so when the logger inserts the data to db, it will not hit # any decoding/encoding issues def _clean_data(self, data): if isinstance(data, bytes): data = data.decode(errors='ignore') return super(FileViewSet, self)._clean_data(data) @detail_route(methods=['get'], renderer_classes=(BinaryFileRenderer,)) def download(self, request, pk=None): queryset = File.objects.get(id=pk) documentFile = queryset.document file_handle = # send file response = FileResponse(file_handle) return response  
  • Saving foreign key ID with Django REST framework serializer

    If you are using Django REST framework on serving your APIs, you probably did the below code in returning the related object in your serializer. class MyTableSerializer(serializers.ModelSerializer): user = UserSerializer(many=False, read_only=True) class Meta: fields = '__all__' model = MyTable But doing this will not allow your API to pass the foreign key id. Instead, you need to include the field name in the serializer like the code below: class MyTableSerializer(serializers.ModelSerializer): user = UserSerializer(many=False, read_only=True) user_id = serializers.IntegerField(write_only=True) class Meta: fields = '__all__' model = MyTable