I'm trying to make the server only return documents which were created by the logged-in user. I'm following this post and this one but the logged in user is returned as "Anonymous".
I am using Django Rest Framework with Django Rest Auth, and a custom user, but no other customisations.
Django 2.0.10
Here is my viewset in api.py:
from rest_framework import viewsets, permissions
from .models import List, Item
from .serializers import ListSerializer, ItemSerializer
class ListViewSet(viewsets.ModelViewSet):
# queryset = List.objects.all()
# permission_classes = [permissions.AllowAny, ]
model = List
serializer_class = ListSerializer
def get_queryset(self):
print(self.request.user)
return List.objects.filter(created_by=self.request.user)
def pre_save(self, obj):
obj.created_by = self.request.user
Settings.py:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
AUTH_USER_MODEL = 'users.CustomUser'
# django rest auth
ACCOUNT_AUTHENTICATION_METHOD = 'email'
I've read other posts about this issue which all talk about custom middleware, but I haven't created a custom middleware unless django-rest-framework or django-rest-auth is actually such a thing? And the posts don't seem to show how to get the user in the viewset.
Also the posts are old, so Django has probably changed.
From this post I tried the following, it did not work:
class ListViewSet(viewsets.ModelViewSet):
# queryset = List.objects.all()
# permission_classes = [permissions.AllowAny, ]
model = List
serializer_class = ListSerializer
def get_queryset(self):
print(self.request.user)
self.request.custom_prop = SimpleLazyObject(lambda: get_actual_value(self.request))
print(self.request.custom_prop)
return List.objects.filter(created_by=self.request.user)
def pre_save(self, obj):
obj.created_by = self.request.user
I'd be very grateful for any help. This is such a basic requirement, it ought to be simple but I'm absolutely stuck.
Edit: in case it helps somebody else, below is my working code based on Lucas Weyne's answer. I extended the logic so the user sees all Lists they created, AND all lists that have the flag "is_public", but can only modify lists they created.
I have set permissions.AllowAny because I want users who are not logged in to see public lists. In the client code, I check whether the user is logged in, and if they are I send the token in the query header.
Note the use of the Q object which is the easiest way I could find to return records that satisfy either of the two conditions. I couldn't find any mention of the Q object in the Django rest framework docs. I eventually found it in the main Django docs.
This all seems to work but if you spot something I've done wrong, please comment! I am not sure if this is the Django way to approach the requirement but I like the fact that the permissions logic is all in one place.
from rest_framework import viewsets, permissions
from .models import List
from .serializers import ListSerializer
from django.db.models import Q
class ListViewSet(viewsets.ModelViewSet):
"""
ViewSet for lists. Before allowing any operation, the user's status is checked.
Anybody can view a public list.
A logged-in user can create lists.
A logged-in user can view, edit and delete the lists they created.
"""
permission_classes = [permissions.AllowAny, ]
model = List
serializer_class = ListSerializer
def get_queryset(self):
# restrict any method that can alter a record
restricted_methods = ['POST', 'PUT', 'PATCH', 'DELETE']
if self.request.method in restricted_methods:
# if you are not logged in you cannot modify any list
if not self.request.user.is_authenticated:
return List.objects.none()
# you can only modify your own lists
# only a logged-in user can create a list and view the returned data
return List.objects.filter(created_by=self.request.user)
# GET method (view list) is available to owner and for public lists
if self.request.method == 'GET':
if not self.request.user.is_authenticated:
return List.objects.filter(is_public__exact=True)
return List.objects.filter(Q(created_by=self.request.user) | Q(is_public__exact=True))
# explicitly refuse any non-handled methods
return List.objects.none()
def pre_save(self, obj):
obj.created_by = self.request.user