details:   https://code.tryton.org/tryton/commit/26aca1c56728
branch:    default
user:      Cédric Krier <[email protected]>
date:      Thu Jan 29 12:42:56 2026 +0100
description:
        Use tables and Model as arguments for sql_column of the Field
diffstat:

 modules/account_payment/payment.py             |   2 +-
 modules/bank/bank.py                           |   2 +-
 modules/country/country.py                     |   2 +-
 modules/product_cost_fifo/move.py              |   4 ++--
 trytond-gis/trytond_gis/fields.py              |   2 +-
 trytond/CHANGELOG                              |   1 +
 trytond/doc/ref/fields.rst                     |   5 +++--
 trytond/trytond/model/fields/char.py           |   8 ++++----
 trytond/trytond/model/fields/dict.py           |   6 ++----
 trytond/trytond/model/fields/field.py          |  14 +++++++-------
 trytond/trytond/model/fields/many2many.py      |  12 +++++++-----
 trytond/trytond/model/fields/many2one.py       |  18 +++++++++---------
 trytond/trytond/model/fields/multiselection.py |   3 +--
 trytond/trytond/model/fields/one2many.py       |   8 ++++----
 trytond/trytond/model/fields/reference.py      |   3 +--
 trytond/trytond/model/fields/selection.py      |   3 +--
 trytond/trytond/model/fields/text.py           |   5 ++---
 trytond/trytond/model/modelsql.py              |  24 ++++++++++++++----------
 18 files changed, 62 insertions(+), 60 deletions(-)

diffs (496 lines):

diff -r 0359c8ecc314 -r 26aca1c56728 modules/account_payment/payment.py
--- a/modules/account_payment/payment.py        Tue Feb 10 19:30:10 2026 +0100
+++ b/modules/account_payment/payment.py        Thu Jan 29 12:42:56 2026 +0100
@@ -547,7 +547,7 @@
     def order_amount(cls, tables):
         table, _ = tables[None]
         context = Transaction().context
-        column = cls.amount.sql_column(table)
+        column = cls.amount.sql_column(tables, cls)
         if isinstance(context.get('amount_order'), Decimal):
             return [Abs(column - abs(context['amount_order']))]
         else:
diff -r 0359c8ecc314 -r 26aca1c56728 modules/bank/bank.py
--- a/modules/bank/bank.py      Tue Feb 10 19:30:10 2026 +0100
+++ b/modules/bank/bank.py      Thu Jan 29 12:42:56 2026 +0100
@@ -245,7 +245,7 @@
         Operator = fields.SQL_OPERATORS[operator]
         result = None
         for field in (cls.number, cls.number_compact):
-            column = field.sql_column(table)
+            column = field.sql_column(tables, cls)
             expression = Operator(column, field._domain_value(operator, value))
             if isinstance(expression, operators.In) and not expression.right:
                 expression = Literal(False)
diff -r 0359c8ecc314 -r 26aca1c56728 modules/country/country.py
--- a/modules/country/country.py        Tue Feb 10 19:30:10 2026 +0100
+++ b/modules/country/country.py        Thu Jan 29 12:42:56 2026 +0100
@@ -437,7 +437,7 @@
 
         _, op, value = domain
         Operator = fields.SQL_OPERATORS[op]
-        column = cls.code.sql_column(table)
+        column = cls.code.sql_column(tables, cls)
         if op.endswith('like'):
             if op.endswith('ilike') and cls.code.search_unaccented:
                 database = Transaction().database
diff -r 0359c8ecc314 -r 26aca1c56728 modules/product_cost_fifo/move.py
--- a/modules/product_cost_fifo/move.py Tue Feb 10 19:30:10 2026 +0100
+++ b/modules/product_cost_fifo/move.py Thu Jan 29 12:42:56 2026 +0100
@@ -61,8 +61,8 @@
         field = cls.fifo_quantity_available._field
         Operator = fields.SQL_OPERATORS[operator]
         column = (
-            cls.quantity.sql_column(table)
-            - cls.fifo_quantity.sql_column(table))
+            cls.quantity.sql_column(tables, cls)
+            - cls.fifo_quantity.sql_column(tables, cls))
         expression = Operator(column, field._domain_value(operator, value))
         if isinstance(expression, operators.In) and not expression.right:
             expression = Literal(False)
