我正在编写一个管理网站,该网站控制着具有相同程序和数据库架构但内容不同的多个网站。我设计的网址如下:
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。
所以......无论如何我还能让我的生活更轻松吗?
答案 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}')