62

I have the following generic class based views built with Django Rest framework (DRF)

class ExampleDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Example.objects.all()
    serializer_class = ExampleSerializer
    renderer_classes = (JSONRenderer, TemplateHTMLRenderer)

    def get(self, request, *args, **kwargs):

        response = self.retrieve(request, *args, **kwargs)
        if request.accepted_renderer.format == 'html':
            form = ExampleForm(data=response.data)
            return Response({'data': response.data, 'form': form}, template_name='example.html')

        return response

This view allow me to obtain both JSON data or HTML form from the same endpoint by specifying the format=json or html.

I would like to programmatically call that view to obtain the rendered HTML form from within another view in order to include this form in another page that will include more stuff.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Etienne Desgagné
  • 3,102
  • 1
  • 29
  • 36

5 Answers5

41

I found the solution for this in the documentation... https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins/

Hint is from their example here:

class AuthorDetail(View):

    def get(self, request, *args, **kwargs):
        view = AuthorDisplay.as_view()
        return view(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        view = AuthorInterest.as_view()
        return view(request, *args, **kwargs)
Community
  • 1
  • 1
user
  • 4,651
  • 5
  • 32
  • 60
  • 16
    Does not work. I get ```The `request` argument must be an instance of `django.http.HttpRequest`, not `rest_framework.request.Request`.``` – waqasgard Jan 30 '19 at 08:45
  • It's certainly possible both projects have diverged in how they treat things ... I haven't played with this in awhile. – user Jan 30 '19 at 14:55
  • 7
    Looks like someone (Greg Brown) proposed an edit that those calls should be `return view(request._request, *args, **kwargs)` -- maybe that is the "newer" way to do it, @waqasgard...worth trying I think. Leaving the answer as-is, since it matches the linked documentation and was valid for older versions of Django. – user Mar 27 '19 at 19:20
  • Here's the link to the 4.0 documentation: https://docs.djangoproject.com/en/4.0/topics/class-based-views/mixins/#avoid-anything-more-complex It has the same example. – Infinite Possibilities Dec 15 '21 at 14:24
34
html_from_view = ExampleDetail.as_view({'get': 'list'})(request).content

OR

html_from_view = ExampleDetail.as_view({'get': 'retrieve'})(request, pk=my_id).render().content
Etienne Desgagné
  • 3,102
  • 1
  • 29
  • 36
  • what is the request ? , and if I'm in one ipython shell – Sérgio Jun 02 '17 at 17:59
  • @Sérgio Read the question. The `request` is the same that was passed to the original view function / get method. If you are in a ipython shell, you have to use another approach. For example, you can emulate a http request using the django test client. https://docs.djangoproject.com/en/2.0/topics/testing/tools/#overview-and-a-quick-example – Håken Lid Jun 28 '18 at 12:05
  • 3
    The above works now, 5 years later, except use `request._request` instead of `request`. – Cloud Artisans Mar 17 '20 at 19:33
  • 2
    This solution works for the ViewSet but not APIView. The solution provided by JD Solanki below works for APIView. – Ketan Sahu Dec 07 '20 at 08:39
  • But how do you create the HttpRequest object programmatically with a body when the body is read-only? An HttpRequest object is required. And for POST requests with a body there appears to be no way whatsoever to create the body programmatically. – Frikster Oct 24 '22 at 21:42
  • It looks like in order to change the body you need custom middleware: https://stackoverflow.com/questions/22740310/how-to-update-django-httprequest-body-in-middleware but then the code in this answer doesn't trigger middleware when it is called. So how on earth do you programatically do this? – Frikster Oct 25 '22 at 02:30
20

As of Django 2.2 and DRF 3.9.2 I am able to get response using below code.

response = UserItemsApiView.as_view()(request=request._request).data

Above example solves below issues:

  • The request argument must be an instance of django.http.HttpRequest, not rest_framework.request.Request
  • Instead of content, using data attribute gave me result from that view.
JD Solanki
  • 898
  • 8
  • 18
  • 3
    I get "b'{"detail":"CSRF Failed: CSRF token missing or incorrect."}'" when trying this, any idea why? – Thorvald Mar 10 '21 at 18:08
1

The syntax of some of the answers was confusing to me, even though that's how the django docs explain it:

https://docs.djangoproject.com/en/4.1/ref/class-based-views/base/#django.views.generic.base.View.as_view

So here's an answer that's a little more understandable for my small brain:

class MyTemplateView(TemplateView):
    template_name = 'my_template.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        view = MyApiView.as_view()
        response = view(request=self.request)
        data = response.data
        context["somedata"] = data
        return context
Brett Elliot
  • 922
  • 1
  • 6
  • 9
-10

If I'm understanding correctly, you need to get the result from view B, while inside view A.

Using the requests/urllib2 and json libraries should solve your problem (as specified in this answer).

To get the URL, you can use a combination of request.get_absolute_uri() and/or request.get_host() and django.core.urlresolvers.reverse.

Community
  • 1
  • 1
Jean Ventura
  • 27
  • 10