【问题标题】:Calling another view in Pyramid在 Pyramid 中调用另一个视图
【发布时间】:2011-10-26 06:26:46
【问题描述】:

我的目标:在 Pyramid 中,调用另一个 view-callable,并在不知道该 view-callable 的任何细节的情况下取回 Response 对象。

在我的 Pyramid 应用程序中,假设我有一个使用 view_config 装饰器定义的视图“foo”:

@view_config(route_name="foo",
             renderer="foo.jinja2")
def foo_view(request):
    return {"whereami" : "foo!"}

现在说我想将“bar”路由到一个暂时做同样事情的视图,所以它在内部调用foo_view并返回它的响应:

@view_config(route_name="bar")
def bar_view(request):
   return foo_view(request)

...但是等等!这不起作用,因为foo_view 不会返回Response,它的渲染器 会。

所以,这将起作用:

@view_config(route_name="bar",
             renderer="foo.jinja2")
def bar_view(request):
    return foo_view(request)

因为它将应用与foo_view 相同的渲染器。但这很糟糕,因为我现在必须通过复制渲染器值来重复自己,并且必须知道被调用视图的渲染器。

所以,我希望 Pyramid 中有一些可用的函数,允许调用另一个可调用视图并返回 Response 对象,而无需知道或关心它是如何呈现的:

@view_config(route_name="bar")
def bar_view(request):
    response = some_function_that_renders_a_view_callable(foo_view, request)
    return response

some_function_that_renders_a_view_callable 会是什么?

pyramid.views.render_view 似乎按名称搜索视图;我不想给我的观点命名。

(注意:返回 HTTPFound 导致客户端重定向到目标路由是我试图避免的。我想“内部”重定向)。

