django rest framework cannot POST data - django

I implemented a basic rest api with the django rest framework. It works perfectly using the browsable api or communicating to it with requests. Next step would be submitting data to the rest api.
Here is what I have done so far.
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.AllowAny',),
'PAGINATE_BY': 10
}
[UPDATE:]
models.py
class Request(models.Model):
name = models.TextField()
def save(self, *args, **kwargs):
super(Request, self).save(*args, **kwargs) # Call the "real" save() method.
serializers.py
class RequestSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Request
fields = ('id', 'name')
views.api
class RequestsViewSet(viewsets.ModelViewSet):
queryset = Request.objects.all()
serializer_class = RequestSerializer
Using the browsable api I see that those are the options supported:
Allow: GET, HEAD, OPTIONS
Obviously, POST (and also PUT) is missing.
What I am doing wrong?
Thanks!

Solved it by adding the post method to the modelviewset (in the view):
def post(self, request, format=None):
...
Thanks for helping!

Well, I think you only need to call save method on the model object to persist the object in the database.
First, import model to the view, instantiate a model object in the view, then call save method on the newly created object. If you have model connected to the backend, that will persist your changes.
models.py
class YourModel(models.Model):
name = models.CharField()
views.py
from models import YourModel
def yourView(request):
yourObject = YourModel(name='John')
yourObject.save()
...
Check also Django documentation for models here

Related

How to use allow only POST in Django REST API Viewset

