Stuart Hungerford wrote:
> Hi all,
>
> More newbie questions. I'm trying to get a feel for the idioms
> that people use in creating real-world Quixote applications,
> especially when dealing with the design choices involved in
> mapping URL's to callables or namespaces.
>
> Suppose I plan to support this URL schema at example.org:
>
> http://example.org/
> http://example.org/foo
> http://example.org/bar/baz
> http://example.org/bar/fiz
>
> Assuming that Apache re-writing is re-writing these URLs to
> the corresponding .../cgi-bin/ URLS for the local setup.
>
> Suppose the Quixote driver script loads the module "example.web".
> Then the _q_exports instance for "example.web" could contain
> simple callables (functions or templates) to implement the
> first and second URL's
>
> _q_exports = ["_q_index", "foo"]
>
> For the third and fourth URL's however I could add a submodule
> "example.web.bar" with callables "baz" and "fiz", OR I could
> create a class with "baz", "fiz" member functions
> (or _q_lookup())?
Right. But you must be explicit in your exports: "bar" must also be
included; importing it isn't enough. The bar module doesn't have to live
in the example.web package, but of course it's a good organizational
practice.
_q_lookup() is the exception to the _q_exports rule. But it wouldn't
make sense to just look up a static module that you could simply import
(and then export).
>
> I *think* going the class route means you must use a _q_lookup()
> function within "example.web" module to catch the "bar" component
> of the URL and return a class instance that can then be traversed?
Only if it's turly a dynamic lookup. If 'baz' and 'fiz' refer to methods
rather than to named instances (lookups), then just define 'baz' and
'fiz' as methods in your class, then add the attribute _q_exports =
['baz', 'fiz'] to your class definition.
If they refer to instances, then _q_lookup is your friend. And yes, it's
a common practice to return a class instance that can be traversed. Note
that you don't *have* to do this; you could also return any other
traversable namespace.
Personally I like to use modules instead of classes for my user
interface code, so I often use a pattern like this in my lookups:
# ---------- widgets/web/main.ptl
import my_database
import widget_ui # a module that knows how to render a Widget
def _q_lookup(req, name):
w = my_database.get_widget(name)
if w:
req.widget = w # add the widget to the *request* namespace
return widget_ui # return a static module that expects this.
# ---------- widgets/web/widget_ui.ptl
...
def _q_index [html] (req):
...
"the widget you have asked for is "
req.widget.get_description()
Your mileage may indeed vary!
> Which of these approaches (or other approaches) are more quixotic?
> Do Quixote developers end up in practice relying on _q_lookup()
> heavily for traversing deep URLs, or do they favour a more 1-1
> mapping to callables?
I think there should be a rough correspondence between the breadth/depth
of your data structure and the breadth/depth of your URL space. If your
database is very hierarchical, then you'll probably end up with a deep
nesting. No matter the shape of your data, a good correspondence here
will make your app more understandable (to yourself, your users and
future maintainers of your code).
-- Graham