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 request.accept.best_match. - 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 Repoze-dev@lists.repoze.org http://lists.repoze.org/listinfo/repoze-dev