Django REST Filtering Tutorial - Filter with Current User (Part II)
Tags: django, djangorestframework, filter, search
Published: 2022-04-18 20:05:20

---
title: Django REST Filtering Tutorial - Filter with Current User (Part II)
published: true
date: 2022-03-24 00:00:00 UTC
tags: django, djangorestframework, filter, search
canonical_url: https://tech.serhatteker.com/post/2022-03/django-rest-filtering-tutorial-part-2/
---

## Entrée

We've seen in [part I] how to setup our [repo], sending HTTP requests and some fundamental concepts about [Django REST] **filtering**.

In this second part we will limit our views/querysets/objects with `user`, more precisely with current authenticated user.

The *authors* list endpoint — `/authors/`, displays all items regardless of whether they belong to current user or not.

```bash
$ http :8000/authors

[
    {
        "first_name": "Name1",
        "full_name": "Name1 Surname1",
        "id": 1,
        "last_name": "Surname1",
        "user": 1
    },
    {
        "first_name": "Name2",
        "full_name": "Name2 Surname2",
        "id": 2,
        "last_name": "Surname2",
        "user": null
    },
    {
        "first_name": "Name3",
        "full_name": "Name3 Surname3",
        "id": 3,
        "last_name": "Surname3",
        "user": 3
    }
]
```

How can we change that behavior in order to return items only owned by current authenticated user?

> NOTE
>
> You can find the code we're working on this tutorial series in this repository: [django-rest-filtering-tutorial](https://github.com/SerhatTeker/django-rest-filtering-tutorial)
>
> You don't need to download and setup the repository in order to follow this series, however I'd like to encorauge to do so, since while you play you learn better as always.


## Current User

This is one common filter usage which is filtering the queryset to ensure only relevant results to the current authenticated user who makes the request are returned.

For this purpose we will override default [get_queryset] as we've seen in [default filtering] in detail.

Let's filter **authors** list endpoint with the *current user*:

```python
from rest_framework import generics

from src.authors.models import Author
from src.authors.serializers import AuthorSerializer


class AuthorListView(generics.ListAPIView):
    serializer_class = AuthorSerializer

    def get_queryset(self):
        """
        This view returns a list of all the authors for the currently
        authenticated user.

        Returns empyt list if user Anonymous
        """
        user = self.request.user

        if not user.is_anonymous:
            return Author.objects.filter(user=user)

        return Author.objects.none()
```

