Python金字塔遍历

7
我会尝试使用pyramid,并且这个遍历问题让我感到很疯狂。基本上,我正在为购物车制作控制面板而摆弄,并且以下是我考虑的基本结构。
登录页面
localhost:6543/admin_login

成功登录后

localhost:6543/admin/home 

查看所有现有产品

localhost:6543/admin/product

编辑产品 X

localhost:6543/admin/product/edit/1

所以我的文件夹结构大致如下(大写的文件是模型):
  • mycart
    • resources.py
    • Admin.py
    • Product.py
    • static
    • templates
    • views
      • __init__.py
      • admin.py
      • root.py

我的 resources.py

    from pyramid.security import Authenticated
    from pyramid.security import Allow
    from pyramid.response import Response

    class Root(object):
       __name__ = ''
       __parent__ = None

       def __init__(self, request):
          pass

       def __getitem__(self, key):

           if key == 'admin_login':
              return Admin()

           elif key == 'admin':
              return Admin()

           raise KeyError

    class Admin(object):

        __name__ = ''
        __parent__ = Root
        __acl__ = [(Allow, Authenticated, 'admin')]

        def __init__(self):
           pass

views/__init.py中,它只是一个空文件。 至于root.py,它只是一个httpexceptions.HTTPNOTFOUND,404代码。 对于views/admin.py
    from pyramid.view import view_config, render_view
    import mycart.resources

    from pyramid.httpexceptions import HTTPNotFound, HTTPFound
    from mycart.views.root import strip_tags
    from pyramid_mailer import get_mailer
    from pyramid_mailer.message import Message

    from pyramid.security import remember , forget , authenticated_userid

    from pyramid.events import subscriber , BeforeRender

    from mycart.Admin import Admin
    from mycart.Product import Product


    @view_config(context='mycart:resources.Admin',   request_method='POST', renderer='admin/login.jinja2')
    def login_post(context, request):

      if 'btnLogin' in request.params:
        token = request.session.get_csrf_token()
        login = request.params['txtLogin']
        password = request.params['txtPassword']

        admin = Admin(login, request)

        if admin.validate_user( password):

            record = admin.find_user_by_login( login )

            request.session['bs_admin_id'] = str(record['_id'])
            request.session['bs_admin_name'] = record['usr']['fname'] + ' ' + record['usr']['lname'];
            request.session['bs_admin_type'] = record['usr']['type']
            headers = remember(request, login )
            return HTTPFound('/admin/home',  headers=headers)

        message = 'Failed login'

      return {'message': message,  'url': '/admin_login', 'page_title': 'Failed Login'}


      @view_config(context='mycart:resources.Admin', name="home", renderer='admin/home.jinja2', permission='admin')
      def home(context, request):
          logged_in = authenticated_userid(request)
          url = request.path_info

          admin = Admin( logged_in, request )
          rec = admin.find_user_by_objectid( request.session['bs_admin_id'] ) ;

          return { 'firstname': rec['usr']['fname']  }


     @view_config(context='mycart:resources.Admin', name="product", renderer='admin/product_listing.jinja2', permission='admin')
          def product_list(context, request):
          print ('yes, showing product listing requested by ', request.session['bs_admin_id'] )

登录后,我将URL指向localhost:6543/admin/product,但注意到它仍然呈现的是主页,而不是产品页面。

我知道我错过了某些东西,但似乎找不出原因。通过查看http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/narr/traversal.html,我知道我正在正确的轨迹上,因为可能存在任意段落。

我尝试将resources.py修改为以下内容:

   .....

   class Admin(object):

       __name__ = ''
       __parent__ = Root
       __acl__ = [(Allow, Authenticated, 'admin')]

       def __init__(self):
           pass

       def __getitem__(self, key):

          if key == 'product':
             print ("WOOT! Listing products")
             ## this is the part where I don't know what should I return or set or how should I hook it up with view_config

          if key == 'home':
             print ("yes, I'm home!")
             ## this is the part where I don't know what should I return or set or how should I hook it up with view_config

          raise KeyError

对于此部分,我取得了一些进展,它绝对会在控制台打印相应的消息。然而,我不知道如何将其与view_configs连接起来,如果需要进行任何更改,view_configs应该是什么参数。
我不知道版本是否会影响任何内容,但无论如何,我正在使用Python 3.3。
任何帮助都将不胜感激。谢谢!
这是我第一次在Java多年后编写Python代码。因此,就金字塔/ Python而言,可能有一些术语/概念我不熟悉。

好的,我想我已经有点理解遍历这个概念了。阅读http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/narr/traversal.html时,有两件事引起了我的注意。

例如,如果路径信息序列为['a','b','c']:

- Traversal starts by acquiring the root resource of the application by calling the root   factory. The root factory can be configured to return whatever object is appropriate as the traversal root of your application.

- Next, the first element ('a') is popped from the path segment sequence and is used as a key to lookup the corresponding resource in the root. This invokes the root resource’s __getitem__ method using that value ('a') as an argument.

- If the root resource “contains” a resource with key 'a', its __getitem__ method will return it. The context temporarily becomes the “A” resource.

