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

时间:2012-11-02 10:29:32

标签: 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

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

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

2 个答案:

答案 0 :(得分: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

我们将使用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 给定的模式。

site_id

Pregenerator可以找到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') 并自动将其添加到网址。

@view_config(route_name='site_users', permission='view')
def users_view(request):
    site = request.context

我们在这里设置我们的应用程序。我们还将路线移动到可包含的功能中,这样我们就可以更轻松地测试路线。

add_site_route

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

混合遍历

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

遍历发生在路径/上,其中第一步是找到树的根(SiteFactory)。路由设置为使用__getitem__作为遍历路径的根来执行遍历。此类被实例化为根,并且使用键作为路径中的下一个段({site_id})调用__parent__。然后,我们找到匹配该密钥的站点对象,并在可能的情况下加载它。然后使用__name__find_interface更新网站对象,以允许__acl__生效。它还通过提供稍后提及的Site权限进行了增强。

<强>程序pregenerator

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

<强>验证

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

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

答案 1 :(得分:3)

对于视图,您可以使用类,以便可以使用__init__方法(docs)执行常规作业:

from pyramid.view import view_config

class SiteView(object):
    def __init__(self, request):
        self.request = request
        self.id = self.request.matchdict['id']
        # Do any common jobs here

    @view_config(route_name='site_overview')
    def site_overview(self):
        # ...

    @view_config(route_name='site_users')
    def site_users(self):
        # ...

    def route_site_url(self, name, **kw):
        return self.request.route_url(name, id=self.id, **kw)

您可以使用路由前缀来处理URL(docs)。不确定这是否对您的情况有帮助。

from pyramid.config import Configurator

def site_include(config):
    config.add_route('site_overview', '')
    config.add_route('site_users', '/user')
    config.add_route('site_items', '/item')
    # ...

def main(global_config, **settings):
    config = Configurator()
    config.include(site_include, route_prefix='/site/{id}')