我可以从另一个视图中调用一个视图吗?

96

我的一个视图需要添加一个项目,除此之外还有其他功能,但我已经有另一个专门用于添加项目的视图。

我是否可以像这样做:

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

def big_view(request):
    # ...
    specific_add_item_view(request)

我认为更简单的方法是使用:return specific_add_item_view(request)。 - Roberth Solís
7个回答

92

当然可以,只要最终你的视图返回一个HttpResponse对象即可。以下代码是完全有效的:

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

def view2(request):
    return view1(request)

如果您不想从第一个视图返回HttpResponse,那么只需将其存储在某个变量中以忽略:

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")

只是想要澄清一下你正在做什么: 在第二个例子中,你只是在view1中启动一些逻辑,不会对响应对象执行任何操作,是吗? - Dominique Guardiola
6
没错,这正是我们的想法。view1 可能对一个模型对象或其他什么东西进行操作。不过 Seth 的想法是正确的。最好的做法可能是将两个视图中的公共功能提取出来,放入一个函数中,让 view1view2 都调用该函数,然后它们返回各自的 HttpResponse 对象。没必要生成一个不会被使用的 HttpResponse - 特别是如果其中包含需要执行大量查询的模板。 - brady
@brady,如何在模板中调用view1的变量或者如何利用view1的变量来展示。 - user2086641
3
尽管 Seth 的想法是正确的,但如果需要从第三方应用程序调用视图,则您的解决方案是正确的! - Diego Ponciano
1
第二种模式是不好的。你可以偶尔从view2调用view1,但如果这样做,应该只是“_return view1_”。关键是你要向view1传递一些额外的参数,这些参数通常不会从url中获取(extra_context、template、success_url等)。在这里,view2充当view1的代理视图。如果你需要view1的整个逻辑,那么这几乎是唯一允许的模式(否则应该使用逻辑提取)。从第三方应用程序中,如果你需要更改,只需获取一个视图并完全重写它。对于基于类的视图,在这种情况下使用继承更加清晰。 - clime
view2(request)会处理由view2(request)返回的结果吗? - Gathide

64

视图函数应该返回一个渲染后的HTML响应给浏览器(通过HttpResponse)。在一个视图内调用另一个视图意味着你可能会进行两次渲染操作。相反,将“添加”功能分离成另一个不是视图的函数,并让两个视图调用它。

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)
    ...

26
如果所调用的视图在第三方应用程序中,我们该怎么办? - Sandip Agarwal
1
Seth,如何在模板中调用view1的变量或者如何利用view1的变量来展示? - user2086641
有时候你会想让一个视图调用另一个视图,比如使用一个视图来下载文本/CSV文件或者使用AJAX来更新视图模板的一部分(在这种情况下,第二个视图不会直接从第一个视图中调用,而是通过视图模板HTML文件中的一些jQuery或JS来调用)。 - Samwise Ganges

15

没有基于类的视图:

def my_view(request):
    return call_another_view(request)

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

使用基于类的视图

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

class CallAnotherView(View):
    ...

应该先调用哪个,my_view(request) 还是 CallAnotherView(View) - Gathide
无论是Gathide还是order都可以使用,你可以选择哪个更清晰。 - natonomo

13
更好的方法是使用模板系统。结合@Seth和@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)

并且您的app_name/view1_template.html文件可能包含一个条件模板标签

{% if big_view %}
<p>Extra html for the bigger view</p>
{% endif %}

感谢 @Neceros 指出最新版本的 Django 已经弃用 render_to_response,而推荐使用 render - hobs

1
我的设置:
- Python 3.9 - Django 4
对于所有基于类的视图,这个方法有效:
class CheckoutPage(View):
    
    template_name = "checkout.html"
    def get(self, request):
        prices = ViewAllPrices.as_view()(request)
        return render(request, self.template_name, {'prices': prices})

1
如果您执行以下操作:
def calledView(bar):
    ...
    return Response(...)

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

你可能会遇到这个错误:

request参数必须是django.http.HttpRequest的实例,而不是rest_framework.request.Request.

所以你应该这样做:
def calledView(request):
    ...
    return Response(...)

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

0
你可以这样做:
def foo(req):
  # code
def index(req):
  return foo(req)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接