基于 localhost:6543/admin/products,view_config 的设置如下:
@view_config(context=Admin, name='products', .... )
在对 resources.py 进行更改后。
    ## class Root(object):
       ....


    class ProductName(object):
        def __init__(self, _key):
            pass

    class Products(object):
        __name__ = ''
        __parent__ = Root


        def __init__(self):
            pass

        def __getitem__(self, key):
            print ('products: ', key)
            if key == 'add':
                return ProductName(key)

            print ('Approaching KeyError')
            raise KeyError


     class Admin(object):

        __name__ = ''
        __parent__ = Root
        __acl__ = [(Allow, Authenticated, 'admin')]

        def __init__(self):
            pass


        def __getitem__(self, key):

            if key == 'products':
               print ('admin: ', key)
               return Products()

            raise KeyError

在 views/admin.py 中

    @view_config(context=Admin, name='products',  renderer='admin/products.jinja2', permission = 'admin')
    def product_add(context, request):
        print 'hey products_add'
        return { 'msg': ''}

不知何故,它没有呈现产品模板,而是默认的404页面。

好的,那么关于子路径的 view_config 输出怎么样?这是我遇到的主要问题。 - Gino
2个回答

4
您可以查看有关遍历的文档,因为您还没有完全理解它。这个教程在理解遍历方面也非常有用。我将尝试在您的情境下进行快速解释:
首先,请求的路径被分成段。例如,/admin/product被分成['admin','product']
然后,Pyramid尝试确定此请求的上下文。为此,它会递归调用__getitem__(这只是另一种说法,即它执行object[segment])来获取从根目录遍历的每个段的对象。例如,在示例中,它执行root['admin'],返回一个管理对象,然后执行admin['product']。当它遇到KeyError时停止。
一旦我们有了上下文,Pyramid 就会查找一个具有此上下文的视图,并且其视图名称是未经遍历的部分。例如,如果 admin['product'] 引发 KeyError,则 Pyramid 将寻找配置了 @view_config(context=Admin, name="product") 的视图。

那么,如何从这个资源树创建一个应用程序呢?首先,确定您的资源树是什么样子。在您的情况下,它可能看起来像这样:

    • 管理
      • 产品容器
        • 产品

有一个名为home的视图适用于管理上下文(/admin/home),一个没有名称的视图适用于ProductContainer (/admin/product),以及一个名为edit的视图适用于产品 (/admin/product/1/edit)。


我已经阅读了http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/narr/traversal.html,但不幸的是,merickel的教程对我来说没有意义。然而,一些奇怪的事情发生了,pyramid没有渲染产品模板。我已经编辑了我的问题,并进行了修改...也许我的resources.py结构有问题? - Gino
你必须确保对于每个对象,__getitem__ 方法都会返回遍历中的下一个对象。由于你没有包含 Root 的代码,所以问题可能出在那里。 - madjar

2

虽然我不知道下面的代码是否优雅或存在漏洞,但它目前确实对我有效。我会将它放在resources.py中,以防有人像我一样遇到同样的问题。

    class ProductName(object):
        __name__ = ''
        __parent__ = Root
        __acl__ = [(Allow, Authenticated, 'admin')]

        def __init__(self, _key):
            pass

    class Products(object):

        __name__ = ''
        __parent__ = Root
        __acl__ = [(Allow, Authenticated, 'admin')]

        def __init__(self):
            pass

        def __getitem__(self, key):
            print ('products: ' + key)
            if key == 'add':
               return ProductName(key)

            print ('Approaching KeyError')
            raise KeyError

views/admin.py

    @view_config(context="**mycart:resources.ProductName**",  name="",     renderer='admin/product_add.jinja2', permission = 'admin')
        def product_add(context, request):
        print 'hey product add'
        return { 'msg': ''}

    @view_config(context="**mycart:resources.Products**", name='' , renderer='admin/product.jinja2', permission = 'admin')
    def product(context, request):
        print 'hey products listing'
        return { 'msg': ''}

太好了!您可以从视图配置中删除 name='',因为这些是默认值。但是您的 __name____parent__ 是错误的。name 必须等于导致该对象的 URL 部分(如果您在 URL /admin 上获取一个 Admin 对象,则名称必须为 admin),而父级必须是直接父级(ProductName 的父级是 Product)。这些用于使用 resource_url 生成 URL,如果它们不正确,则生成的 URL 将是错误的。 - madjar
哈哈哈,是的,我也在尝试其他子路径,并意识到了错误,在寻求更多帮助之前终于解决了它。但感谢你指出这个错误 =D。 - Gino
@madjar 顺便说一下,我在考虑 root_factory,并且想知道当项目开始扩展时,是否有多个工厂来管理各个部分会更可控/优雅。就性能而言,哪种做法可能更好。就维护而言,哪种做法可能更好? - Gino
我认为,将单个遍历树直接映射到URL上,比遍历/调度混合方式更易于管理。我认为在性能方面没有任何有意义的差异,但这可能需要基准测试来确认。至于优雅性,请记住,遍历的元素实现了__getitm__,行为与字典类似,如果您想要,可以使用字典。因此,如果一个有效的根工厂(您可以子类化字典以添加acl、名称和父级),则返回{'admin': {'products': Products()}}的函数。 - madjar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接