diff -r 0359c8ecc314 -r 26aca1c56728 trytond-gis/trytond_gis/fields.py
--- a/trytond-gis/trytond_gis/fields.py Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond-gis/trytond_gis/fields.py Thu Jan 29 12:42:56 2026 +0100
@@ -42,7 +42,7 @@
 
         assert operator in GEOGRAPHIC_OPERATORS
 
-        column = self.sql_column(table)
+        column = self.sql_column(tables, Model)
 
         if operator in {'=', '!='} and value is Null:
             Operator = SQL_OPERATORS[operator]
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/CHANGELOG
--- a/trytond/CHANGELOG Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/CHANGELOG Thu Jan 29 12:42:56 2026 +0100
@@ -1,3 +1,4 @@
+* Use tables and Model as arguments for sql_column of the Field
 * Add support for basic authentication for user application
 * Support the conversion of MJML reports to HTML
 * Allow filtering users to be notified by cron tasks
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/doc/ref/fields.rst
--- a/trytond/doc/ref/fields.rst        Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/doc/ref/fields.rst        Thu Jan 29 12:42:56 2026 +0100
@@ -191,9 +191,10 @@
 
     Return the SQL expression with cast with the type of the field.
 
-.. method:: Field.sql_column(table)
+.. method:: Field.sql_column(tables, Model)
 
-    Return the Column instance based on table.
+    Return the Column or SQL expression instance for the field.
+    :ref:`tables <ref-tables>` could be updated to add new joins.
 
 .. method:: Field.set_rpc(model)
 
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/char.py
--- a/trytond/trytond/model/fields/char.py      Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/char.py      Thu Jan 29 12:42:56 2026 +0100
@@ -3,7 +3,7 @@
 import string
 import warnings
 
-from sql import Expression, Query
+from sql import Column, Expression, Query
 from sql.conditionals import Coalesce, NullIf
 from sql.functions import Trim
 from sql.operators import Not
@@ -143,10 +143,10 @@
                 language = transaction.language
                 model, join, column = self._get_translation_column(
                     Model, name)
-                column = Coalesce(NullIf(column, ''), self.sql_column(model))
+                column = Coalesce(NullIf(column, ''), Column(model, self.name))
             else:
                 language = None
