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.