96

One of my view needs to add an item, along with other functionality, but I already have another view which specifically adds an item.

Can I do something like:

def specific_add_item_view(request):
    item = Item.objects.create(foo=request.bar)

def big_view(request):
    # ...
    specific_add_item_view(request)
john2x
  • 22,546
  • 16
  • 57
  • 95

7 Answers7

92

Sure, as long as when it's all said and done your view returns an HttpResponse object. The following is completely valid:

def view1(request):
    # do some stuff here
    return HttpResponse("some html here")

def view2(request):
    return view1(request)

If you don't want to return the HttpResponse from the first view then just store it into some variable to ignore:

def view1(request):
    # do some stuff here
    return HttpResponse("some html here")

def view2(request):
    response = view1(request)
    # do some stuff here
    return HttpResponse("some different html here")
brady
  • 2,227
  • 16
  • 18
  • Just to clarify what you're doing : in the second example, you're just launching some logic in view1, won't do nothing with the response objects , right? – Dominique Guardiola Jan 26 '11 at 19:39
  • 6
    Yeah that's the idea. `view1` probably operates on an object of a model or something. Seth has the right idea though. It's probably best to take the common functionality out of both views and put it into a function that `view1` and `view2` both call and then they return their respective HttpResponse objects. No need to generate an HttpResponse that's not going to be used - especially if that includes a template that requires a lot of queries. – brady Jan 26 '11 at 22:14
  • @brady,how to call the view1's variable in template or how to make use of view1's variable to display – user2086641 Jul 17 '13 at 13:55
  • 3
    although Seth has the right idea, if one would need to call a view from a third party app, your solution is the way to go! – Diego Ponciano Sep 17 '13 at 20:15
  • 1
    The second pattern is bad. You can occasionally call view1 from view2 but if you do so it should be just _return view1_. The point is that you pass some additional params to view1 that it does not normally get from url (extra_context, template, success_url, ...). Here view2 acts as a proxy view for view1. That is pretty much only allowable pattern if you need the whole logic of view1 (otherwise logic extraction should be used). From third app you should just take a view and rewrite it completely if you need a change. With class based views you use inheritance in such cases so it is more clean. – clime Nov 22 '13 at 14:51
  • Will `view2(request)` process the results returned by `view2(request)`? – Gathide Nov 23 '20 at 13:33
63

View functions should return a rendered HTML back to the browser (in an HttpResponse). Calling a view within a view means that you're (potentially) doing the rendering twice. Instead, just factor out the "add" into another function that's not a view, and have both views call it.

def add_stuff(bar):
    item = Item.objects.create(foo=bar)
    return item

def specific_add_item_view(request):
    item = add_stuff(bar)
    ...

def big_view(request): 
    item = add_stuff(bar)
    ...
Seth
  • 45,033
  • 10
  • 85
  • 120
  • 26
    What do we do in case the called view is in a third party app? – Sandip Agarwal Aug 14 '12 at 06:38
  • 1
    Seth,how to call the view1's variable in template or how to make use of view1's variable to display – user2086641 Jul 17 '13 at 13:55
  • There are cases where you would want to have a view call another view, like when using a view to download a text/csv file or when using AJAX to update part of the view's template (in this case the second view would not be called directly from the first view but rather by some jquery or js in the view's template html file) – Samwise Ganges Jun 16 '20 at 19:28
15

Without class based views:

def my_view(request):
    return call_another_view(request)

def call_another_view(request):
    return HttpResponse( ... )

With class based views:

def my_view(request):
    return CallAnotherView.as_view()(request)

class CallAnotherView(View):
    ...
GIA
  • 1,400
  • 2
  • 21
  • 38
13

A better way is to use the template system. Combining ideas from @Seth and @brady:

def specific_add_item_view(request, extra_context_stuff=None):
    Item.objects.create()
    context_variables = {} # obviously want to populate this
    if extra_context_stuff:
        context_variables.update(extra_context_stuff)
    return render(request, 'app_name/view1_template.html', context_variables)

def bigger_view(request):
    extra_context_stuff = {'big_view': True}
    return specific_add_item_view(request, extra_context_stuff)

And your app_name/view1_template.html might contain a conditional template tag

{% if big_view %}
<p>Extra html for the bigger view</p>
{% endif %}
hobs
  • 18,473
  • 10
  • 83
  • 106
  • Thanks @Neceros for pointing out that recent django versions deprecate `render_to_response` in favor of `render`. – hobs Aug 19 '16 at 22:14
1

If you do this:

def calledView(bar):
    ...
    return Response(...)

def base_view(request):
    resp = add_stuff(request)
    ...

You will probably get this error:

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

So you should do this instead:

def calledView(request):
    ...
    return Response(...)

def base_view(request):
    resp = add_stuff(request._request)
    ...
Paul Bénéteau
  • 775
  • 2
  • 12
  • 36
1

My setup:

  • Python 3.9
  • Django 4

For class based views (all of them), this works:

class CheckoutPage(View):
    
    template_name = "checkout.html"
    def get(self, request):
        prices = ViewAllPrices.as_view()(request)
        return render(request, self.template_name, {'prices': prices})
Arindam Roychowdhury
  • 5,927
  • 5
  • 55
  • 63
0

You can do like this:

def foo(req):
  # code
def index(req):
  return foo(req)