I went through the django rest framework documentation on Viewsets and dont seem to understand how to allow only POST requests on the browser API.
Viewset
class EmailViewSet(viewsets.ModelViewSet):
queryset = models.Email.objects.all()
serializer_class = serializers.EmailSerializer
Model
class Email(models.Model):
email = models.EmailField(max_length=50,unique=True)
def __str__(self):
return str(self.email)
Serializer
class EmailSerializer(serializers.ModelSerializer):
class Meta:
model = models.Email
fields = ["email"]
Viewsets are used together with a router and, depending on what is being exposed in your viewset, various GET and POST will be created by the Django REST framework automatically.
Your EmailViewSet is a ModelSerializer, and exposes .list() (), .retrieve(), .create(), .update(), .partial_update(), and .destroy(), through inheritance. All those actions are GET and POST either at the {prefix}/ and {prefix}/{url_path}/ of your router.
If you want to narrow the set of actions, you should derive EmailViewSet from specific mixins that are limiting the actions of the viewset, for instance (see this example):
CreateModelMixin will be a POST on {prefix}/
UpdateModelMixin will be a POST on {prefix}/{url_path}/
from . import models, serializers
from rest_framework import mixins
class EmailViewSet(viewsets.GenericViewSet,mixins.CreateModelMixin,):
queryset = models.Email.objects.all()
serializer_class = serializers.EmailSerializer
def create(self,request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

Got a `TypeError` when calling `Article.objects.create()`

I am working on Django React Project using the Django REST FRAMEWORK,I am trying to post some data tied to my model.
The list view and the detail view of the project works pretty fine,The only problem is when I try to make a POST request.
Whenever I Try post the data in the CreateAPIView I get an error :
Got a `TypeError` when calling `Article.objects.create()`. This may be because
you have a writable field on the serializer class that is not a valid argument to
`Article.objects.create()`. You may need to make the field read-only, or override
the ArticleSerializer.create() method to handle this correctly.
I have searched through various past problems but non of them seem to fix my problem.
Here is my serializers file:
from rest_framework import serializers
from articles.models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ('id','title','content','star_count','like_count','comment_count','avatar')
Here is my views file
from rest_framework.generics import ListAPIView,RetrieveAPIView,CreateAPIView,UpdateAPIView,DestroyAPIView
from .serializers import ArticleSerializer
from articles.models import Article
from rest_framework import viewsets
class ArticleViewSets(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
models file
content = models.TextField()
comment_count = models.IntegerField(default=0,null=True,blank=True)
like_count = models.IntegerField(default=0,null=True,blank=True)
star_count = models.IntegerField(default=0,null=True,blank=True)
avatar = models.ImageField(null=True,blank=True)
def __str__(self):
return self.title
def save(self):
if not self.slug:
self.slug = slugify(self.title)
super(Article,self).save()
Here is the error generated when I try to make a POST request based on the django rest framework createAPIVew
Got a `TypeError` when calling `Article.objects.create()`. This may be because you have a writable field on the serializer class that is not a valid argument to `Article.objects.create()`. You may need to make the field read-only, or override the ArticleSerializer.create() method to handle this correctly.

Pass custom context to Django model

I'm using django-rest-framework.
All models in my app contain User field and I want to write to this field link to current user.
How can I pass user object to model?
I've tired to write User link in SerializerClass, but I think it's not the best solution.
In view:
def perform_create(self, serializer):
serializer.save(created_by=self.user)
Model:
class Tracker(models.Model):
serial_id = models.PositiveIntegerField(unique=True)
created_by = models.ForeignKey(MyUser)
You dont have to. DRF can do that for you.
As here.
Since you haven't shared your complete code I am inclined to share a VERY SIMPLE implementation without writing a lot of code yourself and relying on DRF's ability is:
from rest_framework import generics
class MyView(generics.CreateAPIView):
queryset = Tracker.objects
serializer_class = TrackerSerializer #Assuming that this is your serializer
def post(self , request , *args , **kwargs):
return self.create(request , *args , **kwargs)
DRF will itself take care of the relationships. This code works assuming that you have authenticaton set up and thus request.user is not an Anonymous User.
To grab the logged in user from a generic view, you should use self.request.user:
In view:
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)

How to make a request in DRF that points to another API endpoint in same Django app

very quick question that is how to nest request in Django-rest-framework. I have end point A that I make POST on and want to make another request to point B in it's serializer perform_create method. This API end points are actually written in same Django application.
Serializer for API A
class ReadingCreate(CreateAPIView):
permission_classes = [IsOwnerOrReadOnly]
serializer_class = ReadingCreateSerializer
def perform_create(self, serializer):
#HERE I WANT TO MAKE REQUEST TO POINT B
serializer.save(user_profile= UserProfile.objects.get(user=self.request.user))
I am familiar with library such as request but I hope there is a better way since, I also need to send token for authentication and I am like in same file. This problem seems simple but I clearly don't know how to do it properly.
Update:
To explain more, "request" should find a book based on the isbn that I send it through ReadingCreateSerializer. But first I need to find a book (Google API), then save it to my DB. This needs to be done because book model is independent of UserProfile object and Reading is not (has additional information). That is what my "request" does.
Of course this could be done with two chain requests from client but I don't want that.
Serializer:
class ReadingCreateSerializer(serializers.HyperlinkedModelSerializer):
isbn = serializers.CharField(required=True, max_length=20)
class Meta:
model = Reading
fields = ['isbn', 'notes', 'rating', 'start_reading_date', 'end_reading_date']
What I tried based on the answer: part of view and part of serializer
def perform_create(self, serializer):
self.request.method = 'POST'
serializer.save(user_profile=UserProfile.objects.get(user=self.request.user), request=self.request)
def save(self, **kwargs):
isbn = self.validated_data['isbn']
request = kwargs.get("request", {})
request.data = {'isbn': isbn}
post_book(request)
What I found is that I can't import views (in my example post_book) to serializers I guess that is not allowed by Django.
This will execute your API class.
APIClassB.as_view()(self.request)
If you need to change request method
self.request.method = 'POST'
APIClassB.as_view()(self.request)

Django Rest Framework bulk updates inserting instead of updating

I'm trying to build out a bulk update view for a specific model using Django Rest Framework. In the short term, it only needs to update one field (toggling an invite from submitted=False to submitted=True), but I'd like it to be able to provide more functionality in the future. Whenever I test the view, however, a new object is being created instead of the current one being modified.
I feel like this must be a simple mistake on my part, but I can't figure out what's going on. The serializer object appears to be ignoring the value for "id" passed in through JSON, which may be contributing to the issue. Current code is:
class InviteBulkUpdateView(generics.UpdateAPIView):
def get_queryset(self):
order = self.kwargs['order']
invite = get_objects_for_user(self.request.user, 'sourcing.view_invite')
return invite.filter(order=order)
serializer_class = InviteInputSerializer
def put(self, request, *args, **kwargs):
data = request.DATA
serializer = InviteInputSerializer(data=data, many=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
class InviteInputSerializer(serializers.ModelSerializer):
class Meta:
model = Invite
fields = ('id', 'order', 'team', 'submitted')
Can anybody shed some light onto what I might be doing wrong?
Just in case somebody is looking for a library to handle this, I wrote a Django-REST-Framework-bulk which allows to do that in a couple of lines (the example only does bulk update but the library also allows bulk create and delete):
from rest_framework_bulk import ListCreateBulkUpdateAPIView
class FooView(ListCreateBulkUpdateAPIView):
model = FooModel
You're not passing object instances to your serializer. (Thus it will create new instances rather than update.) See the docs on dealing with multiple objects in serializers where you'll see your QuerySet passed in.
Django has update method to handle that. You may want to read full info from django documentation.
Here is a sample code where you can use to update given field for multiple records:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import APIException
class Room_Update_ViewSet(APIView):
def put(self, request,*args, **kwargs):
hotel_id = self.kwargs.get('hotel_id')
room_ids = self.request.query_params.get('room_ids')
room_ids = list(map(int, room_ids.split(',')))
try:
Room.objects.filter(hotel_id=hotel_id,id__in=room_ids).update(booked_status=False)
instances = Room.objects.filter(hotel_id=hotel_id,id__in=room_ids)
serializer = RoomSerializer(instance=instances, many=True)
return Response(serializer.data,status=status.HTTP_200_OK)
except Exception as e:
print("Error udating rooms-->",e)
raise APIException