[entity] Make it possible to specify sorting terms in cw_related_rqlst method
Closes #12306543.
--- a/cubicweb/entity.py Thu Jun 02 17:52:43 2016 +0200
+++ b/cubicweb/entity.py Fri Apr 15 10:20:34 2016 +0200
@@ -1012,7 +1012,19 @@
return self.cw_related_rqlst(
rtype, role=role, targettypes=targettypes, limit=limit).as_string()
- def cw_related_rqlst(self, rtype, role='subject', targettypes=None, limit=None):
+ def cw_related_rqlst(self, rtype, role='subject', targettypes=None,
+ limit=None, sort_terms=None):
+ """Return the select node of the RQL query of entities related through
+ `rtype` with this entity as `role`, possibly filtered by
+ `targettypes`.
+
+ The RQL query can be given a `limit` and sort terms with `sort_terms`
+ arguments being a sequence of ``(<relation type>, <sort ascending>)``
+ (e.g. ``[('name', True), ('modification_date', False)]`` would lead to
+ a sorting by ``name``, ascending and then by ``modification_date``,
+ descending. If `sort_terms` is not specified the default sorting is by
+ ``modification_date``, descending.
+ """
vreg = self._cw.vreg
rschema = vreg.schema[rtype]
select = Select()
@@ -1053,22 +1065,31 @@
if gcard == '1':
select.remove_sort_terms()
elif not select.orderby:
- # if modification_date is already retrieved, we use it instead
- # of adding another variable for sorting. This should not be
- # problematic, but it is with sqlserver, see ticket #694445
- for rel in select.where.get_nodes(RqlRelation):
- if (rel.r_type == 'modification_date'
- and rel.children[0].variable == mainvar
- and rel.children[1].operator == '='):
- var = rel.children[1].children[0].variable
- select.add_sort_var(var, asc=False)
- break
- else:
- mdvar = select.make_variable()
- rel = make_relation(mainvar, 'modification_date',
- (mdvar,), VariableRef)
- select.add_restriction(rel)
- select.add_sort_var(mdvar, asc=False)
+ # Build a mapping (rtype, node) for relations usable for sorting.
+ sorting_relations = {}
+ for r in select.where.get_nodes(RqlRelation):
+ lhs, rhs = r.children
+ if lhs.variable != mainvar:
+ continue
+ if r.operator() != '=':
+ continue
+ rhs_term = rhs.children[0]
+ if not isinstance(rhs_term, VariableRef):
+ continue
+ sorting_relations[r.r_type] = r
+ sort_terms = sort_terms or [('modification_date', False)]
+ for term, order in sort_terms:
+ # Add a relation for sorting only if it is not only retrieved
+ # (e.g. modification_date) instead of adding another variable
+ # for sorting. This should not be problematic, but it is with
+ # sqlserver, see ticket #694445.
+ rel = sorting_relations.get(term)
+ if rel is None:
+ mdvar = select.make_variable()
+ rel = make_relation(mainvar, term, (mdvar,), VariableRef)
+ select.add_restriction(rel)
+ var = rel.children[1].children[0].variable
+ select.add_sort_var(var, asc=order)
return select
# generic vocabulary methods ##############################################
--- a/cubicweb/test/unittest_entity.py Thu Jun 02 17:52:43 2016 +0200
+++ b/cubicweb/test/unittest_entity.py Fri Apr 15 10:20:34 2016 +0200
@@ -319,6 +319,17 @@
'WHERE E eid %(x)s, E tags X, X is Personne, X modification_date AA, '
'X nom AB')
+ def test_related_rql_sort_terms(self):
+ with self.admin_access.web_request() as req:
+ tag = self.vreg['etypes'].etype_class('Tag')(req)
+ select = tag.cw_related_rqlst('tags', 'subject',
+ sort_terms=(('nom', True),
+ ('modification_date', False)))
+ expected = (
+ 'Any X,AA ORDERBY AB,AA DESC '
+ 'WHERE E eid %(x)s, E tags X, X modification_date AA, X nom AB')
+ self.assertEqual(select.as_string(), expected)
+
def test_related_rql_ambiguous_cant_use_fetch_order(self):
with self.admin_access.web_request() as req:
tag = self.vreg['etypes'].etype_class('Tag')(req)