# HG changeset patch # User Denis Laxalde # Date 1460708434 -7200 # Node ID 12d226a5bab96b66cce7df1f49424e946068dcf7 # Parent 0b38e373b9856d2a73e3d16cc322e07504579295 [entity] Make it possible to specify sorting terms in cw_related_rqlst method Closes #12306543. diff -r 0b38e373b985 -r 12d226a5bab9 cubicweb/entity.py --- 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 ``(, )`` + (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 ############################################## diff -r 0b38e373b985 -r 12d226a5bab9 cubicweb/test/unittest_entity.py --- 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)