【问题标题】:Is there a better way to switch between HTML and JSON output in Pyramid?有没有更好的方法在 Pyramid 中的 HTML 和 JSON 输出之间切换?
【发布时间】:2011-06-05 17:19:00
【问题描述】:
# /test{.format} no longer seems to work...
config.add_route('test', '/test.{ext}', view='ms.views.test')

views.py:

from pyramid.response import Response
from pyramid.renderers import render

import json

def test(request):
    extension = request.matchdict['ext']
    variables = {'name' : 'blah', 'asd' : 'sdf'}

    if extension == 'html':
        output = render('mypackage:templates/blah.pt', variables, request=request)

    if extension == 'json':
        output = json.dumps(variables)

    return Response(output)

有没有更简单的方法来做到这一点?使用 Pylons,这很简单:

def test(self, format='html'):
    c.variables = {'a' : '1', 'b' : '2'}

    if format == 'json':
        return json.dumps(c.variables)

    return render('/templates/blah.html')

我怀疑我用错了方法...?

【问题讨论】:

  • 您的抱怨是什么?您是否在抱怨金字塔与塔有不同的 API?如果您不喜欢金字塔 API,为什么不回到 Pylons?
  • Pyramid 不使用中间件吗?为什么不能根据用户请求呈现 JSON?在我的书中,直接在视图内部进行操作是一个错误的解决方案。如果可能,请利用中间件。

标签: python api pylons pyramid


【解决方案1】:

试试这个方法:

def test(self, format='html'):
    c.variables = {'a' : '1', 'b' : '2'}

    if format == 'json':
        return Response(json = c.variables)

    return render_to_response('/templates/blah.html')

这与您的 pylons 示例最相似。它还展示了一些更友好的方式来呈现模板或一些 JSON 到响应。

