Writing Clean and Reusable Django APIs: Mastering Mixins

Meda MondongaMeda Mondonga
3 min read

Today, I'd like to share a neat little trick I discovered to simplify and clean up my Django REST Framework (DRF) views by leveraging mixins. If you're like me, and you hate seeing repetitive code everywhere, you're in the right place!

The Problem

When building APIs, especially CRUD operations (Create, Read, Update, Delete), it's easy to repeat yourself. You often end up writing very similar views for each model, creating a mess that's hard to maintain. My code was starting to look repetitive, bloated, and just not very clean.

The Solution: Mixins

Mixins in DRF let you separate your code into small, reusable pieces. By combining DRF's built-in mixins (ListModelMixin, CreateModelMixin, RetrieveModelMixin, etc.) and generic views (GenericAPIView), you can write super clean and concise code.

But I wanted to push it further—so I built customized, reusable view functions.

My Approach: Customized CRUD Functions

I created a single file called mixins.py inside my Django app to centralize all CRUD actions. Here's the simplified concept:

Customized Create View:

def create_customized(model, serializer):
    """
    This function get a model and a serializer class and return the objet created
    """
    class CustomCreateView(CreateModelMixin, GenericAPIView):
        """
        Create something
        """
        queryset = model.objects.all()
        serializer_class = serializer

        def post(self, request, *args,**kwargs):
            """
            post something
            """
            response = self.create(request, *args, **kwargs)

            if response.status_code == status.HTTP_201_CREATED:
                return Response({
                    "message": f"{CREATED}"
                }, status=status.HTTP_201_CREATED)
            return response
    return CustomCreateView

Customized List View:

def list_customized(model, serializer):
    """
    This function get a model and a serializer class and return list of objet
    """
    class ListCustomView(ListModelMixin, GenericAPIView):
        """
        List something
        """
        queryset = model.objects.all()
        serializer_class = serializer

        def get(self, request, *args, **kwargs):
            """
            get all things
            """
            return self.list(request, *args, **kwargs)
    return ListCustomView

Customized Detail, Update, and Delete View:

For detail, update, and delete, I combined multiple mixins into one:

def detail_update_delete_customized(model, serializer):
    """
    This function get a model and a serializer class and return detail, update and delete object
    """
    class DetailCustomView(RetrieveModelMixin, GenericAPIView):
        """
        Detail of somthing
        """
        queryset = model.objects.all()
        serializer_class = serializer

        def get(self, request, *args, **kwargs):
            """
            Get one thing
            """
            if kwargs.get('pk'):
                return self.retrieve(request, *args, **kwargs)
    class UpdateCustomView(UpdateModelMixin, GenericAPIView):
        """
        Update something
        """
        queryset = model.objects.all()  
        serializer_class = serializer

        def put(self, request, *args, **kwargs):
            """
            update all files of something's database
            """
            response = self.update(request, *args, **kwargs)
            if response.status_code == status.HTTP_200_OK:
                return Response({
                    "message":f"{MODIFIED}"
                }, status=status.HTTP_200_OK)
            return response

        def patch(self, request, *args, **kwargs):
            """
            Update just part of something's database
            """
            response = self.partial_update(request, *args, **kwargs)
            if response.status_code == status.HTTP_200_OK:
                return Response({
                    "message": f"{MODIFIED}"
                }, status=status.HTTP_200_OK)
            return response
    class DeleteCustomView(DestroyModelMixin, GenericAPIView):
        """
        Delete one thing
        """
        queryset = model.objects.all()
        serializer_class = serializer

        def delete(self, request, *args,**kwargs):
            """
            Delete a store in database
            """
            response = self.destroy(request, *args, **kwargs)

            if response.status_code == status.HTTP_204_NO_CONTENT:
                return Response({
                    "message": f"{DELETED}"
                }, status=status.HTTP_204_NO_CONTENT)
            return response
    class CombineActionView(UpdateCustomView,
                     DetailCustomView,
                     DeleteCustomView):
        """
        all action
        """
    return CombineActionView

The Result

Now, every time I need CRUD operations for a new model, it's just a matter of calling these functions and passing the model and serializer as parameters. No more repetitive code!

urlpatterns = [
    #endpoints for store
     path("boutique/new/",
         create_customized(Boutique, BoutiqueSerializer).as_view(),
         name="creation-boutique"),
...
]

Benefits

  • Cleaner code: Easier to read and maintain.

  • Reusable logic: Write once, use everywhere.

  • Faster development: Spend less time copying and pasting.

Conclusion

Mixins are powerful allies when you want to keep your DRF code clean, readable, and reusable. Try them out, and let me know how it goes!

What's your favorite tip to write cleaner Django code? Share in the comments!

0
Subscribe to my newsletter

Read articles from Meda Mondonga directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Meda Mondonga
Meda Mondonga