Alberto Valverde wrote:
> Hello,
> I'm trying to make use of the new-in-1.1 "accept" view predicate to 
> register several views with the same name for the same context object to 
> render a response in the content-type requested by the user but the 
> result is not quite what I expect. The zcml looks like this:
>  <view
>     for="sigym.propag.interfaces.IPropagator"
>     permission="view"
>     request_method="GET"
>     accept="text/html"
>     renderer="sigym.ogcserver:views/templates/index.html"
>     />
>  <view
>     for="sigym.propag.interfaces.IPropagator"
>     view="sigym.ogcserver.views.viewpanels.config_view"
>     permission="view"
>     request_method="GET"
>     accept="application/json+xconfig"
>     renderer="json"
>     />
>  <view
>     for="sigym.propag.interfaces.IPropagator"
>     view=".views.list_propagations"
>     permission="view"
>     request_method="GET"
>     accept="application/json"
>     renderer="json"
>     />
> Everything works fine when I request the application/json and 
> application/json+xconfig mimetypes with a xhr since I can control the 
> "Accept" header, however, when a browser makes a "normal" request the 
> result is unpredictable (in practice) since '*/* is sent in the accept 
> header so all of the view predicates evaluate positively.
> I made a quick POC patch so the best match is taken into account 
> (attached). It modifies MultiView.__call__() so it narrows down the 
> predicates that it will evaluate using the request.accept.best_match() 
> method and it seems to work (all bfg tests still pass and the result is 
> as expected when I manually test my bfg app). The patch is against the 
> 1.1 branch in SVN.
> Is this the right approach? If so I would like to finish it up with 
> tests to reach full coverage and contribute it :)

The more I think about it Alberto, I think the patch you sent over is pretty 
much correct (definitely more correct than the current behavior; it must have 
taken a lot of thought, thank you).

I think we might want to change view registration so it operates like this:

- If there's not already a view callable registered for this
   context/request/viewname (this is the first view being registered for
   this particular triad), register a view callable with
   the accept value as a *predicate*, except also set __accept__ on the view
   callable like your patch does (in case another view gets registered for
   the same triad, resulting in a MultiView; we need to keep the value handy).
   Derived views without an "accepts=" in the set of registration arguments
   will be registered with "__accepts__ = None" attribute, while derived
   views with an "accepts=" in the set of registration arguments will be
   registered with an "__accepts__ = 'text/html'" (or whatever).

- If there is already a *non-MultiView* view callable registered for this
   triad, unregister the old view, and register a MultiView with both the old
   view and the new view.

- If there is already a *MultiView* view callable registered for this triad,
   add the new view to the multiview.

A MultiView will keep all views it represents that have the same "accepts" 
value in a "bucket" representing that accepts value.  Each "bucket" will be a 
sequence of (score, view_callable) ordered by score.  The buckets will be kept 
as the values of a dictionary attribute of the multiview.  The dictionary will 
be keyed by accept value.

The __call__ of a MultiView will do this:

- Find the "best" Accepts value for the currently registered set of accepts
   values for this multiview (the keys of the dictionary) by asking

- Retrieve the subset of views in this multiview that match the best accept
   value, and iterate over each of those, testing the predicates of each.
   If a view callable matches, call it and return the response.  If a no
   view callable matches, however, retest "best_match" *without* the previously
   tested "accepts" value to find the "next best" match, ad infinitum until
   we either find a view that matches or we run out of subsets to test.  If
   "best_match" does its job right, the very last subset we test will be the
   set of views that do not have *any* "accepts" value (the subset of views
   keyed on the None accepts value).

Obviously we'll put some shortcut logic in here so it will "go fast" when all 
views in the multiview are registered with "accepts=None".

Hows that?

- C

Repoze-dev mailing list

Reply via email to