-                column = self.sql_column(table)
+                column = self.sql_column(tables, Model)
             column = self._domain_column(operator, column)
 
             threshold = context.get(
@@ -207,7 +207,7 @@
                 column = self._get_translation_order(tables, Model, name)
             else:
                 language = None
-                column = self.sql_column(table)
+                column = self.sql_column(tables, Model)
             column = self._domain_column('ilike', column)
             if database.has_similarity():
                 sim_value = unescape_wildcard(value)
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/dict.py
--- a/trytond/trytond/model/fields/dict.py      Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/dict.py      Thu Jan 29 12:42:56 2026 +0100
@@ -119,10 +119,9 @@
         if '.' not in name:
             return super().convert_domain(domain, tables, Model)
         database = Transaction().database
-        table, _ = tables[None]
         name, key = name.split('.', 1)
         Operator = SQL_OPERATORS[operator]
-        raw_column = self.sql_column(table)
+        raw_column = self.sql_column(tables, Model)
         column = self._domain_column(operator, raw_column, key)
         expression = Operator(column, self._domain_value(operator, value))
         if operator in {'=', '!='}:
@@ -171,8 +170,7 @@
         if not key:
             return super().convert_order(fname, tables, Model)
         database = Transaction().database
-        table, _ = tables[None]
-        column = self.sql_column(table)
+        column = self.sql_column(tables, Model)
         return [database.json_get(column, key)]
 
     def definition(self, model, language):
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/field.py
--- a/trytond/trytond/model/fields/field.py     Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/field.py     Thu Jan 29 12:42:56 2026 +0100
@@ -421,7 +421,8 @@
     def sql_cast(self, expression):
         return Cast(expression, self.sql_type().base)
 
-    def sql_column(self, table):
+    def sql_column(self, tables, Model):
+        table, _ = tables[None]
         return Column(table, self.name)
 
     def _domain_column(self, operator, column):
@@ -451,7 +452,7 @@
         table, _ = tables[None]
         name, operator, value = domain
         Operator = SQL_OPERATORS[operator]
-        column = self.sql_column(table)
+        column = self.sql_column(tables, Model)
         column = self._domain_column(operator, column)
         expression = Operator(column, self._domain_value(operator, value))
         if isinstance(expression, operators.In) and not expression.right:
@@ -464,8 +465,7 @@
     @order_method
     def convert_order(self, name, tables, Model):
         "Return a SQL expression to order"
-        table, _ = tables[None]
-        return [self.sql_column(table)]
+        return [self.sql_column(tables, Model)]
 
     def set_rpc(self, model):
         for attribute, decorator, result in (
@@ -680,7 +680,7 @@
         table, _ = tables[None]
         name, operator, value = domain
         model, join, column = self._get_translation_column(Model, name)
-        column = Coalesce(NullIf(column, ''), self.sql_column(model))
+        column = Coalesce(NullIf(column, ''), Column(model, self.name))
         column = self._domain_column(operator, column)
         Operator = SQL_OPERATORS[operator]
         assert name == self.name
@@ -731,9 +731,9 @@
         if not self.translate:
             return super().convert_order(name, tables, Model)
         assert name == self.name
-        table, _ = tables[None]
         column = self._get_translation_order(tables, Model, name)
-        return [Coalesce(NullIf(column, ''), self.sql_column(table))]
+        return [
+            Coalesce(NullIf(column, ''), self.sql_column(tables, Model))]
 
     def definition(self, model, language):
         definition = super().definition(model, language)
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/many2many.py
--- a/trytond/trytond/model/fields/many2many.py Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/many2many.py Thu Jan 29 12:42:56 2026 +0100
@@ -384,14 +384,19 @@
         else:
             relation = Relation.__table__()
             history_where = None
+        relation_tables = {
+            None: (relation, None),
+            }
         origin_field = Relation._fields[self.origin]
-        origin = getattr(Relation, self.origin).sql_column(relation)
+        origin = getattr(Relation, self.origin).sql_column(
+            relation_tables, Relation)
         origin_where = None
         if origin_field._type == 'reference':
             origin_where = origin.like(Model.__name__ + ',%')
             origin = origin_field.sql_id(origin, Relation)
 
-        target = getattr(Relation, self.target).sql_column(relation)
+        target = getattr(Relation, self.target).sql_column(
+            relation_tables, Relation)
         if '.' not in name:
             if operator.endswith('child_of') or operator.endswith('parent_of'):
                 if Target != Model:
@@ -481,9 +486,6 @@
                 relation_domain,
                 (self.target, 'where', self.filter),
                 ]
-        relation_tables = {
-            None: (relation, None),
-            }
         tables, expression = Relation.search_domain(
             relation_domain, tables=relation_tables)
         query_table = convert_from(None, relation_tables)
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/many2one.py
--- a/trytond/trytond/model/fields/many2one.py  Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/many2one.py  Thu Jan 29 12:42:56 2026 +0100
@@ -133,7 +133,7 @@
         name, operator, ids = domain
         red_sql = reduce_ids(table.id, (i for i in ids if i is not None))
         Target = self.get_target()
-        path_column = getattr(Target, self.path).sql_column(table)
+        path_column = getattr(Target, self.path).sql_column(tables, Target)
         path_column = Coalesce(path_column, '')
         cursor.execute(*table.select(
                 path_column, where=red_sql,
@@ -165,8 +165,8 @@
         name, operator, ids = domain
         red_sql = reduce_ids(table.id, (i for i in ids if i is not None))
         Target = self.get_target()
-        left = getattr(Target, self.left).sql_column(table)
-        right = getattr(Target, self.right).sql_column(table)
+        left = getattr(Target, self.left).sql_column(tables, Target)
+        right = getattr(Target, self.right).sql_column(tables, Target)
         cursor.execute(*table.select(
                 left, right, where=red_sql,
                 order_by=[(right - left).asc, left.asc]))
@@ -228,7 +228,7 @@
 
         table, _ = tables[None]
         name, operator, value = domain[:3]
-        column = self.sql_column(table)
+        column = self.sql_column(tables, Model)
         if '.' not in name:
             if operator.endswith('child_of') or operator.endswith('parent_of'):
                 if Target != Model:
@@ -307,7 +307,7 @@
             expression = column.in_(query)
         else:
             target_domain = [target_domain, rule_domain]
-            target_tables = self._get_target_tables(tables)
+            target_tables = self._get_target_tables(tables, Model)
             target_table, _ = target_tables[None]
             _, expression = Target.search_domain(
                 target_domain, tables=target_tables)
@@ -338,13 +338,13 @@
 
         table, _ = tables[None]
         if oname == 'id':
-            return [self.sql_column(table)]
+            return [self.sql_column(tables, Model)]
 
         ofield = Target._fields[oname]
-        target_tables = self._get_target_tables(tables)
+        target_tables = self._get_target_tables(tables, Model)
         return ofield.convert_order(oexpr, target_tables, Target)
 
-    def _get_target_tables(self, tables):
+    def _get_target_tables(self, tables, Model):
         Target = self.get_target()
         table, _ = tables[None]
         target_tables = tables.get(self.name)
@@ -364,7 +364,7 @@
             else:
                 target = Target.__table__()
                 history_condition = None
-            condition = target.id == self.sql_column(table)
+            condition = target.id == self.sql_column(tables, Model)
             if history_condition:
                 condition &= history_condition
             target_tables = {
diff -r 0359c8ecc314 -r 26aca1c56728 
trytond/trytond/model/fields/multiselection.py
--- a/trytond/trytond/model/fields/multiselection.py    Tue Feb 10 19:30:10 
2026 +0100
+++ b/trytond/trytond/model/fields/multiselection.py    Thu Jan 29 12:42:56 
2026 +0100
@@ -90,8 +90,7 @@
         if operator not in {'in', 'not in'}:
             return super().convert_domain(domain, tables, Model)
         database = Transaction().database
-        table, _ = tables[None]
-        raw_column = self.sql_column(table)
+        raw_column = self.sql_column(tables, Model)
         if value is None:
             expression = Literal(False)
         elif isinstance(value, str):
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/one2many.py
--- a/trytond/trytond/model/fields/one2many.py  Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/one2many.py  Thu Jan 29 12:42:56 2026 +0100
@@ -338,8 +338,11 @@
         else:
             target = Target.__table__()
             history_where = None
+        target_tables = {
+            None: (target, None),
+            }
         origin_field = Target._fields[self.field]
-        origin = getattr(Target, self.field).sql_column(target)
+        origin = getattr(Target, self.field).sql_column(target_tables, Target)
         origin_where = None
         if origin_field._type == 'reference':
             origin_where = origin.like(Model.__name__ + ',%')
@@ -408,9 +411,6 @@
             target_domain = [target_domain, rule_domain]
         if self.filter:
             target_domain = [target_domain, self.filter]
-        target_tables = {
-            None: (target, None),
-            }
         tables, expression = Target.search_domain(
             target_domain, tables=target_tables)
         query_table = convert_from(None, target_tables)
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/reference.py
--- a/trytond/trytond/model/fields/reference.py Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/reference.py Thu Jan 29 12:42:56 2026 +0100
@@ -204,10 +204,9 @@
         pool = Pool()
         name, operator, value, target = domain[:4]
         Target = pool.get(target)
-        table, _ = tables[None]
         name, target_name = name.split('.', 1)
         assert name == self.name
-        column = self.sql_column(table)
+        column = self.sql_column(tables, Model)
         target_domain = [(target_name,) + tuple(domain[1:3])
             + tuple(domain[4:])]
         if 'active' in Target._fields:
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/selection.py
--- a/trytond/trytond/model/fields/selection.py Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/selection.py Thu Jan 29 12:42:56 2026 +0100
@@ -146,14 +146,13 @@
 
     @order_method
     def convert_order(self, name, tables, Model):
-        table, _ = tables[None]
         selections = Model.fields_get([name])[name]['selection']
         if not isinstance(selections, (tuple, list)):
             if not is_instance_method(Model, selections):
                 selections = getattr(Model, selections)()
             else:
                 selections = []
-        column = self.sql_column(table)
+        column = self.sql_column(tables, Model)
         if not self.sort:
             else_ = len(selections) + 1
             selections = ((k, i) for i, (k, v) in enumerate(selections))
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/fields/text.py
--- a/trytond/trytond/model/fields/text.py      Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/fields/text.py      Thu Jan 29 12:42:56 2026 +0100
@@ -54,7 +54,7 @@
         table, _ = tables[None]
         name, operator, value = domain
         assert name == self.name
-        column = self.sql_column(table)
+        column = self.sql_column(tables, Model)
         column = self._domain_column(operator, column)
         if operator.endswith('like'):
             if database.has_search_full_text():
@@ -81,8 +81,7 @@
 
     @order_method
     def convert_order(self, name, tables, Model):
-        table, _ = tables[None]
-        column = self.sql_column(table)
+        column = self.sql_column(tables, Model)
         column = self._domain_column('ilike', column)
         column = self._rank_column(column, name, Model)
         if column:
diff -r 0359c8ecc314 -r 26aca1c56728 trytond/trytond/model/modelsql.py
--- a/trytond/trytond/model/modelsql.py Tue Feb 10 19:30:10 2026 +0100
+++ b/trytond/trytond/model/modelsql.py Thu Jan 29 12:42:56 2026 +0100
@@ -1192,6 +1192,7 @@
             history_clause = (column <= Transaction().context['_datetime'])
             history_order = (column.desc, Column(table, '__id').desc)
             history_limit = 1
+        tables = {None: (table, None)}
 
         columns = {}
         for f in all_fields:
@@ -1200,7 +1201,7 @@
                 if f in _TABLE_QUERY_COLUMNS and cls._is_table_query():
                     column = _TABLE_QUERY_COLUMNS[f]
                 else:
-                    column = field.sql_column(table)
+                    column = field.sql_column(tables, cls)
                 columns[f] = column.as_(f)
                 if backend.name == 'sqlite':
                     columns[f].output_name += ' [%s]' % field.sql_type().base
@@ -1241,7 +1242,6 @@
             if 'id' not in fields_names:
                 columns['id'] = table.id.as_('id')
 
-            tables = {None: (table, None)}
             if domain:
                 tables, dom_exp = cls.search_domain(
                     domain, active_test=False, tables=tables)
@@ -1581,10 +1581,9 @@
 
         tree_ids = {}
         for fname in cls._mptt_fields:
-            field = cls._fields[fname]
             tree_ids[fname] = []
             for sub_ids in grouped_slice(ids):
-                where = reduce_ids(field.sql_column(table), sub_ids)
+                where = reduce_ids(Column(table, fname), sub_ids)
                 cursor.execute(*table.select(table.id, where=where))
                 tree_ids[fname] += [x[0] for x in cursor]
 
@@ -1841,9 +1840,8 @@
                         rule_domain, active_test=False, tables=tables)
                     expression &= domain_exp
                 main_table, _ = tables[None]
-                table = convert_from(None, tables)
                 columns = cls.__searched_columns(
-                    main_table, eager=not count and not query)
+                    tables, eager=not count and not query)
 
                 o_idx = 0
                 for oexpr, otype in order:
@@ -1858,6 +1856,7 @@
                         orderings.extend([otype] * len(forder))
                 done_orderings = True
 
+                table = convert_from(None, tables)
                 union_tables.append(table.select(
                         *columns, where=expression))
             expression = None
@@ -1874,7 +1873,8 @@
         return tables, expression, orderings
 
     @classmethod
-    def __searched_columns(cls, table, *, eager=False, history=False):
+    def __searched_columns(cls, tables, *, eager=False, history=False):
+        table, _ = tables[None]
         columns = [table.id.as_('id')]
         if (cls._history and Transaction().context.get('_datetime')
                 and (eager or history)):
@@ -1884,7 +1884,7 @@
 
         if eager:
             table_query = cls._is_table_query()
-            columns += [f.sql_column(table).as_(n)
+            columns += [f.sql_column(tables, cls).as_(n)
                 for n, f in sorted(cls._fields.items())
                 if not hasattr(f, 'get')
                     and n != 'id'
@@ -1955,16 +1955,20 @@
             order_by = cls.__search_order(order, tables)
 
         # compute it here because __search_order might modify tables
-        table = convert_from(None, tables)
         if query:
             columns = [main_table.id.as_('id')]
         else:
-            columns = cls.__searched_columns(main_table, eager=True)
+            columns = cls.__searched_columns(tables, eager=True)
+            if union_orderings:
+                columns = [
+                    Column(main_table, c.output_name).as_(c.output_name)
+                    for c in columns]
             if backend.name == 'sqlite':
                 for column in columns:
                     field = cls._fields.get(column.output_name)
                     if field:
                         column.output_name += ' [%s]' % field.sql_type().base
+        table = convert_from(None, tables)
         select = table.select(
             *columns, where=expression, limit=limit, offset=offset,
             order_by=order_by)

Reply via email to