I'm trying to create a new entry in my database with Django REST framework and I am able to successfully send a request however validated_data is empty:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from rest_framework import viewsets
from django.shortcuts import get_object_or_404, get_list_or_404
from .models import GetPurchases, CarFax
from .serializers import PurchasesSerializer
from .serializers import CarFaxSerializer
from rest_framework.response import Response
# Create your views here.
class GetCarFax(viewsets.ModelViewSet):
''' This view will be used for POSTing new carfax reports to the database '''
queryset = CarFax.objects.all()
serializer_class = CarFaxSerializer
# authentication_classes = []
permission_classes = []
#print('TEST')
def list(self, request):
# accessed at url: ^api/v1/carfax/$
queryset = CarFax.objects.all()
serializer = CarFaxSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None, *args, **kwargs):
# accessed at url: ^api/v1/retrieve/{pk}/$
queryset = CarFax.objects.all()
record = get_list_or_404(queryset, vin__exact=pk)
serializer = CarFaxSerializer(record, many=True)
return Response(serializer.data)
def create(self, request, **validated_data):
print('TEST')
print(request.data)
print(validated_data)
serializer = CarFaxSerializer(CarFax.objects.create(**validated_data))
headers = self.get_success_headers(serializer.data)
print(serializer.data)
print(headers)
return Response(serializer.data, headers=headers)
serializers.py
class CarFaxSerializer(serializers.ModelSerializer):
class Meta:
model = CarFax
fields = ('vin', 'structural_damage', 'total_loss',
'accident', 'airbags', 'odometer', 'recalls',
'last_updated')
When I print request.data I can see the data I sent. However validated_data is empty, which is the dict I'm using to create the models.
The accepted answer is a nice refactoring but it doesn't answer the question, and thus may not help future readers (like myself, who just went through this). The real reason validated data was empty was because the when you override .create on the ModelViewSet you wipe out the serializer model field bindings and have to explicitly specify them as you would on a regular serializer.
CarFax serializer should have been defined like so:
class CarFaxSerializer(serializers.ModelSerializer):
vin = serializers.CharField(23),
structural_damage = serializers.BooleanField(),
total_loss = serializers.BooleanField(),
accident = serializers.BooleanField(),
airbags = serializers.BooleanField(),
odometer = serializers.IntegerField(),
recalls = serialziers.BooleanField(),
last_updated = serializers.DateField(),
class Meta:
model = CarFax
fields = ('vin', 'structural_damage', 'total_loss',
'accident', 'airbags', 'odometer', 'recalls',
'last_updated')
First of all, your GetCarFax class seems like messy :( because you'd override most of the methods, but it doesn't provide any advantages to the view. So change your GetCarFax view class to below,
class GetCarFax(viewsets.ModelViewSet):
"""
This view will be used for POSTing new carfax reports to the database
"""
queryset = CarFax.objects.all()
serializer_class = CarFaxSerializer
# authentication_classes = []
permission_classes = []
lookup_field = 'myfieldname'
This few lines of code will handle all the CRUD operations for you :)
Related
class SalonCarDetailsSerializer(serializers.ModelSerializer):
salon = PrimaryKeyRelatedField(queryset=Salon.objects.filter(owner=?))
class Meta:
model = SalonCarDetails
fields = ["salon", "car", "price", "number_of_cars"]
CurrentUserDefault() doesn't works
Well, you could write your own PrimaryKeyRelated field like that:
class SalonKeyRelatedField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
qs = super().get_queryset()
request = self.context.get('request')
return qs
then you can filter qs by request.user, this will be called only on POST and PUT requests. You can then include it in your serializer
salon = SalonKeyRelatedField()
don't forget to include salon in your fields
I wish I could see your views.py, but anyway I make some assumptions about it and put it in the class implementation scenario.
#
### views.py
#
# Django native libraries
from rest_framework.serializers import Serializer
from rest_framework import viewsets, mixins, status
from django.db.models import Q
# your serializer and model
from .serializers import YourSalonCarSerializer
from .models import SalonCarDetails
class YourCustomViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
queryset = SalonCarDetails.objects.all()
serializer_class = YourSalonCarSerializer
def get_queryset(self):
if self.request.user.is_anonymous:
return []
lookups = Q(user=self.request.user)
queryset = self.queryset.filter(lookups)
Can I use Serializer just for validation purpose?
for example if I had CommentSerializer as
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Is it as as per motive of DRF to use this serializer just for validation of request or some data without refering to any Django models? For example:
from rest_framework.views import APIView
from rest_framework.response import Response
class MyAPIView(APIView):
def post(self, request, format=None):
serializer = CommentSerializer(data=dict(request.data))
if serializer.is_valid():
validated_data = serializer.validated_data
# do something with validated_data
# return some data as Response()
or am I fighting against the framework guidance?
In my Django project, I have a statement app which just stores the statements of the registered users. I was experimenting with it to learn Django and at some point, I asked myself how to show only the statements of the current user. So, I filtered the queryset based on the current user and then I have got this error message (see title), when I wanted to see the statements.
My views.py:
from statements.models import Statement
from statements.serializers import StatementSerializer, UserSerializer
from rest_framework import generics
from django.contrib.auth.models import User
from rest_framework import permissions
from statements.permissions import IsOwnerOrReadOnly
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.views import APIView
from rest_framework.authtoken.models import Token
from rest_framework.authentication import TokenAuthentication
#api_view(['GET'])
def api_root(request, format=None):
return Response({'links':{
'users': reverse('user-list', request=request, format=format),
'statements': reverse('statement-list', request=request, format=format)
}})
class StatementList(generics.ListCreateAPIView):
#queryset = Statement.objects.all() <--- with this, everything was fine but I wanted to get only the statements of the current user
serializer_class = StatementSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
# I've overridden this method so that only the statements of the current user should be displayed (BUT this caused the error)
def get_queryset(self):
user = self.request.user
return Statement.objects.filter(owner=user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response({'statements': serializer.data})
class StatementDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Statement.objects.all()
serializer_class = StatementSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
My models.py:
from django.db import models
# Create your models here.
class Statement(models.Model):
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=100, blank=False)
owner = models.ForeignKey('auth.User', related_name='statements', on_delete=models.CASCADE)
class Meta:
ordering = ('created',)
My serializers.py:
class StatementSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Statement
fields = ('url','id', 'text', 'owner')
class UserSerializer(serializers.HyperlinkedModelSerializer):
statements = serializers.HyperlinkedRelatedField(many=True, view_name='statement-detail', read_only=True)
class Meta:
model = User
fields = ('url', 'id', 'username', 'statements')
So, I also read the other stackoverflow posts regarding this type error, but they were not helpful.
The problem is in StatementList View.
If the request is unauthenticated the default value of request.user is an instance of django.contrib.auth.models.AnonymousUser.
So, you can check if the user is authenticated before doing Statement.objects.filter(owner=user) query.
# views.py
class StatementList(generics.ListCreateAPIView):
#queryset = Statement.objects.all() <--- with this, everything was fine but I wanted to get only the statements of the current user
serializer_class = StatementSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
# I've overridden this method so that only the statements of the current user should be displayed (BUT this caused the error)
def get_queryset(self):
user = self.request.user
# check if the user is authenticated
if user.is_authenticated():
return Statement.objects.filter(owner=user)
return Statement.objects.none()
# END
Or you can change the permission_classes to permissions.IsAuthenticated, it will make sure that only authenticated user can access the endpoint.
Make sure in settings.py
DEBUG = True
I'm following this tutorial (Django Rest Framework) and I'm facing a problem when I try to use serializers. It returns a empty result, instead the 49086 records that it should to return. My query it's ok because when I try to show the data without serializers it's shows these data. Please, what I'm doing wrong?
models.py
# coding=utf-8
from __future__ import unicode_literals
from django.db import models
class Workers(models.Model):
chapa = models.IntegerField(primary_key=True)
emp_cod = models.IntegerField(primary_key=False)
class Meta:
managed = False
db_table = 'WORKERS'
serializers.py
from rest_framework import serializers
from .models import Workers
class WorkersSerializer(serializers.ModelSerializer):
class Meta:
model = Workers
fields = '__all__'
views.py
...
#api_view(['GET'])
#permission_classes((permissions.AllowAny,))
def get_all_workers(request):
data = Workers.objects.using('rh').all().order_by('emp_cod')
print(data.count()) # Returns 49086
serializer = WorkersSerializer(data)
print(serializer.data) # Returns {}
json = JSONRenderer().render(serializer.data)
return Response(json) # Returns Django Rest standard page with "{}" data
You should use many=True serializer's argument to serialize multiple objects. Also you can pass serializer.data directly as Response argument:
#api_view(['GET'])
#permission_classes((permissions.AllowAny,))
def get_all_workers(request):
data = Workers.objects.using('rh').all().order_by('emp_cod')
serializer = WorkersSerializer(data, many=True)
return Response(serializer.data)
Since your view return so many objects at once, I suggest you to add pagination:
from rest_framework.pagination import PageNumberPagination
#api_view(['GET'])
#permission_classes((permissions.AllowAny,))
def get_all_workers(request):
data = Workers.objects.using('rh').all().order_by('emp_cod')
paginator = PageNumberPagination()
paginator.page_size = 10
result_page = paginator.paginate_queryset(data, request)
serializer = WorkersSerializer(result_page, many=True)
return Response(serializer.data)
I am creating a Notification apps by Django Rest Framework which users can MARK AS READ ALL notification by using PATCH API in frontend. How can I Bulk Update data can do this task.
This serializer and viewset below just for PATCH only one notification object, but I want to do it all with Notifications which have field is_read = False
Edited with the right way
My Serializers:
class NotificationEditSerializer(ModelSerializer):
class Meta:
model = Notification
fields = (
'id',
'is_read'
)
My Viewset:
from rest_framework.response import Response
class NotificationListAPIView(ReadOnlyModelViewSet):
queryset = Notification.objects.all()
permission_classes = [AllowAny]
serializer_class = NotificationEditSerializer
lookup_field = 'id'
#list_route(methods=['PATCH'])
def read_all(self, request):
qs = Notification.objects.filter(is_read=False)
qs.update(is_read=True)
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)
My URL:
from rest_framework import routers
router.register(r'notifications/read_all', NotificationListAPIView)
You can try to use list_route for example:
from rest_framework.response import Response
from rest_framework.decorators import list_route
class NotificationListAPIView(ReadOnlyModelViewSet):
#YOUR PARAMS HERE
#list_route()
def read_all(self, request):
qs = Notification.objects.filter(is_read=False)
qs.update(is_read=True)
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)
the api is available by ^YOUCURRENTURL/read_all/$ more details marking-extra-actions-for-routing
NOTE! since DRF 3.10 #list_route() decorator was removed, you should use #action(detail=False) instead, I used #action(detail=False, methods=['PATCH']) to bulk patch, for example Thank you #PolYarBear