In the documentation for dynamic extension of DRF they describe the possibility to to add query parameters through the viewset.(instructions)
These added query parameters act as if they were being passed in the url request, thereby triggering dynamic actions available through the extension.
But I can't get this to work. It seems impossible to make the serializer or router to recognize the alteration of the request instance.
Any suggestions as to where to learn more about how this works, or alternative ways to do it would be greatly appreciated.
class EventViewSet(DynamicModelViewSet):
# …
def list(self, request, *args, **kwargs):
# sideload location by default
request.query_params.add('include[]', 'location.')
# filter for status=current by default
status = request.query_params.get('filter{status}')
if not status:
request.query_params.add('filter{status}','current')
return super(EventViewSet, self).list(request, *args, **kwargs)
Related
I have a DRF view where I need to ensure that uploaded files land on the filesystem and not just in memory. DRF respects Django's FILE_UPLOAD_HANDLERS setting, but I don't want to change it for my whole app, just this one view.
I know that in a regular Django view I could set request.upload_handlers to my desired value, but that doesn't seem to work in DRF. I've tried doing it from .initialize_request() in my viewset, like so:
def initialize_request(self, request, *args, **kwargs):
request.upload_handlers = ["django.core.files.uploadhandler.TemporaryFileUploadHandler"]
return super().initialize_request(request, *args, **kwargs)
but I'm getting:
AttributeError: You cannot set the upload handlers after the upload has been processed.
What is the correct way for me to set the upload handlers for a single DRF view (in particular, the create action of a generic viewset)?
It seems like you are not assigning the upload handler in the right way
from django.core.files.uploadhandler import TemporaryFileUploadHandler
from rest_framework import viewsets
class MyUploadViewSet(viewsets.ModelViewSet):
# your view class attributes goes here....
def initialize_request(self, request, *args, **kwargs):
request.upload_handlers = [TemporaryFileUploadHandler(request)] # initialization goes here
return super().initialize_request(request, *args, **kwargs)
Note
This will work as-is in all DRF class based views
I've got a bunch of existing API endpoints with different URLs and parameters. I'd like to enable asynchronous execution of some of them by adding a general-purpose /tasks(?P<path>.+) endpoint which calls the path endpoint asynchronously, returning a Celery task ID for the client to check the status of the task at their leisure. So far it's very similar to another question, but I was hoping there would be an existing pattern to resolve and call the relevant view without sending another HTTP request. Even though it would be fast enough to send a request, it would be seemingly unnecessary complexity.
It seems this might be possible at the view level, basically stripping off the URL prefix and then using the built-in URL resolver to figure out what to call asynchronously with a slightly modified request object.
This is just an example, the implementation depends on how your code is organized.
Assume you have the following ViewSet, mapped to the url '/example':
class ExampleViewSet(ViewSet):
def list(self, request, *args, **kwargs):
result = some_task()
return Response(result)
Now you can create a second ViewSet and map it to example/async, or tasks/example:
class ExampleAsyncViewSet(ViewSet):
def list(self, request, *args, **kwargs):
result = some_task.delay()
return Response({"task_id": result.id})
You could also use an argument or a query param:
class ExampleViewSet(ViewSet):
def list(self, request, *args, **kwargs):
if (kwargs.get("async") is True: # or request.query_params.get("async") ...
result = some_task.delay()
return Response({"task_id": result.id})
result = some_task()
return Response(result)
Following this tutorial, I'm trying to add a url to which one could make a POST request without a model:
router.register(r'send_message', SendMessageViewSet, base_name='send_message')
I don't need a GET, but I added one for debugging purposes:
class SendMessageViewSet(ViewSet):
def get(self, request, *args, **kwargs):
return Response(HTTP_200_OK)
def create(self, request, *args, **kwargs):
...
Yet I'm able to get the "list" (url with no pk) but not the specific resource.
Thanks!
You probably have missed my link to the full repository.
You are doomed because you are overriding get instead of list.
See https://github.com/linovia/drf-demo/blob/master/drf_demo/model_less/views.py for a full working example.
I want to post to an endpoint from within a CBV and retrieve the response (in order to access the id of the created resource). I've had a look at How to programmatically call a Django Rest Framework view within another view? but cannot find how to send field values through with the request object. request.data is immutable, and passing kwargs through doesn't seem to be doing anything:
from app.views import OtherViewSet
class NewViews(APIView):
def post(self, request, *args, **kwargs):
# kwargs value here is {'field1':1, 'field2':2...}
view_func = OtherViewSet({'post':'create'})
response = view_func(self.request, *args, **kwargs).data
Although the kwargs contains the field values (set through several url named groups), the response always looks like:
{'field1': ['This field is required'], 'field2':['This field is required...
What am I doing wrong? Thanks all :)
Edit:
Once I've got the ID of the created resource, I want to retrieve the resource as rendered by a particular (custom) format (I don't want to return the response object above).
views.py
from app.views import OtherViewSet
class NewViews(APIView):
def post(self, request, *args, **kwargs):
view = OtherViewSet.as_view({'post':'create'})
response = view(request, *args, **kwargs)
return response
I want to create an object using the CreateAPIView from the django-rest-framework. When calling the view, I get a MemoryError. That's probably because the view tries to present all 350000 existing objects in the browseable response.
How should I prevent the view from performing the corresponding query? Defining a post or a get_queryset method does not help.
I solved the problem by using the APIView instead of the CreateAPIView. Here's the class I wrote:
class VoteCreateAPIView(views.APIView):
def post(self, request, *args, **kwargs):
vote = request.POST.get('vote', '')
# here some validation
Vote.objects.create(
user=request.user,
vote=vote)
return response.Response({'vote': vote}, status=status.HTTP_200_OK)
I would still be curious if there's a better way to do it.