[Cubicweb] Integrating CubicWeb views into Pyramid (long)
Christophe de Vienne
christophe at unlish.com
Mon Aug 4 20:59:00 CEST 2014
This mail is inteded as a discussion started about how CW views can be integrated in Pyramid.
I took some time to study the pyramid view selection system, and ended with a few scenarios.
For those who don't know Pyramid internal like I did this morning, I will try to describe what I understood.
For the others, please have a look to confirm I am not telling too much rubbish.
Pyramid views selection
This is only the important parts of what I understood of the system, not
an exhaustive description.
Registering a View
When we register a view in Pyramid, we pass various parameters to the
"add_view" function (or view_config decorator).
Three of them are the informations used for the first phase of the view
selection (later I call them the main predicates triplet):
Other parameters are predicates, like `accept`, `xhr` etc. These are evaluated
in a second phase, once the view(s) matching the first three are found.
For a complete list of the predicate parameters, see the second part of the
add_view documentation at
When registering a view, Pyramid will wrap it inside a ViewDeriver and register
the wrapper, using the main predicates triplet as a key for future lookups.
But if the view main predicates triplet matches an already registerd view, a
little dance happens:
- Get a MultiView:
* If the existing view is a IMultiView, it is kept.
* If not, a new one is instanciated, the existing view is added to it and
replaced by it in the registry.
- The new view is added to the multiview.
This is implemented in config/views.py, after line 1248
Selection of the right view
The initial lookup is done in pyramid/router.py line 141
view_callable = adapters.lookup(
(IViewClassifier, request.request_iface, context_iface),
IView, name=view_name, default=None)
The view_callable variable may correspond to a single view, or several ones.
In any case, we can call it with context and request, and this call will
eventually call the right view after, among other things, checking the view(s)
For single views, the check is done by ViewDeriver (config/views.py line 290 at
which basically wraps the view callable in layers that check everything needed
before getting to the actual view.
If the view_callable is actually a MultiView, it starts by selecting the first
view matching all the predicates. The MultiView class is in config/views.py
See in particular its 'match' function, this is were the predicates-based view
selection is done (line 570).
Now, you may wonder in which order the views are sorted in the multi-view.
The answer is the 'make' function of PredicateList in config/util.py line 117
It computes a 'order' value for the predicates of a view. This order basically
depends on the number of predicates that are in the view.
Hence, the more predicated on a view, the highest it gets in the list.
Integrating CW Views
Goal: Have Cubicweb Views selected by pyramid.
The selection behavior should be consistent with the cw predicates weight based
Several approaches should be studied, some less integrated than others.
Use a ViewMapper
Here, the idea is to register a single pseudo view for each view __regid__
present in the CW registry.
The view mapper associated with these pseudo views would do a view lookup on
the CW registry first, then call it for rendering.
* Easy to implement
* Need to keep two registries in the long term
* Two phases lookup: once in pyramid, once in CW.
* A lookup is performed when pyramid assumes it is finished and
successful, which means we do not respect the pyramid API (A
ViewMapper is just supposed to render an already selected view)
* CW views are not registered directly by pyramid
I don't like this solution because it is too much of a workaround
and we would not use the pyramid API, just wrapping stuffs.
Use a custom IMultiView
Implements a IMultiView (see pyramid.config.views.MultiView) that lookups in
the CW registry in hits __discriminator__.
One instance of this class would be registered for each __regid__, like with
the ViewMapper-based solution.
* Not too difficult to implement
* Respect more the pyramid API: the lookup is performed at a moment it is
expected by pyramid. In the end, pyramid will know the right view, and
any other system looking up for a view will find an actual one, not a
* The CW views are not registered directly in pyramid
* Still doing two lookups in two different registries.
Use CW predicates in add_view (basic)
Here we add a "cwselect" predicate to pyramid, that makes it able to evaluate
the cubicweb predicates.
* We by-pass the CW registry
* We loose the cw predicate weigths
Use CW predicates in add_view + total ordering
Here we choose to drop the runtime evaluation of the predicates weight.
Instead, we evaluate the weight of a predicate when it matches, and use that to
sort the views in the registry.
This would need either a slight change of the pyramid MultiView, which would
sort the views in this new order we compute instead of the default one, or a
change in the 'make' function so that the order of the view includes the
To use this system, we would need to duplicate the view registering when the
expression has some "or" operators in it. The idea is to obtain 'and-only'
predicate expressions for add_view.
The only blocking point against that would be if some actual cw predicates
returns a variable weight depending on the context, because it would make it
impossible to pre-evaluate an expression weight if it matches.
* By-pass the CW registry
* Very integrated solution
* We force the predicates to have a fixed value when they match.
Use CW predicates in add_view + cw predicate weight
Add runtine evalution of predicate weigths into pyramid.
No real clue on how we can to that (yet), although it will most probably
involve changes in MultiView.
Thank you if you made it so far, and please tell me what you think of the various
options, we need opinions to make progress.
More information about the Cubicweb