【问题讨论】:

    标签: python view pyramid


    【解决方案1】:

    你不能这样做吗:

    @view_config(name="baz")
    def baz_view(request):
        return HTTPFound(location=self.request.route_path('foo'))
    

    【讨论】:

    • 是和否——HTTP 重定向是往返的两倍,如果用户注意到返回的页面位于与她请求的不同的 URL 上,这可能会让用户感到困惑。当我们谈论的原始 URL 是主页“/”页面时,后者变得更加令人担忧。
    • 这是在不使用 URL 的情况下重定向到视图的好方法,而且 IMO 比内部调用其他视图更可取。
    【解决方案2】:

    您可以使用request.invoke_subrequest 调用视图:

    from wsgiref.simple_server import make_server
    
    from pyramid.config import Configurator
    
    from pyramid.request import Request
    
    
    def view_one(request):
    
        subreq = Request.blank('/view_two')
        response = request.invoke_subrequest(subreq)
        return response
    
    def view_two(request):
    
        request.response.body = 'This came from view_two'
        return request.response
    
    if __name__ == '__main__':
    
        config = Configurator()
        config.add_route('one', '/view_one')
        config.add_route('two', '/view_two')
        config.add_view(view_one, route_name='one')
        config.add_view(view_two, route_name='two')
        app = config.make_wsgi_app()
        server = make_server('0.0.0.0', 8080, app)
        server.serve_forever()`
    

    /view_one 在浏览器中被访问时,打印在 浏览器窗格将是“这来自 view_two”。 view_one 视图 使用pyramid.request.Request.invoke_subrequest() API 获取 来自同一应用程序中另一个视图 (view_two) 的响应 当它执行时。它通过构建一个新的请求来做到这一点,该请求具有 它知道会匹配view_two 视图注册的 URL,并且 将该新请求传递给 pyramid.request.Request.invoke_subrequest()view_two 视图 callable 被调用,它返回一个响应。 view_one 视图 callable 然后简单地返回它从 view_two 查看可调用对象。

    【讨论】:

      【解决方案3】:

      是的。有一些顾虑

      • 不返回响应
      • 谓词/渲染器
      • 权限
      • 与旧请求关联的请求属性

      这就是为什么你不应该从视图中调用视图作为函数,除非你知道你在做什么

      Pyramid 创建者为服务器端重定向做了很棒的工具 - http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/subrequest.html

      【讨论】:

        【解决方案4】:

        不是您要求的精确解决方案,而是您描述的问题的解决方案:

        创建一个视图类,其中 foo 和 bar 都是方法。然后 bar 可以调用 self.foo()

        普通的view_configuration,比如模板名可以应用到类中,然后你可以只用视图名来装饰每个方法。

        简而言之,如果我正确理解问题,以下内容应该可以满足您的需求。

        @view_defaults(renderer="foo.jinja2")
        class WhereaboutsAreFoo(object):
        
            @view_config(route-name="foo")
            def foo_view(self):
                return {"whereami" : "foo!"}
        
            @view_config(route-name="bar")
            def bar_view(self):
                return self.foo_view()
        

        【讨论】:

        • 授予,在提出问题时这可能不是一个选项。我不确定金字塔 1.3 什么时候出来的。
        • 我的目标:在 Pyramid 中,调用另一个 view-callable,并在不知道该 view-callable 的任何细节的情况下取回一个 Response 对象。 当然必须放置 another 特定类中的可调用视图与不了解该可调用视图的任何细节直接相反。
        • 很确定这不起作用。这是我尝试时发生的情况: ValueError: 无法将类 example.views.ExampleViews 的视图可调用方法 foo_view 的返回值转换为响应对象。返回的值是 {'whereami': 'foo!'}。您可能忘记在视图配置中定义渲染器。
        【解决方案5】:

        我也在为此苦苦挣扎。我有一个使用render_to_response method 的解决方案,尽管我确信有一个“更正确”的方法可以做到这一点。然而,在有人发布之前,我是这样处理的:

        from pyramid.renderers import render_to_response
        
        @view_config(route_name="foo", renderer="foo.mak")
        def foo_view(request):
            return {'stuff':'things', '_renderer':'foo.mak')
        
        def bar_view(request):
            values = foo_view(request)
            renderer = values['_renderer']
            return render_to_response(renderer,values)
        

        (金字塔 1.3)

        这需要使用渲染器,但是通过在原始视图的返回值中声明该渲染器,您可以在不知道它是什么的情况下在另一个视图中检索它。我怀疑这样做的需要并不容易找到,因为还有其他更好的方法可以完成此解决方案解决的任务。

        另一个缺点是它依赖于视图可调用的直接导入。如果能直接通过路由查找就好了。

        【讨论】:

          【解决方案6】:

          Pyramid 文档here 表明,将name 关键字参数从view_config 中删除将导致视图由函数本身(而不是字符串)注册:

          这样的注册...意味着视图名称将是 *my_view*

          因此,在您的情况下,您应该能够使用 pyramid.view.render_viewpyramid.view.render_view_to_response 直接引用 foo_view

          @view_config(route_name="bar")
          def bar_view(request):
              return pyramid.views.render_view_to_response(None, request, name=foo_view)
          

          更新:

          是的,你的权利,通过视图功能不起作用

          这很有趣,但是使用您的示例代码并将route_name 应用于配置 对我不起作用。然而,下面的例子,只是给视图一个 name 设置路由 url 并为视图命名。以这种方式render_view_to_response 像宣传的那样工作。命名, 您的视图可能不是您想要的,但此配置完成与您的相同的事情 没有添加配置的示例代码。

          @view_config(name="foo")
          def foo_view(request):
              # returning a response here, in lieu of having
              # declared a renderer to delegate to...
              return Response('Where am i? `{0[whereami]}'.format({"whereami" : "foo!"}))
          
          @view_config(name="bar")
          def bar_view(request):
              # handles the response if bar_view has a renderer 
              return render_view_to_response(None, request, name='foo')
          
          @view_config(name="baz")
          def baz_view(request):
              # presumably this would not work if foo_view was
              # not returning a Response object directly, as it
              # skips over the rendering part. I think you would
              # have to declare a renderer on this view in that case.
              return foo_view(request)
          
          if __name__ == '__main__':
              config = Configurator()
              config.scan()
              app = config.make_wsgi_app()
              serve(app, host='127.0.0.1', port='5000')
          

          【讨论】:

          • 这会导致“ValueError:无法将视图返回值“None”转换为响应对象。”我还需要对目标视图做些什么来完成这项工作吗?
          猜你喜欢
          • 2017-10-07
          • 2022-01-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-15
          • 2011-06-16
          • 1970-01-01
          相关资源
          最近更新 更多