as a former zope / plone developer, i've got quite a lot of experience with traversal and view lookup, so maybe i'll be able to help here.

On 12/6/10 10:43 AM, Seth wrote:
Okay, so I think I'm just having a hard time grokking the idea of a
"context" (even after beating myself to death with the documentation).

With that in mind, let me rephrase my initial question with a more
high-level question:

How is a large web application in Pyramid supposed to look?


Yes, that's a bit vague so let me clarify my thought process here:

   -  If I had a webapp with, say, 70+ available "routes" it seems
silly to me to specify every single view call in the config, so I
think I'd be a real fan of the config.scan() callable and the
corresponding @view_config decorator.

yup, this is what it's there for. the only time i'd use an external config is if i was explicitly supporting extensibility, if i wanted to make room for other packages to override my view registrations with ones of their own.

   -  TG's object dispatch (and other web frameworks I'm used to) make
the class/module hierarchy vs. url/path hierarchy pretty
straightforward, so it's more natural for me to think of a url as
traversing "controller objects" rather than having "model contexts."

not sure what to say about this. neither is particularly "natural". really, traversal and view lookup are not that hard to grok. i suspect it's only because you're trying to map it onto what you're familiar with, which isn't really the same thing at all, that you're having problems. let me take a stab at describing the process, maybe this will help it click for you. i'll do it with an example, rather than in the abstract, which also might help.

in my fictitious example app we'll have three types of objects: folders, web pages, and calendar events. a folder can contain zero or more other objects, of any type, including other folders. each of these objects are 'model' objects, in both the pyramid traversal sense AND the sqlalchemy or other ORM sense... i.e. they are instances of a model class that are persisted in a database somewhere.

in our example, the root object is a folder; the app has a custom get_root function that returns the root folder. the root folder, like all folders, has a __getitem__ method that returns the contents of that folder. the root folder contains "user folders". each user folder is a workspace for a given user, and inside of each user folder that user is allowed to add and manage content of any of the three defined types.

okay, so now we've set the model / traversal stage. but what about views? each content type will have a display view and an edit view, with the display view as the default. the display view will be named "view", and the edit view will be named "edit". these will have the same name and function for each model type, but the specific implementation of each view will obviously be different; displaying and/or editing a folder is not the same thing as displaying and/or editing a plain web page.

so now we're ready to look at the whole process. imagine a web request with the following path:

/mike/special-project/meeting-2010-12-12/edit

as always, traversal starts w/ the root object, '/'. then the root.__getitem__('mike') will be called, to see if a user folder named 'mike' exists. if not, 404. (the system will actually first look to see if a view named 'mike' is registered for folder objects, but this isn't the case, so 404 it is.)

but mike does exist, and he does have a user folder. so traversal now has this folder, upon which is called __getitem__('special-project'). there are now three possibilities:

- there is no 'special-project' item in the mike folder, so we get a 404 as before.

- there is a 'special-project' web page or calendar event object in the mike folder. this object will be returned, but a) it doesn't have a __getitem__ method and b) there is no 'meeting-2010-12-12' view registered, so again we end up w/ a 404.

- there is a 'special-project' folder in the mike folder. this has a __getitem__ method, of course, so traversal continues.

let's assume the latter of these is what we have. yet again we call __getitem__ on the retrieved folder, this time looking for a 'meeting-2010-12-12' object. if it does not exist... yup, you guessed it: 404. but let's assume it does exist, and that it's a calendar event object. this object will be retrieved and, since there's no __getitem__ method, we'll know that traversal has terminated, and the event object is our final traversal context.

that means that we've finally arrived at view lookup. we've got an event object, and the next path element is 'edit'. has an edit view been registered for event objects? yes; as stated originally, an event object has been registered for EVERY object type. but since we have an event object context, the event object's edit view is what is returned. presumably this view would render an edit form template in the simple case, or, if we are making a POST request, this view would validate and (if valid) persist the submitted form values, ultimately redirecting to the display view with an 'event saved' message.

