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 )
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.
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 :
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
).
While I do not know if the code below is elegant or any loopholes, it is definitely working for me now. I'll put it in , in case someone is facing the same problem like me.
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': ''}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With