我正在编写一个管理网站,它控制几个具有相同程序和数据库架构但内容不同的网站.我设计的网址如下:
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.
那么......无论如何我还能让我的生活更轻松吗?
使用混合方法来加载站点可能对您有用.
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.