Can you get the request method in a DRF ModelViewSet? - django

I am building a Django chat app that uses Django Rest Framework. I created a MessageViewSet that extends ModelViewSet to show all of the message objects:
class MessageViewSet(ModelViewSet):
queryset = Message.objects.all()
serializer_class = MessageSerializer
This chat app also uses Channels and when a user sends a POST request, I would like to do something channels-realted, but I can't find a way to see what kind of request is made. Is there any way to access the request method in a ModelViewSet?

Rest Framework viewsets map the http methods: GET, PUT, POST, and DELETE to view methods named list, update, create, and destroy respectively; so in your case, you would need to override the create method:
class MessageViewSet(ModelViewSet):
queryset = Message.objects.all()
serializer_class = MessageSerializer
def create(self, request):
print('this is a post request', request)
...

we can't get 'GET' request because ModelViewSet is inherited from
mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,
mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet
these mixins so instead of 'GET' request we can override list method
enter image description here

Related

action decorator not calling method in DRF?

so I have a route api/reviews it works fine for basic CRUD operations but according to DRF documentation, I can add extra actions to post, put, patch requests by using the #action decorator.
the route is:
POST domain/api/reviews data=irrelevent JSON
# reviews/<uuid:id> && reviews
# Provides CRUD + List interface for Reviews
class ReviewView(viewsets.ModelViewSet):
permission_classes = [IsPosterOrSafeOnly]
queryset = Review.objects.all()
serializer_class = ReviewSerializer
pagination_class = Pagination
#action(detail=False, methods=['post','put', 'patch'], serializer_class=ReviewSerializer)
def rating(self, request):
print("100: ", request)
from what I understand the way this works is that when I do a post request to the reviews route it will do the post like it normally does and it will do the task in the action decorator.
so I tried doing a post request to see if the code inside the 'rating' method gets run and it does not. I have tried setting detail to both True and False including the pk. nothing really seems to work.

Different authentications and permissions in ModelViewSet - Django REST framework

This question is similar to this one: Using different authentication for different operations in ModelViewSet in Django REST framework, but it didn't work for me.
I've got the following viewset:
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = UserProfile.objects.none()
permission_classes = [SpecialPermission]
SpecialPermission looks like this:
class SpecialPermission(IsAuthenticated):
def has_permission(self, request, view):
if request.method == 'POST':
return True
return super().has_permission(request, view)
REST framework settings:
"DEFAULT_AUTHENTICATION_CLASSES": ["backend.api.authentication.ExpiringTokenAuthentication"],
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
I want to everybody to be able to post to UserViewSet but every other method should require Authentication. However, with the code above I get an Unauthorized Response on post.
What do I need to change?
Although it can be done, this requirement imo does not justify this ifology as auth/user related stuff should be clean and secure.
Instead extract POST method from this viewset to its own class.
class UserViewSet(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
serializer_class = UserSerializer
queryset = UserProfile.objects.none()
permission_classes = [SpecialPermission]
class CreateUserView(CreateAPIView):
serializer_class = UserSerializer
queryset = UserProfile.objects.none()
authentication_classes = []
if you really want to disable authentication in this viewset I'd rather recommend this
def get_authenticators(self):
if self.action == 'create':
return []
return super().get_authenticators()
That's more explicit than your solution.
I figured it out: Making perform_authentication lazy solved my problem. Now I can post but authentication still runs on all other methods where it is needed.
def perform_authentication(self, request):
"""
Perform authentication on the incoming request.
Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
pass

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)

django rest framework - allow only certain methods

I have a resource where i only want to allow a client to do a post request on the resource, thats why i use
class MyViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
in my viewset.
When i do a post request, it works as expected.
When i do a list request, it throws a 405 response, as expected.
When i do a retrieve, put, patch or delete method, it throws a 404 instead of a 405...why?
How can i make every single request return a 405 despite of the post request?
thanks and greetings!
Use http_method_names attribute
class MyViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
http_method_names = ['post']
# your code
There doesn't seem to be any reason to use a ViewSet if you only want to support a single action. Instead, use a CreateApiView with a specific URL pointing to it.
If you viewset doesn't have any detail methods, then the drf SimpleRouter will not create any url route for /api/basename/{id}/
So Django's url dispatcher will not match those urls at all, and returns a 404.
I don't think it makes sense to return 405 for every single method. That status implies that at least one method should be valid for a specific url.
You could add a dummy detail method, but just hand all requests over to the APIView 405 handler.
I think this should force the router to register detail urls for the viewset, and just return 405 for everything (possibly with the exception of OPTIONS).
class MyViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
def retrieve(self, request, *args, **kwargs):
return self.http_method_not_allowed(request, *args, **kwargs)

django rest framework cannot POST data

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