Enable Partial Updates in Django REST
Tags: python, django, api, rest
Published: 2020-11-23 13:22:07

---
title: Enable Partial Updates in Django REST
published: true
date: 2020-09-17 00:00:00 UTC
tags: python, django, api, rest
canonical_url: https://tech.serhatteker.com/post/2020-09/enable-partial-update-drf/
---

## Background

There are 2 `HTTP Request Methods` to update the data:

> **PUT**
>
> The PUT method requests that the enclosed entity be stored under the supplied
> URI. If the URI refers to an already existing resource, it is modified; if the
> URI does not point to an existing resource, then the server can create the
> resource with that URI.
>
> **PATCH**
>
> The PATCH method applies partial modifications to a resource.

As we can see `PUT` updates the **every** resource of **entire** data, whereas `PATCH`
updates the **partial** of data.

In other words: We can say `PUT` **replace** where `PATCH` **modify**.

So in this article we are going to look for `PATCH` method.

If you want more information about **HTTP** you can look at: [Hypertext Transfer Protocol - WIKI].

## Entrée

Let's assume you have a simple `ModelViewSet` for your [Django Rest Framework], and you want to allow users to update partial data field(s) of this model. How can you achieve that?

```python
# views.py
from rest_framework import permissions, viewsets


class TodoViewSet(viewsets.ModelViewSet):
    queryset = Task.objects.all()
    serializer_class = TaskSerializer
```

After some lookup for the __doc__ and __core__ of [DRF] I found that `ModelViewSet` inherits some `Mixins` __parent classes__ and you have tooverride default __partial__ `kwargs` in `UpdateModelMixin`.


```python
# viewsets.py
# https://github.com/encode/django-rest-framework/blob/91916a4db1/rest_framework/viewsets.py

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass
```

You can see it below on line 9.

```python
# https://github.com/encode/django-rest-framework/blob/91916a4db14cd6a06aca13fb9a46fc667f6c0682/rest_framework/mixins.py#L64


class UpdateModelMixin:
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)
```

## Solution

So you simply override `update` function like below:

```python
# views.py
from rest_framework import permissions, viewsets


class TodoViewSet(viewsets.ModelViewSet):
    queryset = Task.objects.all()
    serializer_class = TaskSerializer

    def update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return super().update(request, *args, **kwargs)
```

## Clean Solution

In order to be *clean* side of programming it would be better to separate it and use it as `Mixin` for maintenance and testing:

```python
# utils.py


class EnablePartialUpdateMixin:
    """Enable partial updates

    Override partial kwargs in UpdateModelMixin class
    https://github.com/encode/django-rest-framework/blob/91916a4db14cd6a06aca13fb9a46fc667f6c0682/rest_framework/mixins.py#L64
    """
    def update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return super().update(request, *args, **kwargs)
```

Use it like below:

```python
# views.py
from rest_framework import permissions, viewsets
from .utils import EnablePartialUpdateMixin


class TodoViewSet(EnablePartialUpdateMixin, viewsets.ModelViewSet):
    queryset = Task.objects.all()
    serializer_class = TaskSerializer
```


## Appendix

You can find `model` and `serializer` class of the **TodoViewSet** below.

Also you can look up for _full_ code detail in this repo: [YADRTA]

```python
# models.py


class Task(BaseModelMixin):
    STATES = (
        ("todo", "Todo"),
        ("wip", "Work in Progress"),
        ("suspended", "Suspended"),
        ("waiting", "Waiting"),
        ("done", "Done"),
    )

    title = models.CharField(max_length=255, blank=False, unique=True)
    description = models.TextField()
    status = models.CharField(max_length=15, choices=STATES, default="todo")
    tag = models.ForeignKey(to=Tag, on_delete=models.DO_NOTHING)
    category = models.ForeignKey(to=Category, on_delete=models.DO_NOTHING)

    class Meta:
        ordering = ("title",)

    def __str__(self):
        return f"{self.created_by}:{self.title}"
```

```python
# serializers.py
from rest_framework import serializers


class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = base_model_mixin_fields + [
            "title",
            "description",
            "status",
            "tag",
            "category",
        ]
```

All done!




---
**Changelog**

- 2020-10-26 : Added **Background** subsection




[Django Rest Framework]: https://www.django-rest-framework.org/
[DRF]: https://www.django-rest-framework.org/
[YADRTA]: https://github.com/SerhatTeker/yadrta
[Hypertext Transfer Protocol - WIKI]: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol