6

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

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

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

那么......无论如何,我有没有让我的生活更轻松?

4

2 回答 2

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

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')

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

@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在遍历层次结构中查找请求的实例。如果当前请求未解析为基于站点的 URL,这可能会失败。然后,预生成器route_url使用站点 ID 更新发送到的关键字。

验证

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

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

于 2012-11-02T15:14:37.833 回答
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}')
于 2012-11-02T10:58:14.683 回答