So I've been trying to puzzle out some issues, which might or might not 
be related to this topic.

In KARL, we currently write all of our views something in the form of:

def myview(context, request):
    [ do stuff ]
    api = TemplateAPI(context, request) # a utility class
    return render_template_to_response('', api=api,
                                       foo=1, bar=2)

In the template, we do things like "tal:replace="href api.context_url" 
and so on.  Basically, the KARL "api" object is a "garbage barge" of 
utility attributes and functions totally specific to the application 
itself.  There is nothing general about it.

What I'd like to do is to be able to write a KARL view like this:

def myview(context, request):
    [ do stuff ]
    return render_template_to_response('', foo=1, bar=2)

... but still have some way of accessing the KARL "api" in the template. 
  In essence, I'd like to put the BFG templating system in charge of 
adding one or more globals that is accessible within the template.

There's a half step towards this that's simple.  It'd be relatively easy 
to put both "context" and "request" into the names passed to all 
templates.  But this doesn't help us very much in the common case, 
because then we'd need to write a lot of hairy expressions to compute 
things based on those values.  Basically the template would assume the 
job of our current "API", which is not ideal.

It'd be better if we could add some configuration that would one or more 
globals to a template's namespace.  For example, one configuration might 
look like (via ZCML):

  <templatename name="api" component=".api.TemplateAPI"/>

The ".api.TemplateAPI" class would accept, say, a context and a request 
(or just a request, as requests already have a context attribute), ala:

class TemplateAPI(object):
     def __init__(self, context, request):
         self.context = context
         self.request = request
         self.context_url = model_url(context)

At this point the name "api" would just be jammed into the template 
names on each template rendering and it could have both attributes and 
methods accessible via "api.whatever".

At some point I had considered using a Chameleon expression prefix to 
separate these sorts of "builtin" names from the main template namespace 
names.  For example, instead of "tal:replace="href api.context_url", 
you'd access builtins via an expression type, e.g. "tal:replace="href 
api:context_url".  That bugged Malthe because it's essentially a "second 
"python:" expression type that happens to not use the "main" template 
namespace.  I'm not sure about it either, because it's obvious that 
you'll want to use the names passed to the template explicitly in those 
expressions too, and the semantics and implementation could get pretty 
confusing if you need to make that possible.

Can anyone think of a better way of achieving this goal?  If we don't 
collectively come up with something better, I am apt to just allow folks 
to register factories via ZCML that produce something that is jamed into 
the main template namespace, ala the "templatename" ZCML directive 
above.  Any number of them will be registerable.  A conflict will occur 
during ZCML processing if two share the same name.  These functions, if 
they exist, will be called on each template rendering and their results 
will be jammed into the top-level namespace.

