如何以优雅的方式处理复杂的URL?

Lin*_*ong 6 python pyramid

我正在编写一个管理网站,它控制几个具有相同程序和数据库架构但内容不同的网站.我设计的网址如下:

http://example.com/site                 A list of all sites which under control
http://example.com/site/{id}            A brief overview of select site with ID id
http://example.com/site/{id}/user       User list of target site
http://example.com/site/{id}/item       A list of items sold on target site
http://example.com/site/{id}/item/{iid} Item detailed information
# ...... something similar
Run Code Online (Sandbox Code Playgroud)

如您所见,几乎所有URL都需要site_id.在几乎所有的视图中,我都要做一些常见的工作,比如使用site_id查询Site模型和数据库.此外,每当我调用request.route_path时,我都必须传递site_id.

那么......无论如何我还能让我的生活更轻松吗?

Mic*_*kel 5

使用混合方法来加载站点可能对您有用.

def groupfinder(userid, request):
    user = request.db.query(User).filter_by(id=userid).first()
    if user is not None:
        # somehow get the list of sites they are members
        sites = user.allowed_sites
        return ['site:%d' % s.id for s in sites]

class SiteFactory(object):
    def __init__(self, request):
        self.request = request

    def __getitem__(self, key):
        site = self.request.db.query(Site).filter_by(id=key).first()
        if site is None:
            raise KeyError
        site.__parent__ = self
        site.__name__ = key
        site.__acl__ = [
            (Allow, 'site:%d' % site.id, 'view'),
        ]
        return site
Run Code Online (Sandbox Code Playgroud)

我们将使用groupfinder将用户映射到主体.我们在这里选择的只是将它们映射到他们拥有会员资格的网站.我们的简单遍历只需要一个根对象.它更新加载site__acl__使用相同的校长groupfinder是建立创建.

您需要request.db在Pyramid Cookbook中设置给定的模式.

def site_pregenerator(request, elements, kw):
    # request.route_url(route_name, *elements, **kw)
    from pyramid.traversal import find_interface
    # we use find_interface in case we improve our hybrid traversal process
    # to take us deeper into the hierarchy, where Site might be context.__parent__
    site = find_interface(request.context, Site)
    if site is not None:
        kw['site_id'] = site.id
    return elements, kw
Run Code Online (Sandbox Code Playgroud)

Pregenerator可以自动找到site_id并将其添加到URL中.

def add_site_route(config, name, pattern, **kw):
    kw['traverse'] = '/{site_id}'
    kw['factory'] = SiteFactory
    kw['pregenerator'] = site_pregenerator

    if pattern.startswith('/'):
        pattern = pattern[1:]
    config.add_route(name, '/site/{site_id}/' + pattern, **kw)

def main(global_conf, **settings):
    config = Configurator(settings=settings)

    authn_policy = AuthTktAuthenticationPolicy('seekrit', callback=groupfinder)
    config.set_authentication_policy(authn_policy)
    config.set_authorization_policy(ACLAuthorizationPolicy())

    config.add_directive(add_site_route, 'add_site_route')

    config.include(site_routes)
    config.scan()
    return config.make_wsgi_app()

def site_routes(config):
    config.add_site_route('site_users', '/user')
    config.add_site_route('site_items', '/items')
Run Code Online (Sandbox Code Playgroud)

我们在这里设置应用程序.我们还将路线移动到可包含的功能中,这可以让我们更容易地测试路线.

@view_config(route_name='site_users', permission='view')
def users_view(request):
    site = request.context
Run Code Online (Sandbox Code Playgroud)

然后简化我们的观点.只有在用户有权访问该站点并且已经为我们加载了站点对象时,才会调用它们.

混合遍历

add_site_route添加了一个自定义指令config以使用包装器增强您的对象,该包装器add_route将自动为路径添加遍历支持.当匹配该路由时,它将{site_id}从路由模式中获取占位符并将其用作遍历路径(/{site_id}是我们根据遍历树的结构定义的路径).

遍历发生在/{site_id}第一步找到树根(/)的路径上.路由设置为使用SiteFactory遍历路径的根执行遍历.此类被实例化为根,并__getitem__使用键(即path({site_id})中的下一个段)调用.然后,我们找到匹配该密钥的站点对象,并在可能的情况下加载它.然后使用a更新站点对象__parent____name__允许其find_interface工作.它还通过__acl__后面提到的提供权限得到增强.

程序pregenerator

每个路由都使用预生成器进行更新,该预生成器尝试Site在请求的遍历层次结构中查找实例.如果当前请求未解析为基于站点的URL,则可能会失败.然后,预生成器将route_url使用站点ID 更新发送到的关键字.

认证

该示例显示了如何使用身份验证策略将用户映射到主体,指示此用户位于"site:"组中.然后将site(request.context)更新为具有ACL,表示如果site.id == 1"site:1"组中的某个人具有"view"权限.在users_view随后更新为需要"查看"的权限.HTTPForbidden如果拒绝用户访问视图,则会引发异常.如果需要,您可以编写异常视图以有条件地将其转换为404.

我的答案的目的只是为了展示混合方法如何通过在后台处理URL的常见部分来使您的视图更好一些.HTH.