10

I have pored over several similar posts (and Calling a class-based view of an app from another app in same project seemed promising, but does not work), but some are older and none quite work for me. Here's my setup (using Django==2.0.6, djangorestframework==3.8.2)

I have a basic model (simplified here):

from django.db import models
class Resource(models.Model):
    name = models.CharField(max_length=100, null=False)

I have a basic endpoint where I can list and create Resource instances:

from rest_framework import generics, permissions
from myapp.models import Resource
from myapp.serializers import ResourceSerializer

class ListAndCreateResource(generics.ListCreateAPIView):
    queryset = Resource.objects.all()
    serializer_class = ResourceSerializer
    permission_classes = (permissions.IsAuthenticated,)

(afaik, the details of the serializer are not relevant, so that is left out).

Anyway, in addition to that basic endpoint, I have another API endpoint which performs some actions, but also creates some Resource objects in the process. Of course, I would like to make use of the functionality encapsulated in the ListAndCreateResource class so I only have to maintain one place where Resources are created.

I have tried:

Attempt 1:

class SomeOtherView(generics.CreateAPIView):
    def post(self, request, *args, **kwargs):
        # ... some other functionality...
        # ...
        response = ListAndCreateResource().post(request, *args, **kwargs)
        # ... more functionality...
        return Response({'message': 'ok'})

Unfortunately, that does not work for me. In my trace, I get:

  File "/home/projects/venv/lib/python3.5/site-packages/rest_framework/generics.py", line 111, in get_serializer
    kwargs['context'] = self.get_serializer_context()
  File "/home/projects/venv/lib/python3.5/site-packages/rest_framework/generics.py", line 137, in get_serializer_context
    'request': self.request,
AttributeError: 'ListAndCreateResource' object has no attribute 'request'

Attempt 2: This attempt tries to use the as_view method which is part of all Django class-based views:

class SomeOtherView(generics.CreateAPIView):
    def post(self, request, *args, **kwargs):
        # ... some other functionality...
        # ...
        response = ListAndCreateResource.as_view()(request, *args, **kwargs)
        # ... more functionality...
        return Response({'message': 'ok'})

But that gives up with:

AssertionError: The `request` argument must be an instance of `django.http.HttpRequest`, not `rest_framework.request.Request`

So my question is...is there a straightforward way to do this? I can access the _request attribute of the rest_framework.request.Request object (which is of type django.http.HttpRequest, but then I do not have any of the authentication details that are contained in the DRF Request object (indeed, my ListAndCreateResource returns a 403 if I use response = ListAndCreateResource().as_view()(request._request, *args, **kwargs) in attempt #2 above).

Thanks in advance!

blawney_dfci
  • 594
  • 5
  • 18
  • I suppose one possible alternative would be to move the code performing validation and object creation (the code typically run in `rest_framework.mixins.CreateModelMixin.create`) to some other function (outside of both classes) and have both of those views use that function. That doesn't strike me as particularly elegant, and I'm surprised this has not come up before in other DRF-related SO threads. – blawney_dfci Jul 03 '18 at 03:03
  • I agree with you @blawney_dfci there should be an easier way to reuse rest service view classes. Same problem on this end: several apis are already functional and I need to call these from the python side of things. Guess they really want to disconnect view and model/control. – akahunahi Feb 13 '19 at 06:07
  • the first method worked for me, but serializer worked in a strange way – Max Rukhlov May 19 '20 at 07:31

3 Answers3

5

This seems a bit late, but in case anyone is wondering.

class SomeOtherView(generics.CreateAPIView):
    def post(self, request, *args, **kwargs):
        # ... some other functionality...
        # ...
        response = ListAndCreateResource.as_view()(request, *args, **kwargs)
        # ... more functionality...
        return Response({'message': 'ok'})

The as_view() is a function that when called, returns a function that takes a request, *args, **kwargs. So basically, a class view is an encapsulated function view.

Ahmed I. Elsayed
  • 2,013
  • 2
  • 17
  • 30
  • 1
    I am getting the following error message [The `request` argument must be an instance of `django.http.HttpRequest`, not `rest_framework.request.Request`]. Is there a workaround for this? – Aman Sharma Dec 07 '21 at 07:00
  • 2
    @AmanSharma use `request._request`, the DRF request is a wrapper around django `request` which can be accessed using `._request` – Ahmed I. Elsayed Dec 07 '21 at 19:31
2

I think you can use request._request. The DRF keeps a protected member _request, as is, received from the API call.

Sajal
  • 96
  • 1
  • 5
0

You can access the request with self.request in class based views.

Setti7
  • 149
  • 4
  • 16
  • 1
    I do know that much, but perhaps I misunderstand your answer. The issue (in attempt #2) is that `self.request` is of type `rest_framework.request.Request`. In attempt #1, I am unable to create an instance of `ListAndCreateResource` that has the `request` attribute set. If I set it explicitly, I just get other missing attribute errors for the `ListAndCreateResource` object I am creating. – blawney_dfci Jul 03 '18 at 02:31
  • I read your question and answered another thing, maybe try reading [this](https://stackoverflow.com/questions/9899113/get-request-session-from-a-class-based-generic-view). – Setti7 Jul 03 '18 at 02:43
  • 2
    I can sort of see where you're going by linking that thread (gives some alternate approaches), but I was hoping there was a more direct way without having to resort to a bunch of overridden methods. Thanks anyway. – blawney_dfci Jul 03 '18 at 02:58