Python Pyramid traversal

pyramid, python, traversal

Solution

You take a look at the doc about traversal, because you've haven't got it quite right. This tutorial is also quite useful in understanding traversal. I'll try to do a quick explanation in your context :

First of all, the path of the request is split intro segments. For example `/admin/product` is split into `['admin', 'product']`.

Then, pyramid tries to determine the context for this request. For that, it recursively call `__getitem__` (which is just another way to say it does `object[segment]`) for each segment from the root (it traverses). In the exemple, it does `root['admin']`, which returns an admin object, then does `admin['product']`. It stops when it encounters a KeyError.

Once we have a context, pyramid searches for a view with this context, and whose view name is the part that wasn't traversed. For example, if `admin['product']` raise a KeyError, then pyramid looks for a view that configured with `@view_config(context=Admin, name="product")`.

So, how do you make an app from that ? First, you determine what is your resource tree. In your case, it might looks like this :

- Root

- Admin

- ProductContainer

- Product

There is a view named `home` for the Admin context (`/admin/home`), a view with no name for the `ProductContainer` (`/admin/product`) and a view named `edit` for the product (`/admin/product/1/edit`).

Problem

I've been trying out pyramid and this traversal thing is sending me nuts. I'm basically fiddling around to make a control panel for a shopping cart and this is the basic structure that I have in mind. The login page ``` localhost:6543/admin_login ``` Upon successful login ``` localhost:6543/admin/home ``` To view all existing products ``` localhost:6543/admin/product ``` To edit product X ``` localhost:6543/admin/product/edit/1 ``` So my folder structure is something like this ( Capitalize files are models ) - mycart - resources.py - Admin.py - Product.py - static - templates - views - __init__.py - admin.py - root.py My 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 ``` In `views/__init.py`, it's simply a blank file. As for `root.py`, it's simply a `httpexceptions.HTTPNOTFOUND`, 404 code For `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'] ) ``` After logging in, I point the url to localhost:6543/admin/product, I notice that it still rendering the home page, instead of the product page. I know I missed out something but I can't seem to find out why. Looking through http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/narr/traversal.html, I know that I on the right track as there might be arbitary segments. I've tried modifying resources.py to be the following ``` ..... 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 ``` For this part, I made some progress where it's definitely printing the respective message in the console. However , I have no inkling how should I hook it up with the view_configs and what should be parameters be for the view_configs if any changes need to be made. I do not know if version affects anything but anyway, I am using python 3.3 Any help will be appreciated. Thanks! This is my first time coding in python after years of java. So there might be some terms / concepts that I'm not familiar with respect to pyramid / python. Ok, I think I kinda got my mind to wrap around this traversal thing. Reading through http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/narr/traversal.html, 2 things caught my attention. For example, if the path info sequence is ['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. ``` So based on localhost:6543/admin/products, the settings for view_config is like the following: @view_config(context=Admin, name='products', .... ) So after making changes to 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 ``` And in 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': ''} ``` Somehow or rather, it isn't rendering the product template, but the default 404.

Original source