【讨论】:

    【解决方案2】:

    PyramidURL Dispatch 是非常强大和灵活的机制。首先,我们将编写正确的 url 模式。在route pattern syntax 中,我们可以使用regular expressions 替换标记。

    '/test{ext:\\..*}'
    

    在这里我们可以看到 url 路径应该包含 . (句点),然后是任何符号。所有符号,包括 . (句点)将在 request.matchdict 中的键 ext 下。

    当然,我们可以使正则表达式复杂化以指定可能存在哪些扩展:

    '/test{ext:\\.(html|json)}'
    

    然后我们用我们的模式添加路由:

    config.add_route('test',
                     pattern='/test{ext:\\.(html|json)}')
    

    想补充的是,我们可以使用custom predicates指定扩展集。

    为了指定默认扩展名,我们可以使用简单的pregenerator

    def default_extension(ext):
        def pregenerator(request, elements, kw):
            if 'ext' not in kw:
                kw['ext'] = ext
    
            return elements, kw
    
        return pregenerator
    
    config.add_route('test',
                     pattern='/test{ext:\\.(html|json)}',
                     pregenerator=default_extension('.html'))
    
    request.route_path('test')
    # '/test.html'
    request.route_path('test', ext='.json')
    # '/test.json'
    

    之后,我们将Traversal 帮助我们在 htmljson 输出之间切换:

    config.add_route('test',
                     '/test{ext:\\.(html|json)}',
                     pregenerator=default_extension('.html'),
                     traverse='{ext}')
    

    使用add_route 中的traverse 参数,我们强制我们的应用程序为hybrid。我们应该明白,为我们的视图提供上下文的工厂不能包含与我们的扩展匹配的键。 default 根工厂没有。

    views.py:

    from pyramid.view import view_config, view_defaults
    
    
    @view_defaults(route_name='test')
    class Test(object):
        def __init__(self, request):
            self.request = request
            self.variables = {
                'name': 'blah',
                'asd': 'sdf'
            }
    
        @view_config(name='.html', renderer='mypackage:templates/blah.pt')
        def html(request):
            return {
                'request': request,
                'variables': self.variables
            }
    
        @view_config(name='.json', renderer='json')
        def json(request):
            return {
                'request': request,
                'variables': self.variables
            }
    

    在这里,我们创建了class Test 并为其指定了路由名称。然后我们通过扩展名来分隔方法。

    【讨论】:

    • 我喜欢使用遍历来区分不同扩展的行为。不过,视图配置可以非常简单。它可以是一个单独的函数,接受一个用 2 个@view_config 行修饰的请求,两者都带有route_name,以及不同的names 和renderers。它使示例的复杂性大大降低(仅 7 行代码)。
    【解决方案3】:

    我认为,更好的方法是使用不同的渲染器添加两次相同的视图。假设我们有以下视图:

    def my_view(request):
        return {"message": "Hello, world!"}
    

    现在在我们的配置中,我们可以添加两次相同的视图:

    from pyramid.config import Configurator
    config = Configurator()
    config.add_route('test', '/test', my_view, renderer="templates/my_template.mako")
    config.add_route('test', '/test', my_view, renderer="json", xhr=True)
    

    我们现在拥有的:

    1. 如果我们将浏览器指向 url /test,则查看 my_view 将呈现模板 "templates/my_template.mako",并返回作为上下文提供的 dict。
    2. 如果我们将再次调用my_view 发出 XHR 请求,但现在返回的 dict 将被编码为 JSON 并传输回调用方(请read docs 检查请求是否通过 XHR 完成)。

    我们可以使用相同的想法来定义不同的路线,但附加相同的视图:

    from pyramid.config import Configurator
    config = Configurator()
    config.add_route('test', '/test', my_view, renderer="templates/my_template.mako")
    config.add_route('test_json', '/test.json', my_view, renderer="json")
    

    现在/test 将触发模板渲染,但/test.json 将只返回 JSON 编码字符串。

    您可以更进一步,通过add_router 方法的accept 参数调度到正确的渲染器:

    from pyramid.config import Configurator
    config = Configurator()
    config.add_route('test', '/test', my_view, renderer="templates/my_template.mako")
    config.add_route('test', '/test', my_view, renderer="json", accept="application/json")
    

    如果请求带有标头 Accept 设置为 application/json 值 JSON 将被返回,否则你得到渲染模板。

    请注意,这仅适用于您预定义了一组数据格式,您希望在这些数据格式中对来自视图的响应进行编码,但这是通常的情况。如果您需要动态调度,您可以使用add_routedecorate 参数装饰您的视图,这将根据您的规则选择正确的渲染器。

    【讨论】:

    • +1 我认为这最能体现做事的“金字塔方式”。它很灵活,将视图与渲染器分离,使视图更易于单元测试,因为您无需解析 html 文档,只需在字典中查找值。
    • 这似乎是一种巧妙的做法。我将如何将此方法应用于我的 Handlers 方法?我在文档中发现,您可以使用 view_config 装饰函数,这将允许您设置渲染器,但我看不到 URL Dispatch 的等效项。
    • add_handler 不是这样工作的吗?我从未使用过处理程序,但文档指出,“任何额外的关键字参数都会传递给 add_route。”
    • 显然 wsiwyg 编辑器在这里搞砸了,我不能在不提交评论的情况下输入返回字符...但是请参阅plope.com/static/pyr_so.txt
    • 如果你使用遍历呢?
    【解决方案4】:

    这就是你要找的吗? Pylons 和 Pyramid 有不同的 API。所以他们会有所不同。你可以让它们更相似一点,但你不能让它们完全一样。

    def test(request):
        extension = request.matchdict['ext']
        variables = {'name' : 'blah', 'asd' : 'sdf'}
    
        if extension == 'json':
            return Response( json.dumps(variables) )
    
        return Response( render('mypackage:templates/blah.pt', variables, request=request) )
    

    【讨论】:

    • @Tom Willis:这不是真正的问题。可以吗?
    猜你喜欢
    • 1970-01-01
    • 2016-04-28
    • 1970-01-01
    • 1970-01-01
    • 2011-05-02
    • 2015-12-01
    • 2021-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多