URL调度

URL dispatch 提供将URL映射到的简单方法 view 使用简单的模式匹配语言编写代码。一组有序的模式被逐个检查。如果其中一个模式匹配与请求关联的路径信息,则 view callable 被调用。视图可调用是应用程序中定义的特定代码位,它接收 request 返回一个 response 对象。

高级操作概述

如果应用程序中存在任何路由配置,则 Pyramid Router 根据一组有序的URL匹配模式检查每个传入请求 路线图 .

如果任何路由模式与 requestPyramid 将调用 view lookup 查找匹配视图的过程。

如果路线图中没有与 request 在您的申请中提供, Pyramid 将故障转移到使用 traversal 执行资源位置和视图查找。

路由配置

Route configuration 是添加新的 route 应用程序。有一条路线 name ,作为用于生成URL的标识符。该名称还允许开发人员将视图配置与路由关联。路线还具有 模式 ,旨在与 PATH_INFO URL的一部分(方案和端口之后的部分,例如, /foo/bar 在URL中 http://localhost:8080/foo/bar )它还可以选择具有 factory 和一套 route predicate 属性。

配置路由以匹配视图

这个 pyramid.config.Configurator.add_route() 方法添加一个 route configurationapplication registry . 下面是一个例子:

# "config" below is presumed to be an instance of the
# pyramid.config.Configurator class; "myview" is assumed
# to be a "view callable" function
from views import myview
config.add_route('myroute', '/prefix/{one}/{two}')
config.add_view(myview, route_name='myroute')

当A view callable 通过以下方式添加到配置中 add_view() 通过其 route_name 谓词,当关联的路由模式在请求期间匹配时,将始终找到并调用该视图可调用。

更常见的是,你不会使用任何 add_view 项目“设置”代码中的语句。您将使用 add_route 语句,并使用 scan 将视图可调用文件与路由关联。例如,如果这是项目的一部分 __init__.py

config.add_route('myroute', '/prefix/{one}/{two}')
config.scan('mypackage')

注意我们不打电话 add_view() 在此设置代码中。然而,上述 scan 执行 config.scan('mypackage') 会把每个 configuration decoration 包括任何装饰有 pyramid.view.view_config 室内装潢师 mypackage python包。例如,如果您有 views.py 在您的包中,扫描将获取其任何配置修饰符,因此我们可以在其中添加一个引用 myroute 作为一个 route_name 参数:

from pyramid.view import view_config
from pyramid.response import Response

@view_config(route_name='myroute')
def myview(request):
    return Response('OK')

上述组合 add_routescan 完全等同于使用 add_routeadd_view .

路由模式语法

模式匹配语言的语法 Pyramid 中的URL调度 模式 争论是直截了当的。它接近 Routes 系统使用 Pylons .

这个 模式 在路由配置中使用的字符可以以斜线字符开头。如果模式不是以斜杠字符开头,则在匹配时将在其前面加上一个隐式斜杠。例如,以下模式是等效的:

{foo}/bar/baz

还有:

/{foo}/bar/baz

如果一个模式是一个有效的URL,它将不会与传入的请求匹配。相反,它对于生成外部URL很有用。见 External routes 有关详细信息。

模式段(介于 / 模式中的字符)可以是文本字符串(例如, fooor 它可能是替换标记(例如, {{foo}} 或者两者的某种组合。替换标记不需要在前面加上 / 性格。

替换标记的格式为 {{name}} ,其中表示“接受下一个斜杠字符之前的任何字符,并将其用作 name matchdict value."

模式中的替换标记必须以大写或小写ASCII字母或下划线开头,并且只能由大写或小写ASCII字母、下划线和数字组成。例如: aa_b_bb9 都是有效的替换标记名,但是 0a 不是。

在 1.2 版更改: 在金字塔1.2之前,替换标记不能以下划线开头。以前的版本要求替换标记以大写或小写字母开头。

matchdict是一个字典,表示基于路由模式从URL中提取的动态部分。它可以作为 request.matchdict . 例如,下面的模式定义了一个文本段 (foo )和两个替换标记 (bazbar ):

foo/{baz}/{bar}

上面的模式将匹配这些URL,生成以下matchdict:

foo/1/2        -> {'baz': '1', 'bar': '2'}
foo/abc/def    -> {'baz': 'abc', 'bar': 'def'}

但是,它将不匹配以下模式:

foo/1/2/        -> No match (trailing slash)
bar/abc/def     -> First segment literal mismatch

段中的段替换标记的匹配将只在模式中的段中的第一个非字母数字字符之前完成。例如,如果使用了这种路由模式:

foo/{name}.html

文字路径 /foo/biz.html 将匹配上述路线模式,匹配结果将为 {{'name': 'biz'}} . 然而,文字路径 /foo/biz 不匹配,因为它不包含文本 .html 在段的末尾,表示为 {{name}}.html (它只包含 biz 不是 biz.html

要捕获这两个段,可以使用两个替换标记:

foo/{name}.{ext}

文字路径 /foo/biz.html will match the above route pattern, and the match result will be {{'name': 'biz', 'ext': 'html'}}. This occurs because there is a literal part of . (句号)在两个替换标记之间 {{name}}{{ext}} .

替换标记可以选择指定一个正则表达式,该表达式将用于决定路径段是否应与标记匹配。要指定替换标记只应与正则表达式定义的特定字符集匹配,必须使用稍微扩展的替换标记语法形式。在大括号内,替换标记名后面必须跟一个冒号,然后直接跟在正则表达式后面。这个 违约 与替换标记关联的正则表达式 [^/]+ 匹配一个或多个不是斜线的字符。例如,在发动机罩下,更换标记 {{foo}} 能更准确地拼写为 {{foo:[^/]+}} . 可以将其更改为任意正则表达式以匹配任意字符序列,例如 {{foo:\d+}} 只匹配数字。

例如,可以使用两个替换标记,它们之间没有任何文字字符 /{{foo}}{{bar}} . 但是,如果不指定自定义正则表达式来限制每个标记捕获的内容,这将是一个无意义的模式。

段必须包含至少一个字符才能匹配段替换标记。例如,对于URL /abc/

  • /abc/{{foo}} 不会匹配。
  • /{{foo}}/ 将匹配。

请注意,表示匹配路径段的值将是未加引号的URL,并在matchdict中从utf-8解码为Unicode。例如,以下模式:

foo/{bar}

匹配以下URL时:

http://example.com/foo/La%20Pe%C3%B1a

matchdict将类似(值为url decoded/utf-8 decoded):

{'bar': 'La Pe\xf1a'}

路径段中的文本字符串应表示 译码 价值观 PATH_INFO 提供给金字塔。您不希望使用URL编码的值或字节字符串来表示模式中编码为UTF-8的文本。例如,而不是这样:

/Foo%20Bar/{baz}

您将希望使用类似的内容:

/Foo Bar/{baz}

对于在其文本中包含“高阶”字符的模式,您将希望使用Unicode值作为模式,而不是任何URL编码或UTF-8编码的值。例如,您可能会尝试使用这样的字节字符串模式:

/La Pe\xc3\xb1a/{x}

但这可能会导致启动时出错,或者无法正确匹配。您将希望使用Unicode值作为模式,而不是原始字节串转义。可以使用高阶Unicode值作为模式 Python source file encoding 加上源代码中unicode模式中的“real”字符,如下所示:

/La Peña/{x}

或者可以忽略源文件编码,并在模式中使用等效的Unicode转义字符。

/La Pe\xf1a/{x}

动态段名称不能包含高阶字符,因此这仅适用于模式中的文字。

如果图案有 * 在它后面的名称被认为是“余数匹配”。余数匹配 must 在模式的末尾。与段替换标记不同,它不需要前面有斜线。例如:

foo/{baz}/{bar}*fizzle

上面的模式将匹配这些URL,生成以下matchdict:

foo/1/2/           ->
         {'baz': '1', 'bar': '2', 'fizzle': ()}

foo/abc/def/a/b/c  ->
         {'baz': 'abc', 'bar': 'def', 'fizzle': ('a', 'b', 'c')}

注意,当 *stararg 余数匹配,放入matchdict的值将转换为表示路径其余部分的路径段的元组。这些路径段是未加引号的URL,并从UTF-8解码为Unicode。例如,对于以下模式:

foo/*fizzle

匹配以下路径时:

/foo/La%20Pe%C3%B1a/a/b/c

将生成以下matchdict:

{'fizzle': ('La Pe\xf1a', 'a', 'b', 'c')}

默认情况下, *stararg 将其余部分解析为按段拆分的元组。更改用于匹配标记的正则表达式也可以捕获URL的其余部分,例如:

foo/{baz}/{bar}{fizzle:.*}

上面的模式将匹配这些URL,生成以下matchdict:

foo/1/2/           -> {'baz': '1', 'bar': '2', 'fizzle': ''}
foo/abc/def/a/b/c  -> {'baz': 'abc', 'bar': 'def', 'fizzle': 'a/b/c'}

This occurs because the default regular expression for a marker is [^/]+ which will match everything up to the first /, while {fizzle:.*} will result in a regular expression match of .* capturing the remainder into a single value.

路由声明订购

当请求进入系统时,路由配置声明按特定顺序进行评估。因此,路由配置声明的顺序非常重要。路由声明的计算顺序是它们在启动时添加到应用程序的顺序。(这与将URL映射到 Pyramid 提供、命名 traversal ,这不依赖于模式顺序)。

对于通过 add_route 方法,路由的计算顺序是它们被强制添加到配置中的顺序。

例如,具有以下模式的路由配置语句可以按以下顺序添加:

members/{def}
members/abc

在这种配置中, members/abc 模式将 从未 相配。这是因为匹配顺序将始终匹配 members/{{def}} 首先,路由配置 members/abc 永远不会被评估。

路由配置参数

路由配置 add_route 语句可以指定大量参数。它们作为API文档的一部分记录在 pyramid.config.Configurator.add_route() .

其中许多论点是 route predicate 争论。路由谓词参数指定请求的某些方面必须为真,以便在路由匹配过程中将关联的路由视为匹配。路由谓词参数的示例有 patternxhrrequest_method .

其他参数是 namefactory . 这些参数既不表示谓词,也不表示查看配置信息。

路由匹配

路由配置的主要目的是匹配(或不匹配) PATH_INFO 存在于针对URL路径模式的请求期间提供的wsgi环境中。 PATH_INFO 表示请求的URL的路径部分。

方式 Pyramid 这很简单吗?当请求进入系统时,对于系统中存在的每个路由配置声明, Pyramid 检查请求 PATH_INFO 与声明的模式相反。这种检查是按照路由的声明顺序进行的。 pyramid.config.Configurator.add_route() .

当声明路由配置时,它可能包含 route predicate 争论。与路由声明关联的所有路由谓词都必须 True 用于在检查期间用于给定请求的路由配置。如果集合中有任何谓词 route predicate 提供给路由配置的参数返回 False 在检查过程中,该路由将被跳过,路由匹配将通过已排序的一组路由继续进行。

如果任何路由匹配,则路由匹配过程将停止,并且 view lookup 子系统接管为匹配的路由找到最合理的可调用视图。大多数情况下,只有一个视图可以匹配(配置了 route_name 与匹配的路由匹配的参数)。为了更好地了解路由和视图在实际应用程序中的关联方式,可以使用 pviews 命令,如 pviews :显示给定URL的匹配视图 .

如果在所有路由模式用尽后没有匹配的路由, Pyramid 回落到 traversalresource locationview lookup .

媒人

当与特定路由配置相关联的URL模式与请求匹配时,名为 matchdict 作为的属性添加 request 对象。因此, request.matchdict 将包含与中的替换模式匹配的值 pattern 元素。matchdict中的键将是字符串。这些值将是Unicode对象。

注解

如果没有匹配的路由URL模式,则 matchdict 附加到请求的对象将 None .

匹配的路线

当与特定路由配置关联的URL模式与请求匹配时,名为 matched_route 作为的属性添加 request 对象。因此, request.matched_route 将是实现 IRoute 与请求匹配的接口。路由对象最有用的属性是 name ,这是匹配的路由的名称。

注解

如果没有匹配的路由URL模式,则 matched_route 附加到请求的对象将 None .

路由示例

让我们来看看路由配置语句通常如何声明的一些示例,以及如果它们与请求中的信息匹配,将会发生什么。

例1

配置路由匹配到的最简单路由声明 直接地 导致调用特定的视图可调用:

1
2
config.add_route('idea', 'site/{id}')
config.scan()

当路由配置 view 属性被添加到系统中,并且传入的请求与 模式 路由配置的 view callable 命名为 view 将调用路由配置的属性。

回想一下 @view_config 等于调用 config.add_view ,因为 config.scan() 调用将导入 mypackage.views ,如下所示,并执行 config.add_view 在引擎盖下面。然后,每个视图将路由名称映射到匹配的可调用视图。在上面的示例中,当请求的URL匹配时 /site/{{id}} ,可在python点式路径名处调用的视图 mypackage.views.site_view 将随请求一起调用。换句话说,我们已经将一个可直接调用的视图与一个路由模式相关联。

/site/{{id}} 在请求期间,路由模式匹配 site_view 使用该请求作为其唯一参数调用View Callable。当此路线匹配时,a matchdict 将生成并附加到请求 request.matchdict . 如果匹配的特定URL是 /site/1 , the matchdict 将是一本只有一个键的字典, id ;该值将是字符串 '1' ,Ex. {{'id': '1'}} .

这个 mypackage.views 上面提到的模块可能是这样的:

1
2
3
4
5
6
from pyramid.view import view_config
from pyramid.response import Response

@view_config(route_name='idea')
def site_view(request):
    return Response(request.matchdict['id'])

视图可以通过请求直接访问matchdict,并且可以访问其中的变量,这些变量与路由模式产生的密钥匹配。

意见查看配置 有关视图的详细信息。

例2

下面是您可能添加到应用程序中的一组更复杂的route语句的示例:

1
2
3
4
config.add_route('idea', 'ideas/{idea}')
config.add_route('user', 'users/{user}')
config.add_route('tag', 'tags/{tag}')
config.scan()

下面是一个对应的示例 mypackage.views 模块:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from pyramid.view import view_config
from pyramid.response import Response

@view_config(route_name='idea')
def idea_view(request):
    return Response(request.matchdict['idea'])

@view_config(route_name='user')
def user_view(request):
    user = request.matchdict['user']
    return Response('The user is {}.'.format(user))

@view_config(route_name='tag')
def tag_view(request):
    tag = request.matchdict['tag']
    return Response('The tag is {}.'.format(tag))

上述配置将允许 Pyramid 要以这些形式服务URL,请执行以下操作:

/ideas/{idea}
/users/{user}
/tags/{tag}
  • 当URL与模式匹配时 /ideas/{{idea}} ,可在点式python路径名处调用的视图 mypackage.views.idea_view 将被调用。对于特定的URL /ideas/1 , the matchdict 生成并附加到 request 将包括 {{'idea': '1'}} .
  • 当URL与模式匹配时 /users/{{user}} ,可在点式python路径名处调用的视图 mypackage.views.user_view 将被调用。对于特定的URL /users/1 , the matchdict 生成并附加到 request 将包括 {{'user': '1'}} .
  • 当URL与模式匹配时 /tags/{{tag}} ,可在点式python路径名处调用的视图 mypackage.views.tag_view 将被调用。对于特定的URL /tags/1 , the matchdict 生成并附加到 request 将包括 {{'tag': '1'}} .

在本例中,我们再次将每个路由与 view callable 直接。在所有情况下,请求 matchdict 详细说明进程在URL中找到的信息的属性将传递给视图callable。

例3

这个 context 默认情况下,传递到作为URL调度结果找到的视图的资源对象将是 root factory 在启动时配置 root_factory 论据 Configurator 用于配置应用程序)。

您可以通过传入 factory 论据 add_route() 特定路由的方法。这个 factory 应该是接受 request 并返回将作为视图使用的上下文资源的类的实例。

使用工厂路线的示例:

1
2
config.add_route('idea', 'ideas/{idea}', factory='myproject.resources.Idea')
config.scan()

上述路线将制造 Idea 资源作为 context 假设 mypackage.resources.Idea 解析为在其中接受请求的类 __init__ . 例如:

1
2
3
class Idea(object):
    def __init__(self, request):
        pass

在更复杂的应用程序中,此根工厂可能是表示 SQLAlchemy 模型。视图 mypackage.views.idea_view 可能如下所示:

1
2
3
4
@view_config(route_name='idea')
def idea_view(request):
    idea = request.context
    return Response(idea)

在这里, request.context 是的实例 Idea . 如果资源对象确实是一个SQLAlchemy模型,那么您甚至不必在视图中执行可调用的查询,因为您可以通过 request.context .

路由工厂 有关如何使用Route工厂的详细信息。

匹配根URL

如何使用路由模式匹配根URL(“/”)并不完全显而易见。为此,请在调用中将空字符串作为模式 add_route()

1
config.add_route('root', '')

或提供文本字符串 / 作为模式:

1
config.add_route('root', '/')

生成路由URL

使用 pyramid.request.Request.route_url() 方法根据路由模式生成URL。例如,如果您已使用 name “福” pattern “A/B/C”,您可以这样做。

1
url = request.route_url('foo', a='1', b='2', c='3')

这将返回类似字符串的内容 http://example.com/1/2/3 (至少如果当前协议和主机名暗示 http://example.com

只生成 path 来自路由的URL部分,使用 pyramid.request.Request.route_path() API代替 route_url() .

url = request.route_path('foo', a='1', b='2', c='3')

这将返回字符串 /1/2/3 而不是完整的URL。

传递给的替换值 route_urlroute_path 必须是Unicode或以UTF-8编码的字节字符串。此规则有一个例外:如果您试图替换“余数”匹配值(A *stararg 替换值),该值可以是包含Unicode字符串或UTF-8字符串的元组。

请注意,URL和路径由 route_urlroute_path 始终是URL引用的字符串类型(它们不包含非ASCII字符)。因此,如果添加了这样的路线:

config.add_route('la', '/La Peña/{city}')

然后你用 route_pathroute_url 像这样:

url = request.route_path('la', city='Québec')

最后,您将得到编码为UTF-8的路径和这样引用的URL:

/La%20Pe%C3%B1a/Qu%C3%A9bec

如果你有 *stararg 路线模式的其余动态部分:

config.add_route('abc', 'a/b/c/*foo')

然后你用 route_pathroute_url 使用A 一串 作为替换值:

url = request.route_path('abc', foo='Québec/biz')

您传递的值将被URL引用,但结果中嵌入的斜杠除外:

/a/b/c/Qu%C3%A9bec/biz

通过传递由路径元素组成的元组,可以得到类似的结果:

url = request.route_path('abc', foo=('Québec', 'biz'))

在这种情况下,元组中的每个值都将被URL引用并由斜杠连接:

/a/b/c/Qu%C3%A9bec/biz

静态路由

可以添加一个 static 关键字参数。例如:

1
2
config = Configurator()
config.add_route('page', '/page/{action}', static=True)

添加的路由 True static 关键字参数在请求时永远不会考虑进行匹配。静态路由仅对URL生成有用。因此,向 add_route() 什么时候? static 传递为 True 因为其他的论点都不会被采用。此规则的一个例外是使用 pregenerator 参数,当 staticTrue .

External routes 是隐式静态的。

1.1 新版功能: 这个 static 参数 add_route() .

外部路由

1.5 新版功能.

作为有效URL的路由模式被视为外部路由。喜欢 static routes 它们仅对URL生成有用,在请求时不考虑进行匹配。

1
2
3
4
5
>>> config = Configurator()
>>> config.add_route('youtube', 'https://youtube.com/watch/{video_id}')
...
>>> request.route_url('youtube', video_id='oHg5SJYRHA0')
>>> "https://youtube.com/watch/oHg5SJYRHA0"

大多数模式替换和调用 pyramid.request.Request.route_url() 将按预期工作。但是,呼叫 pyramid.request.Request.route_path() 反外部模式将引发异常,并通过 _app_urlroute_url() 针对具有外部模式的路由生成URL也会引发异常。

重定向到斜线附加路由

像姜戈那样的行为 APPEND_SLASH=True 使用 append_slash 参数 pyramid.config.Configurator.add_notfound_view() 或等效物 append_slash 论据 pyramid.view.notfound_view_config 装饰者。

添加 append_slash=True 是一种自动重定向请求的方法,其中URL缺少尾随斜杠,但需要一个匹配正确的路由。当配置时,以及应用程序中的至少一个其他路由时,如果 PATH_INFO 不是以斜线结尾,如果 PATH_INFO plus 斜线匹配任何路线的模式。在这种情况下,它执行HTTP重定向到附加的斜线 PATH_INFO . 此外,您还可以传递任何实现 pyramid.interfaces.IResponse 然后用它代替默认类 (pyramid.httpexceptions.HTTPFound

让我们用一个例子。如果应用程序中配置了以下路由:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from pyramid.httpexceptions import HTTPNotFound

def notfound(request):
    return HTTPNotFound()

def no_slash(request):
    return Response('No slash')

def has_slash(request):
    return Response('Has slash')

def main(g, **settings):
    config = Configurator()
    config.add_route('noslash', 'no_slash')
    config.add_route('hasslash', 'has_slash/')
    config.add_view(no_slash, route_name='noslash')
    config.add_view(has_slash, route_name='hasslash')
    config.add_notfound_view(notfound, append_slash=True)

如果请求使用 PATH_INFO 价值 /no_slash ,第一条路线将匹配,浏览器将显示“无斜线”。但是,如果请求使用 PATH_INFO 价值 /no_slash/no 路由将匹配,并且未找到追加斜线的斜线视图将找不到具有追加斜线的匹配路由。因此, notfound 将调用视图并返回“未找到”正文。

如果请求使用 PATH_INFO 价值 /has_slash/ ,第二条路线将匹配。如果请求使用 PATH_INFO 价值 /has_slash 一条路线 will 通过斜线追加找到 Not Found View . HTTP重定向到 /has_slash/ 将返回到用户的浏览器。因此, notfound 视图将永远不会被调用。

以下应用程序使用 pyramid.view.notfound_view_configpyramid.view.view_config 装饰师和 scan 要做完全相同的工作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from pyramid.httpexceptions import HTTPNotFound
from pyramid.view import notfound_view_config, view_config

@notfound_view_config(append_slash=True)
def notfound(request):
    return HTTPNotFound()

@view_config(route_name='noslash')
def no_slash(request):
    return Response('No slash')

@view_config(route_name='hasslash')
def has_slash(request):
    return Response('Has slash')

def main(g, **settings):
    config = Configurator()
    config.add_route('noslash', 'no_slash')
    config.add_route('hasslash', 'has_slash/')
    config.scan()

警告

不应该 依靠这种机制进行重定向 POST 请求。斜线追加的重定向 Not Found View 将变成一个 POST 请求进入 GET 失去任何 POST 原始请求中的数据。

pyramid.view更改未找到视图 有关如何配置视图和/或 Not Found View .

调试路由匹配

当进入你的应用程序的请求与你期望的路径不匹配时,你可以在引擎盖下偷看一眼,这很有用。要调试路由匹配,请使用 PYRAMID_DEBUG_ROUTEMATCH 环境变量或 pyramid.debug_routematch 配置文件设置(设置为 true )特定请求的路由匹配决策的详细信息 Pyramid 应用程序将打印到 stderr 从中启动应用程序的控制台。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
PYRAMID_DEBUG_ROUTEMATCH=true $VENV/bin/pserve development.ini
Starting server in PID 13586.
serving on 0.0.0.0:6543 view at http://127.0.0.1:6543
2010-12-16 14:45:19,956 no route matched for url \
                                    http://localhost:6543/wontmatch
2010-12-16 14:45:20,010 no route matched for url \
                            http://localhost:6543/favicon.ico
2010-12-16 14:41:52,084 route matched for url \
                            http://localhost:6543/static/logo.png; \
                            route_name: 'static/', ....

环境变量和 .ini 文件设置 有关如何和在何处设置这些值的详细信息。

您也可以使用 proutes 命令查看应用程序中配置的所有路由的显示。有关详细信息,请参阅 proutes :显示所有应用程序路由 .

使用路由前缀组成应用程序

1.2 新版功能.

这个 pyramid.config.Configurator.include() 方法允许从单独的文件中包含配置语句。见 构建可扩展应用程序的规则 有关此方法的信息。使用 pyramid.config.Configurator.include() 允许您使用小的、可能可重用的组件构建应用程序。

这个 pyramid.config.Configurator.include() 方法接受名为 route_prefix 这对基于URL调度的应用程序的作者很有用。如果 route_prefix 提供给include方法,它必须是字符串。此字符串表示 route prefix 这将预先处理由 包括 配置。任何调用 pyramid.config.Configurator.add_route() 在包含的可调用文件中,其模式的前缀为 route_prefix . 这可用于帮助将一组路由安装到与包含的可调用文件作者预期的不同位置,同时仍保持相同的路由名称。例如:

1
2
3
4
5
6
7
8
from pyramid.config import Configurator

def users_include(config):
    config.add_route('show_users', '/show')

def main(global_config, **settings):
    config = Configurator()
    config.include(users_include, route_prefix='/users')

在上述配置中, show_users 路线将有一个有效的路线模式 /users/show 而不是 /show 因为 route_prefix 参数将在模式前面。只有当URL路径为 /users/showpyramid.request.Request.route_url() 使用路由名称调用函数 show_users ,它将生成具有相同路径的URL。

创建与请求匹配的路由 route_prefix 没有尾随斜杠,通过 inherit_slash=True 呼唤 add_route .

1
2
3
4
5
6
7
8
from pyramid.config import Configurator

def users_include(config):
    config.add_route('show_users', '', inherit_slash=True)

def main(global_config, **settings):
    config = Configurator()
    config.include(users_include, route_prefix='/users')

上述配置将匹配 /users 而不是 /users/ .

路由前缀是递归的,因此,如果通过include本身执行的可调用文件返回并包含另一个可调用文件,则第二级路由前缀将以第一个为前缀:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from pyramid.config import Configurator

def timing_include(config):
    config.add_route('show_times', '/times')

def users_include(config):
    config.add_route('show_users', '/show')
    config.include(timing_include, route_prefix='/timing')

def main(global_config, **settings):
    config = Configurator()
    config.include(users_include, route_prefix='/users')

在上述配置中, show_users 路线仍然有一个有效的路线模式 /users/show . 这个 show_times 然而,路线将有一个有效的模式 /users/timing/times .

路由前缀不影响路由集 姓名 在任何给定的金字塔中,配置都必须是完全唯一的。如果您在许多小的子应用程序中使用 pyramid.config.Configurator.include() ,明智的做法是在路由名称中使用点式名称,这样它们就不会与将来可能添加的其他包发生冲突。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from pyramid.config import Configurator

def timing_include(config):
    config.add_route('timing.show_times', '/times')

def users_include(config):
    config.add_route('users.show_users', '/show')
    config.include(timing_include, route_prefix='/timing')

def main(global_config, **settings):
    config = Configurator()
    config.include(users_include, route_prefix='/users')

存在一个方便的上下文管理器,用于为任何 pyramid.config.Configurator.add_route()pyramid.config.Configurator.include() 上下文中的调用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from pyramid.config import Configurator

def timing_include(config):
    config.add_route('timing.show_times', '/times')

def main(global_config, **settings)
    config = Configurator()
    with config.route_prefix_context('/timing'):
        config.include(timing_include)
        config.add_route('timing.average', '/average')

自定义路由谓词

每个送入 custom_predicates 的参数 add_route() 必须是可调用的接受两个参数。传递给自定义谓词的第一个参数是按惯例命名的字典 info . 第二个参数是当前的 request 对象。

这个 info 字典包含多个值,包括 matchroute . match 表示路由在URL中匹配的参数的字典。 route 是表示匹配的路由的对象(请参见 pyramid.interfaces.IRoute 对于这样一个路由对象的API)。

info['match'] 当谓词需要访问路由匹配时非常有用。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def any_of(segment_name, *allowed):
    def predicate(info, request):
        if info['match'][segment_name] in allowed:
            return True
    return predicate

num_one_two_or_three = any_of('num', 'one', 'two', 'three')

config.add_route('route_to_num', '/{num}',
                 custom_predicates=(num_one_two_or_three,))

以上 any_of 函数生成一个谓词,该谓词确保名为 segment_name 在允许值集合中,由 allowed . 我们用这个 any_of 函数生成一个名为 num_one_two_or_three 从而确保 num 段是其中一个值 onetwothree ,并将结果作为自定义谓词使用,方法是将其放入元组中, custom_predicates 参数 add_route() .

自定义路由谓词也可以 修改 这个 match 字典。例如,谓词可能会对值进行某种类型转换:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def integers(*segment_names):
    def predicate(info, request):
        match = info['match']
        for segment_name in segment_names:
            try:
                match[segment_name] = int(match[segment_name])
            except (TypeError, ValueError):
                pass
        return True
    return predicate

ymd_to_int = integers('year', 'month', 'day')

config.add_route('ymd', '/{year}/{month}/{day}',
                 custom_predicates=(ymd_to_int,))

请注意,转换谓词仍然是谓词,因此它必须返回 TrueFalse . 一个谓词 only 转换,如我们上面演示的转换,应该无条件地返回 True .

为了避免尝试/排除不确定性,路由模式可以包含指定该标记要求的正则表达式。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def integers(*segment_names):
    def predicate(info, request):
        match = info['match']
        for segment_name in segment_names:
            match[segment_name] = int(match[segment_name])
        return True
    return predicate

ymd_to_int = integers('year', 'month', 'day')

config.add_route('ymd', '/{year:\d+}/{month:\d+}/{day:\d+}',
                 custom_predicates=(ymd_to_int,))

现在不再需要尝试/排除,因为除非这些标记匹配,否则路由将完全不匹配。 \d+ 这要求它们是 int 类型转换。

这个 match 字典在内部传递 info 到每个附加到路由的谓词都将是相同的字典。因此,在注册自定义谓词时, match dict,注册谓词的代码通常应该安排谓词为 last 自定义谓词列表中的自定义谓词。否则,自定义谓词将在执行 match 修改将收到 被改进的 匹配字典。

警告

依靠自定义谓词的顺序来构建转换管道是一个糟糕的主意,其中一个谓词依赖于另一个谓词的副作用。例如,注册两个自定义谓词是一个糟糕的主意,一个用于将值转换为int,另一个用于将该整数转换为某个自定义对象。只需在一个自定义谓词中完成所有这些操作。

这个 route 对象中 info dict是一个具有两个有用属性的对象: namepattern . 这个 name 属性是路由名称。这个 pattern 属性是路由模式。下面是在一组路由谓词中使用路由的示例:

1
2
3
4
5
6
7
8
def twenty_ten(info, request):
    if info['route'].name in ('ymd', 'ym', 'y'):
        return info['match']['year'] == '2010'

config.add_route('y', '/{year}', custom_predicates=(twenty_ten,))
config.add_route('ym', '/{year}/{month}', custom_predicates=(twenty_ten,))
config.add_route('ymd', '/{year}/{month}/{day}',
                 custom_predicates=(twenty_ten,))

上述谓词添加到多个路由配置时,确保当且仅当路由名称为“YMD”、“YM”或“Y”时,年份匹配参数为“2010”。

还可以通过设置 __text__ 属性。这将帮助您 pviews 命令(见) proutes :显示所有应用程序路由pyramid_debugtoolbar .

如果谓词是类,只需添加 __text__ 以标准方式拥有。

1
2
3
4
5
6
class DummyCustomPredicate1(object):
    def __init__(self):
        self.__text__ = 'my custom class predicate'

class DummyCustomPredicate2(object):
    __text__ = 'my custom class predicate'

如果谓词是方法,则需要在方法声明之后对其进行赋值(请参见 PEP 232

1
2
3
def custom_predicate():
    pass
custom_predicate.__text__ = 'my custom method predicate'

如果谓词是类方法,则使用 @classmethod 将不起作用,但您仍然可以通过将其包装在ClassMethod调用中轻松完成。

1
2
3
4
def classmethod_predicate():
    pass
classmethod_predicate.__text__ = 'my classmethod predicate'
classmethod_predicate = classmethod(classmethod_predicate)

同样适用于 staticmethod 使用 staticmethod 而不是 classmethod .

参见

也见 pyramid.interfaces.IRoute 有关路由对象的更多API文档。

路由工厂

虽然在基本应用程序中这不是一个特别常见的需求,但是“路由”配置声明可以提到“工厂”。当一个工艺路线与一个请求匹配,并且一个工厂连接到该工艺路线时, root factory 在启动时传递给 Configurator 被忽略。相反,与工艺路线关联的工厂用于生成 root 对象。此对象通常用作 context 可调用视图的资源最终通过 view lookup .

1
2
3
config.add_route('abc', '/abc',
                 factory='myproject.resources.root_factory')
config.add_view('myproject.views.theview', route_name='abc')

工厂可以是python对象,也可以是 dotted Python name (字符串)指向上面这样一个python对象。

这样,每条路线都可以使用不同的工厂,从而可以提供不同的 context 与每个特定路由相关的视图的资源对象。

工厂必须是可调用的,它接受请求并返回任意的python对象。例如,下面的类可以用作工厂:

1
2
3
class Mine(object):
    def __init__(self, request):
        pass

实际上,路由工厂在概念上与 root factory 描述在 资源树 .

当您尝试使用 Pyramid authorization policy 提供声明性的“上下文敏感”安全检查。每个资源可以维护一个单独的 ACL ,如中所述 使用 Pyramid 使用URL调度的安全性 . 当您希望将URL调度与 traversal 如文件所示 结合遍历和URL调度 .

使用 Pyramid 使用URL调度的安全性

Pyramid 提供自己的安全框架,该框架查询 authorization policy 在允许调用任何应用程序代码之前。此框架根据访问控制列表进行操作,该列表存储为 __acl__ 资源对象的属性。通常要做的事情是 __acl__ 为了声明性安全目的动态地指向资源对象。你可以使用 factory 指向附加自定义项的工厂的参数 __acl__ 对象在创建时。

这样的 factory 可能是这样的:

1
2
3
4
5
6
class Article(object):
    def __init__(self, request):
        matchdict = request.matchdict
        article = matchdict.get('article', None)
        if article == '1':
            self.__acl__ = [ (Allow, 'editor', 'view') ]

如果路线 archives/{{article}} 匹配,物品编号为 1Pyramid 将生成一个 Article context 具有ACL的资源,允许 editor 校长 view 许可。显然,您可以做比检查路由的匹配dict更多的常规操作,以查看 article 参数与特定字符串匹配。我们的样本 Article 工厂阶级不是很有野心。

注解

安全性 有关的详细信息 Pyramid 安全和ACL。

路由视图可调用注册和查找详细信息

当一个请求进入与路由模式匹配的系统时,通常的结果很简单:与路由关联的视图可调用与导致调用的请求一起调用。

对于大多数用法,您不需要了解更多。它如何工作是一个实现细节。然而,为了完整性,我们将解释 does 在本节中工作。如果你不感兴趣,可以跳过它。

当视图与路由配置关联时, Pyramid 确保A view configuration 在请求期间匹配路由模式时始终可以找到的。这样做:

  • 特殊路线 interface 在启动时为每个路由配置声明创建。
  • 当一个 add_view 声明中提到 route name 属性 view configuration 在启动时注册。此视图配置使用路由特定接口作为 request 类型。
  • 在运行时,当请求导致任何路由匹配时, request 对象用路由特定的接口进行修饰。
  • 请求被路由特定接口修饰的事实导致 view lookup 机器始终使用通过路由配置使用该接口注册的视图可调用,以服务与路由模式匹配的请求。

从上面的描述中我们可以看到,从技术上讲,URL调度实际上并没有将URL模式直接映射到可调用的视图。相反,URL调度是一个 resource location 机制。一 Pyramid resource location 子系统(即 URL dispatchtraversal 找到一个 resource 对象是 context A的 request . 一旦 context 一个单独的子系统,名为 view lookup 然后负责查找和调用 view callable 基于上下文和请求中可用的信息。使用URL调度时,资源位置和视图查找子系统由 Pyramid 仍在使用,但使用的方式不需要开发人员详细了解其中任何一个。

如果没有匹配的路由,请使用 URL dispatchPyramid 回落到 traversal 处理 request .

工具书类

演示如何 URL dispatch 可用于创建 Pyramid 应用程序存在于 sqlacalchemy+url调度wiki教程 .