> NOTE
>
> In our [repo](https://github.com/SerhatTeker/django-rest-filtering-tutorial) we use `ModelViewSet` for **authors** endpoint. You can update it like above.

You can look at related `AuthorSerializer` and `Author` model in the [repo], however for easy access I put them there as well. In order to examine them just expand the below section.

{% collapsible AuthorSerializer and Author model %} 

`AuthorSerializer` from `src/authors/serializers.py`:

```python
# src/authors/serializers.py
from rest_framework import serializers

from src.authors.models import Author


class AuthorSerializer(serializers.ModelSerializer):

    class Meta:
        model = Author
        fields = ("id", "user", "first_name", "last_name", "full_name")
        read_only_fields = ("full_name",)
```

`Author` model from `src/authors/model.py`:

```python
# src/authors/model.py
from django.db import models
from django.conf import settings

USER = settings.AUTH_USER_MODEL


class Author(models.Model):
    """
    Author entity

    Provides first_name and last_name, since he/she can write unter a Pen Name
    """
    user = models.ForeignKey(to=USER, on_delete=models.DO_NOTHING, blank=True, null=True)
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"
```

{% endcollapsible %}

Now we can make a successful request by including the username and password of one of the users we created in `populate_db.py`:

```bash
$ http -a "User1:1234" ":8000/authors/"

[
    {
        "first_name": "Name1",
        "full_name": "Name1 Surname1",
        "id": 1,
        "last_name": "Surname1",
        "user": 1
    },
]
```

As you can recognize now it returns only one object instead of three.


And if you we make a request as **anonymous** or unauthenticated user, we will get:

```bash
$ http ":8000/authors/"

[]
```


### User Authentication and Permission

It's usually better idea to separate `authentication` and `permission` layer/logic. For this goal you can use `authentication_classes` and `permission_classes` on per-view or per-viewset basis;

```python
from rest_framework import generics
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated

from src.authors.models import Author
from src.authors.serializers import AuthorSerializer


class AuthorListView(generics.ListAPIView):
    serializer_class = AuthorSerializer
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

    def get_queryset(self):
        """
        This view returns a list of all the authors for the currently
        authenticated user.
        """
        user = self.request.user

        return Author.objects.filter(user=user)
```

Another method is setting the default authentication globally, using the `DEFAULT_AUTHENTICATION_CLASSES` setting:

```python
# settings.py
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.BasicAuthentication",
        "rest_framework.authentication.SessionAuthentication",
    ]
}
```

> INFO
>
> In the [repo](https://github.com/SerhatTeker/django-rest-filtering-tutorial) these are disabled, you can set them globally if you comment out the related lines at the end of the **setting.py** file.


And now our view will look cleaner like this:

```python
# src/authors/views.py
from rest_framework import generics

from src.authors.models import Author
from src.authors.serializers import AuthorSerializer


class AuthorListView(generics.ListAPIView):
    serializer_class = AuthorSerializer

    def get_queryset(self):
        """
        This view returns a list of all the authors for the currently
        authenticated user.
        """
        return Author.objects.filter(user=self.request.user)
```

For more info about `authentication` and `permissions` you can look at:
- [drf authentication]
- [drf permissions]

## Custom FilterBackend

There is another method which is that we can also provide our own generic filtering backend.

To do so override `BaseFilterBackend` class, and override the `.filter_queryset` method. The method should return a new, filtered queryset.

As well as allowing clients to perform searches and filtering, generic filter backends can be useful to restrict which objects should be visible to any given request or user.

Let's write `IsOwnerFilterBackend` for our current user logic:

```python
# src/authors/filters.py
from django_filters import rest_framework as filters


class IsOwnerFilterBackend(filters.BaseFilterBackend):
    """
    Filter that only allows users to get their own objects
    """
    def filter_queryset(self, request, queryset, view):
        return queryset.filter(owner=request.user)

```

Add this filter to our `AuthorListView`:

```python
# src/authors/views.py
from rest_framework import generics
from django_filters import rest_framework as filters

from src.authors.models import Author
from src.authors.serializers import AuthorSerializer
from src.authors.filters import IsOwnerFilterBackend


class AuthorListView(generics.ListAPIView):
    serializer_class = AuthorSerializer
    queryset = Author.objects.all()
    filter_backends = (IsOwnerFilterBackend,)
    filterset_fields = ("user",)
```

Now if we send request we only receive related authors to the user.

One side note to this method; as we've already seen above, we can achieve the same behavior by overriding `get_queryset()` on the view, but using a filter backend allows us to more easily add this restriction to multiple views, or to apply it across the entire **API**.

We can also make this filter backend as our global default backend:

```python
# settings.py
REST_FRAMEWORK = {
    "DEFAULT_FILTER_BACKENDS": ["src.authors.filters.IsOwnerFilterBackend"],
}
```

## Conclusion

In this tutorial we learned how to filter the queryset to ensure only relevant results to the current authenticated user who makes the request are returned.

See you in the next part of this tutorial series.

All done!




[repo]: https://github.com/SerhatTeker/django-rest-filtering-tutorial
[part I]: https://tech.serhatteker.com/post/2022-03/django-rest-filtering-tutorial-part-1/
[Django REST]: https://www.django-rest-framework.org/
[django-filter]: https://django-filter.readthedocs.io/en/main/
[default filtering]: https://tech.serhatteker.com/post/2022-03/django-rest-filtering-tutorial-part-1/#default-filtering
[get_queryset]: https://github.com/encode/django-rest-framework/blob/0323d6f8955f987771269506ca5da461e2e7a248/rest_framework/generics.py#L48-L73
[drf authentication]: https://www.django-rest-framework.org/api-guide/authentication/
[drf permissions]: https://www.django-rest-framework.org/api-guide/permissions/