Robert Marianski wrote:
> On Mon, Aug 24, 2009 at 10:11:04PM -0400, Chris McDonough wrote:
>> I like this.
>> Some variations:
>> - Have IOWrap be a multiadapter on context and request, so we can vary the 
>> o-wrap based on request type too (e.g. "IManagementRequest" vs. 
>> "IRetailRequest").
> Good idea. I'll go ahead and add this in.
>> - If IOWrap can't be adapted, just return the result of the view (instead of 
>> throwing a component lookup error).
> I'm kind of torn on this. On the one hand, I like that a specific
> exception would get thrown, so it's easy to know when there's a
> configuration problem and how to fix it. On the other, getting no wrap
> is obvious itself, and may be better for users to initially see an
> unwrapped page rather than seeing their applications explode.
> I'd expect that there would typically be a catch-all owrap defined
> anyway, so it's unlikely that this will be much of an issue in practice.
> But in retrospect again, I think it's better to fail more gracefully by
> showing an unwrapped view instead of an exception. I'll go ahead and
> make this change too.
>> Please forgive the digression, but this is physically sort of tied in: we 
>> still 
>> need a good way to allow a set of "top level names" to be supplied to 
>> templates. 
>>   So far, the best we could do for that pattern has been something like 
>> ("api" 
>> is not a good name, I can't think of anything better, though):
> Can you give an example use case for this? For the cases in my mind, I'd
> prefer to push as much logic out into the owrap as possible, so that the
> actual views don't need to worry about it. I can potentially see a lot
> more ui reuse if this is possible. For example, I can write management
> views against a dublin core interface, and this can be used in any
> application now because it's not tied to any owrap macro.
> The fuzzy area in my mind is a dynamic sidebar. I often have cases where
> there's *always* a sidebar in the theme, maybe save for a couple of
> pages, but the contents change depending on the view. It comes down to
> whether you want to manage the contents of the sidebar as part of the
> view, or part of the owrap, and I think this depends on how drastic the
> changes themselves are.
> I think having the owrap do some sort of lookup to figure out what goes
> in the sidebar is more flexible. But in practice, I've found it more
> annoying to work with it this way, because it's more convenient to
> handle it in the view's template/logic, especially if it's varied on a
> view to view basis.
> Is this the sort of thing you have in mind?
>> class TemplateAPI:
>>     def __init__(self, context, request):
>>         ... do stuff ...
>> Then in a view:
>> api = TemplateAPI(context, request)
>> return render_template_to_response('templates/',
>>                                      api=api)
>> When the template needs access to "common" names, it then does e.g. 
>> tal:content="api.something".
> I guess I don't exactly follow what the top level common names are. Are
> they things like topnav, header, footer, where those are calculated
> based on context and security?
>> Malthe said it might be possible to register one or more new TAL expression 
>> types (each of which might represent a namespace) so templates could do 
>> something like tal:content="api:something" (note colon instead of dot) to 
>> get 
>> top-level names instead.  The view would no longer need to pass an "api" to 
>> provide the template with access to very common top-level names.
> I personally like this sort of thing. With zpt in a grok/plone setting,
> I've found myself writing traversal adapters for things like checking
> security, or application wide custom template "filters". It's much
> easier to be able to use these in a template by using this sort of
> syntax rather than passing them on through the view. For example:
> <a tal:attributes="href model_url(context, request, 'edit')"
>    tal:condition="context/has_permission:edit">Edit</a>
> Maybe some sort of syntax for model_url would be convenient too :)
> What do you guys typically do?
>> However, computing the values for the top-level names often requires access 
>> to 
>> stuff in the request or the context, and the current "render_template" APIs 
>> don't require that you pass either in to the view.
>> I guess we could provide an alternate implementation of the template* APIs 
>> (like 
>> render_template_to_response and get_template, etc) that must be passed the 
>> request (the request has access to the context too as request.context) as 
>> the 
>> first argument if values for names needed to be computed based on those bits 
>> of 
>> information.
> That sounds reasonable to me.
>> If views had access to top-level names like this, we could also just do 
>> owraps 
>> the more traditional way (using METAL), if one of the names was e.g. 
>> "main_template".  I suppose the names would be computed via an adapter 
>> lookup 
>> just like the rm.owrap stuff does now.
> For cases where the owrap varies a lot from view to view, I think the
> metal approach works well. But for times when the dynamicness of the
> owrap can be easily calculated from the owrap, I think pushing it to the
> owrap makes sense.
>> That said, I don't think that approach is mutually exclusive with the 
>> pagelet 
>> approach.  The pagelet approach is more generic: any templating language 
>> could 
>> be used for the owrap or the wrapped view; the same can't be said for the 
>> top-level-name way.  Maybe some combination of both.
> Definitely. Although imho, if you're using macros for the wrapping in
> all your views, then you have less to gain by using an adapted owrap.
> Robert
>> On 8/24/09 2:14 AM, Robert Marianski wrote:
>>> I was thinking about different ways to apply the owrap theme that's
>>> typical for most applications.
>>> I like the way that z3c.pagelet does it:
>>> To summarize, views are registered through a different directive, and
>>> then a layout is applied with a special directive. The advantage here is
>>> that the views no longer specify their master template. It's configured
>>> externally through zcml. This, theoretically anyway, allows more view
>>> reuse since they can be registered with a separate owrap in a different
>>> application without modification to the view code.
>>> Anyway, I have a simple poc implementation for repoze.bfg.
>>> $ svn co 
>>> rm.owrap
>>> $ cd rm.owrap
>>> $ python
>>> $ bin/buildout
>>> $ bin/test
>>> $ bin/paster serve src/dummyapp/dummyapp.ini
>>> Like z3c.pagelet, it adds a new directive for view registration. But
>>> instead of a new directive for the layout, it just uses a plain adapter
>>> to get the owrap. A new directive might be better because it would stand
>>> out more in the zcml, but it was easier to do this way for a poc.
>>> Robert
>>> _______________________________________________
>>> Repoze-dev mailing list
>> _______________________________________________
>> Repoze-dev mailing list

Repoze-dev mailing list

Reply via email to