okay, that was all quite a bit longer than i intended, but along the way i think i managed to cover most of the possible results of a single traversal / view lookup attempt. hopefully that will help to give you a fresh look at how this all fits together.

   -  All the Pyramid example apps I've seen seem to have a single
view.py containing all the "view" methods. In a larger app setting,
wouldn't you want to split these up into separate view modules?
(forgive me, I have not gone googling for examples of large repoze.bfg
apps)

yes, it would absolutely be a good idea to use 'view' package folder with separate modules if you have many views.

   -  So the issue that I'm wrestling with, of course, is how to make
the various @view_configs load the right view method from the
appropriate view module&  method via a config.scan() process.

when you decorate a function with @view_config, you're registering it as a view with a specific name for a specific context type (or types). the view registration happens when config.scan() is called against the module containing the decorated view methods, typically at pyramid startup. whenever traversal has resolved itself to a specific object, the registry will be checked to see if that object type has a registered view with the specified name. does that make sense now?

So maybe I want traversal, maybe I don't. Maybe I just need a "light-
bulb moment" about contexts. I think Pyramid's "different" way of
doing things just has me confused, coming from a background of using
frameworks that have a more "object dispatch" approach to urls/paths.

hopefully the baby-steps example above at least nudges you in the direction of such a "light-bulb" moment.

Which brings me to my new set of questions, which are:

1. What would a larger Pyramid app look like directory/module wise?
Wouldn't this look more like a default Pylons 1/TG 2 setup?
(controllers-*ahem*-views in a "views" dir, models in a "models" dir,
etc)

yes, this would be a fine way to organize a larger pyramid app. although, for pylons developers, there will also be "handlers". handlers are essentially classes that aggregate a set of related views as methods on that class, with some shortcuts provided for registering these views. a handler is roughly analogous to the pylons 1.0 idea of a controller.

2. Are the Pyramid developers intentionally moving toward a less
"object dispatch" setup, and a more declarative setup where routes and
views are explicitly defined (no magic or ponies)?

pyramid developers aren't "moving toward" a less object dispatch setup... they've never HAD an object dispatch approach, that's a TG thing, so there's no need to move away from one.

but yes, routes and views should be explicitly defined. again, handlers will provide some shortcuts for mapping related routes to a related set of views, similar to how pylons controllers work, which will make the view mapping a tiny bit less explicit, in a "convention-over-configuration" way. but handlers are optional; use them if they fit your brain, don't use them if they don't.

3. With the movement away from controllers to a view-based setup, does
this make a Pylons 1 style "map.connect('/{controller}/{action}/
{id}')" route setup totally foreign?

nope, handlers are the concept that keep that style of thinking relevant.

4. Would a typical "large" Pyramid app then have lines and lines of
route/view definitions in the __init__.py (or other config file)?

if you were building an extensive and extensible large pyramid app, then you might conceivably have many view registrations in zcml configuration. if extensibility was not a concern, probably view decorators would be used; in that case the view registration lives right with the view function itself. also, handlers could be used, which would then allow multiple views to be registered all at once as part of the handler registration.

Please forgive me if all of this seems elementary to you bfg/Pyramid
experts. I've been a web developer for years but for some reason
Pyramid is making me feel like a n00b. Again, I think I need a
revelation about contexts.

no worries, those of us who've been doing this for years often forget how mind-bending this can be to someone who's never used it before, especially someone who is used to something that is close enough to be confusing, but which is still fundamentally different. the good news is that i can assure you that once it clicks it's really quite easy to work with... if you decide to use it, it only takes a very short while before you'll be thinking in terms of traversal and view lookups, and then _you'll_ be the one forgetting how mind-bending this can be to someone else...

-r

--
You received this message because you are subscribed to the Google Groups 
"pylons-devel" group.
To post to this group, send email to pylons-de...@googlegroups.com.
To unsubscribe from this group, send email to 
pylons-devel+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/pylons-devel?hl=en.

Reply via email to