details: https://code.tryton.org/tryton/commit/e1657467e49c
branch: default
user: Cédric Krier <[email protected]>
date: Thu Jun 04 23:41:21 2026 +0200
description:
Add row factory to cursor
Closes #14797
diffstat:
modules/account/tax.py | 8 +-
modules/account_payment/payment.py | 18 ++--
modules/account_statement/journal.py | 13 +-
modules/account_stock_eu_excise/account_stock_eu.py | 13 ++-
trytond/CHANGELOG | 2 +
trytond/doc/ref/backend.rst | 16 ++++-
trytond/trytond/backend/__init__.py | 6 +-
trytond/trytond/backend/postgresql/__init__.py | 6 +-
trytond/trytond/backend/postgresql/database.py | 10 ++-
trytond/trytond/backend/sqlite/__init__.py | 6 +-
trytond/trytond/backend/sqlite/database.py | 25 ++++++-
trytond/trytond/ir/model.py | 11 +-
trytond/trytond/ir/translation.py | 76 +++++++++-----------
trytond/trytond/model/modelsql.py | 17 ++-
trytond/trytond/tools/__init__.py | 11 ---
15 files changed, 142 insertions(+), 96 deletions(-)
diffs (742 lines):
diff -r 1d35758d7739 -r e1657467e49c modules/account/tax.py
--- a/modules/account/tax.py Thu Jun 04 23:07:09 2026 +0200
+++ b/modules/account/tax.py Thu Jun 04 23:41:21 2026 +0200
@@ -19,8 +19,7 @@
from trytond.modules.currency.fields import Monetary
from trytond.pool import Pool
from trytond.pyson import Bool, Eval, If, PYSONEncoder
-from trytond.tools import (
- cursor_dict, is_full_text, lstrip_wildcard, sqlite_apply_types)
+from trytond.tools import is_full_text, lstrip_wildcard, sqlite_apply_types
from trytond.transaction import Transaction
from trytond.wizard import Button, StateAction, StateView, Wizard
@@ -884,7 +883,7 @@
Move = pool.get('account.move')
MoveLine = pool.get('account.move.line')
TaxLine = pool.get('account.tax.line')
- cursor = Transaction().connection.cursor()
+ connection = Transaction().connection
move = Move.__table__()
move_line = MoveLine.__table__()
@@ -943,8 +942,9 @@
if backend.name == 'sqlite':
sqlite_apply_types(query, [None] + ['NUMERIC'] * len(names))
+ cursor = connection.cursor(row_factory=backend.dict_row)
cursor.execute(*query)
- for row in cursor_dict(cursor):
+ for row in cursor:
for name in names:
result[name][row['tax']] = row[name] or 0
return result
diff -r 1d35758d7739 -r e1657467e49c modules/account_payment/payment.py
--- a/modules/account_payment/payment.py Thu Jun 04 23:07:09 2026 +0200
+++ b/modules/account_payment/payment.py Thu Jun 04 23:41:21 2026 +0200
@@ -21,7 +21,7 @@
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval, If
from trytond.rpc import RPC
-from trytond.tools import cursor_dict, sortable_values, sqlite_apply_types
+from trytond.tools import sortable_values, sqlite_apply_types
from trytond.transaction import Transaction
from trytond.wizard import StateAction, Wizard
@@ -182,7 +182,7 @@
def get_payment_aggregated(cls, groups, names):
pool = Pool()
Payment = pool.get('account.payment')
- cursor = Transaction().connection.cursor()
+ connection = Transaction().connection
payment = Payment.__table__()
@@ -207,16 +207,16 @@
if backend.name == 'sqlite':
sqlite_apply_types(
query, [None, None, 'NUMERIC', 'NUMERIC', None])
+ cursor = connection.cursor(row_factory=backend.namedtuple_row)
cursor.execute(*query)
- for row in cursor_dict(cursor):
- group = cls(row['group_id'])
+ for row in cursor:
+ group = cls(row.group_id)
- result['payment_count'][group.id] = row['payment_count']
- result['payment_complete'][group.id] = \
- not row['payment_not_complete']
+ result['payment_count'][group.id] = row.payment_count
+ result['payment_complete'][group.id] = not row.payment_not_complete
- amount = row['payment_amount']
- succeeded = row['payment_amount_succeeded']
+ amount = row.payment_amount
+ succeeded = row.payment_amount_succeeded
if amount is not None and backend.name == 'sqlite':
amount = group.company.currency.round(amount)
diff -r 1d35758d7739 -r e1657467e49c modules/account_statement/journal.py
--- a/modules/account_statement/journal.py Thu Jun 04 23:07:09 2026 +0200
+++ b/modules/account_statement/journal.py Thu Jun 04 23:41:21 2026 +0200
@@ -12,7 +12,7 @@
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.rpc import RPC
-from trytond.tools import cursor_dict, sqlite_apply_types
+from trytond.tools import sqlite_apply_types
from trytond.transaction import Transaction
@@ -125,7 +125,7 @@
Statement = pool.get('account.statement')
statement = Statement.__table__()
transaction = Transaction()
- cursor = transaction.connection.cursor()
+ connection = transaction.connection
id2currency = {j.id: j.currency for j in journals}
@@ -156,12 +156,13 @@
if 'last_date' in names:
types.append('DATE')
sqlite_apply_types(query, types)
+ cursor = connection.cursor(row_factory=backend.namedtuple_row)
cursor.execute(*query)
- for row in cursor_dict(cursor):
- journal = row['journal']
+ for row in cursor:
+ journal = row.journal
if 'last_amount' in names:
result['last_amount'][journal] = (
- id2currency[journal].round(row['last_amount']))
+ id2currency[journal].round(row.last_amount))
if 'last_date' in names:
- result['last_date'][journal] = row['last_date']
+ result['last_date'][journal] = row.last_date
return result
diff -r 1d35758d7739 -r e1657467e49c
modules/account_stock_eu_excise/account_stock_eu.py
--- a/modules/account_stock_eu_excise/account_stock_eu.py Thu Jun 04
23:07:09 2026 +0200
+++ b/modules/account_stock_eu_excise/account_stock_eu.py Thu Jun 04
23:41:21 2026 +0200
@@ -12,12 +12,13 @@
from sql.conditionals import Case
from sql.operators import Exists
+from trytond import backend
from trytond.i18n import gettext
from trytond.model import (
DeactivableMixin, MatchMixin, ModelSQL, ModelView, fields)
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval, Id, If
-from trytond.tools import cursor_dict, decistmt
+from trytond.tools import decistmt
from trytond.tools.decimal_ import DecimalNull
from trytond.transaction import Transaction
@@ -541,7 +542,7 @@
Move = pool.get('stock.move')
transaction = Transaction()
context = transaction.context
- cursor = transaction.connection.cursor()
+ connection = transaction.connection
move = Move.__table__()
location = Location.__table__()
@@ -578,8 +579,9 @@
declaration_ids = [s.id for s in declarations]
query.where = where & fields.SQL_OPERATORS['in'](
move.product, declaration_ids)
+ cursor = connection.cursor(row_factory=backend.dict_row)
cursor.execute(*query)
- for values in cursor_dict(cursor):
+ for values in cursor:
declaration = id2declaration[values['id']]
for name in names:
if qty := values[name]:
@@ -614,7 +616,7 @@
Move = pool.get('stock.move')
transaction = Transaction()
context = transaction.context
- cursor = transaction.connection.cursor()
+ connection = transaction.connection
move = Move.__table__()
location = Location.__table__()
@@ -651,8 +653,9 @@
declaration_ids = [s.id for s in declarations]
query.where = where & fields.SQL_OPERATORS['in'](
move.product, declaration_ids)
+ cursor = connection.cursor(row_factory=backend.dict_row)
cursor.execute(*query)
- for values in cursor_dict(cursor):
+ for values in cursor:
declaration = id2declaration[values['id']]
for name in names:
if qty := values[name]:
diff -r 1d35758d7739 -r e1657467e49c trytond/CHANGELOG
--- a/trytond/CHANGELOG Thu Jun 04 23:07:09 2026 +0200
+++ b/trytond/CHANGELOG Thu Jun 04 23:41:21 2026 +0200
@@ -1,3 +1,5 @@
+* Remove cursor_dict
+* Add row factory to cursor
* Add filename extension to Binary field
* Add filters attribute for binary and image widgets
* Move last_user and last_modification to ResourceAccessMixin
diff -r 1d35758d7739 -r e1657467e49c trytond/doc/ref/backend.rst
--- a/trytond/doc/ref/backend.rst Thu Jun 04 23:07:09 2026 +0200
+++ b/trytond/doc/ref/backend.rst Thu Jun 04 23:41:21 2026 +0200
@@ -20,6 +20,19 @@
The maximum number of parameters supported by the backend.
+.. attribute:: dict_row
+
+ A row factory for cursor returning rows as :py:class:`dictionary <dict>`.
+
+.. attribute:: namedtuple_row
+
+ A row factory for cursor returning rows as
+ :py:func:`~collections.namedtuple`.
+
+.. attribute:: scalar_row
+
+ A row factory for cursor returning the first column as a scalar value.
+
Database
========
@@ -33,7 +46,8 @@
.. method:: Database.get_connection([autocommit[, readonly]])
- Retrieve a connection object as defined by :pep:`249#connection`.
+ Retrieve a connection object as defined by :pep:`249#connection`, bearing in
+ mind that the ``cursor()`` method accepts a row_factory keyword parameter.
If autocommit is set, the connection is committed after each statement.
If readonly is set, the connection is read only.
diff -r 1d35758d7739 -r e1657467e49c trytond/trytond/backend/__init__.py
--- a/trytond/trytond/backend/__init__.py Thu Jun 04 23:07:09 2026 +0200
+++ b/trytond/trytond/backend/__init__.py Thu Jun 04 23:41:21 2026 +0200
@@ -13,7 +13,8 @@
__all__ = [
'name', 'Database', 'TableHandler',
'DatabaseIntegrityError', 'DatabaseDataError', 'DatabaseOperationalError',
- 'DatabaseTimeoutError', 'MAX_QUERY_PARAMS']
+ 'DatabaseTimeoutError', 'MAX_QUERY_PARAMS',
+ 'dict_row', 'namedtuple_row', 'scalar_row']
name = urllib.parse.urlparse(config.get('database', 'uri', default='')).scheme
@@ -38,3 +39,6 @@
DatabaseTimeoutError = _module.DatabaseTimeoutError
TableHandler = _module.TableHandler
MAX_QUERY_PARAMS = _module.MAX_QUERY_PARAMS
+dict_row = _module.dict_row
+namedtuple_row = _module.namedtuple_row
+scalar_row = _module.scalar_row
diff -r 1d35758d7739 -r e1657467e49c
trytond/trytond/backend/postgresql/__init__.py
--- a/trytond/trytond/backend/postgresql/__init__.py Thu Jun 04 23:07:09
2026 +0200
+++ b/trytond/trytond/backend/postgresql/__init__.py Thu Jun 04 23:41:21
2026 +0200
@@ -3,7 +3,8 @@
from .database import (
Database, DatabaseDataError, DatabaseIntegrityError,
- DatabaseOperationalError, DatabaseTimeoutError)
+ DatabaseOperationalError, DatabaseTimeoutError, dict_row, namedtuple_row,
+ scalar_row)
from .table import TableHandler
MAX_QUERY_PARAMS = 50_000 # rounded down from 65_535
@@ -14,6 +15,9 @@
DatabaseIntegrityError,
DatabaseOperationalError,
DatabaseTimeoutError,
+ dict_row,
+ namedtuple_row,
+ scalar_row,
MAX_QUERY_PARAMS,
TableHandler,
]
diff -r 1d35758d7739 -r e1657467e49c
trytond/trytond/backend/postgresql/database.py
--- a/trytond/trytond/backend/postgresql/database.py Thu Jun 04 23:07:09
2026 +0200
+++ b/trytond/trytond/backend/postgresql/database.py Thu Jun 04 23:41:21
2026 +0200
@@ -16,7 +16,7 @@
from psycopg import IntegrityError as DatabaseIntegrityError
from psycopg import IsolationLevel
from psycopg import OperationalError as DatabaseOperationalError
-from psycopg import connect
+from psycopg import connect, rows
from psycopg.errors import QueryCanceled as DatabaseTimeoutError
from psycopg.sql import SQL, Identifier
from psycopg_pool import ConnectionPool
@@ -35,7 +35,8 @@
__all__ = [
'Database',
'DatabaseIntegrityError', 'DatabaseDataError', 'DatabaseOperationalError',
- 'DatabaseTimeoutError']
+ 'DatabaseTimeoutError',
+ 'dict_row', 'namedtuple_row', 'scalar_row']
logger = logging.getLogger(__name__)
@@ -63,6 +64,11 @@
return super().execute(query, params, prepare=prepare, binary=binary)
+dict_row = rows.dict_row
+namedtuple_row = rows.namedtuple_row
+scalar_row = rows.scalar_row
+
+
class ForSkipLocked(For):
def __str__(self):
assert not self.nowait, "Can not use both NO WAIT and SKIP LOCKED"
diff -r 1d35758d7739 -r e1657467e49c trytond/trytond/backend/sqlite/__init__.py
--- a/trytond/trytond/backend/sqlite/__init__.py Thu Jun 04 23:07:09
2026 +0200
+++ b/trytond/trytond/backend/sqlite/__init__.py Thu Jun 04 23:41:21
2026 +0200
@@ -6,7 +6,8 @@
from .database import (
Database, DatabaseDataError, DatabaseIntegrityError,
- DatabaseOperationalError, DatabaseTimeoutError)
+ DatabaseOperationalError, DatabaseTimeoutError, dict_row, namedtuple_row,
+ scalar_row)
from .table import TableHandler
MAX_QUERY_PARAMS = 200 # estimation from the SQLITE_MAX_EXPR_DEPTH=1_000
@@ -20,6 +21,9 @@
DatabaseIntegrityError,
DatabaseOperationalError,
DatabaseTimeoutError,
+ dict_row,
+ namedtuple_row,
+ scalar_row,
MAX_QUERY_PARAMS,
TableHandler,
]
diff -r 1d35758d7739 -r e1657467e49c trytond/trytond/backend/sqlite/database.py
--- a/trytond/trytond/backend/sqlite/database.py Thu Jun 04 23:07:09
2026 +0200
+++ b/trytond/trytond/backend/sqlite/database.py Thu Jun 04 23:41:21
2026 +0200
@@ -10,6 +10,7 @@
import time
import urllib.parse
import warnings
+from collections import namedtuple
from decimal import Decimal
from sqlite3 import DatabaseError
from sqlite3 import IntegrityError as DatabaseIntegrityError
@@ -32,7 +33,8 @@
__all__ = [
'Database',
'DatabaseIntegrityError', 'DatabaseDataError', 'DatabaseOperationalError',
- 'DatabaseTimeoutError']
+ 'DatabaseTimeoutError',
+ 'dict_row', 'namedtuple_row', 'scalar_row']
logger = logging.getLogger(__name__)
_default_name = config.get('database', 'default_name', default=':memory:')
@@ -455,8 +457,25 @@
class SQLiteConnection(sqlite.Connection):
- def cursor(self):
- return super().cursor(SQLiteCursor)
+ def cursor(self, *, row_factory=None):
+ cursor = super().cursor(SQLiteCursor)
+ cursor.row_factory = row_factory
+ return cursor
+
+
+def dict_row(cursor, row):
+ fields = [column[0] for column in cursor.description]
+ return {key: value for key, value in zip(fields, row)}
+
+
+def namedtuple_row(cursor, row):
+ fields = [column[0] for column in cursor.description]
+ cls = namedtuple("Row", fields)
+ return cls._make(row)
+
+
+def scalar_row(cursor, row):
+ return row[0]
class Database(DatabaseInterface):
diff -r 1d35758d7739 -r e1657467e49c trytond/trytond/ir/model.py
--- a/trytond/trytond/ir/model.py Thu Jun 04 23:07:09 2026 +0200
+++ b/trytond/trytond/ir/model.py Thu Jun 04 23:41:21 2026 +0200
@@ -12,6 +12,7 @@
from sql.operators import Equal
import trytond.config as config
+from trytond import backend
from trytond.cache import Cache
from trytond.i18n import gettext
from trytond.model import (
@@ -22,7 +23,7 @@
from trytond.pyson import Bool, Eval, PYSONDecoder
from trytond.report import Report
from trytond.rpc import RPC
-from trytond.tools import cursor_dict, is_instance_method
+from trytond.tools import is_instance_method
from trytond.tools.string_ import StringMatcher
from trytond.transaction import Transaction, without_check_access
from trytond.wizard import (
@@ -352,11 +353,13 @@
pool = Pool()
Model = pool.get('ir.model')
- cursor = Transaction().connection.cursor()
+ connection = Transaction().connection
+ cursor_select = connection.cursor(row_factory=backend.dict_row)
+ cursor = connection.cursor()
ir_model_field = cls.__table__()
- cursor.execute(*ir_model_field
+ cursor_select.execute(*ir_model_field
.select(
ir_model_field.id.as_('id'),
ir_model_field.name.as_('name'),
@@ -367,7 +370,7 @@
ir_model_field.help.as_('help'),
ir_model_field.access.as_('access'),
where=ir_model_field.model == model.__name__))
- model_fields = {f['name']: f for f in cursor_dict(cursor)}
+ model_fields = {f['name']: f for f in cursor_select}
for field_name, field in model._fields.items():
if hasattr(field, 'get_target'):
diff -r 1d35758d7739 -r e1657467e49c trytond/trytond/ir/translation.py
--- a/trytond/trytond/ir/translation.py Thu Jun 04 23:07:09 2026 +0200
+++ b/trytond/trytond/ir/translation.py Thu Jun 04 23:41:21 2026 +0200
@@ -17,13 +17,14 @@
from sql.operators import Concat
import trytond.config as config
+from trytond import backend
from trytond.cache import Cache
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.model import Index, ModelSQL, ModelView, fields
from trytond.pool import Pool
from trytond.pyson import Eval, PYSONEncoder
-from trytond.tools import cursor_dict, file_open
+from trytond.tools import file_open
from trytond.tools.string_ import LazyString, StringPartitioned
from trytond.transaction import (
Transaction, inactive_records, without_check_access)
@@ -185,7 +186,7 @@
@classmethod
def register_fields(cls, model, module_name):
- cursor = Transaction().connection.cursor()
+ connection = Transaction().connection
ir_translation = cls.__table__()
# Prefetch field translations
@@ -195,6 +196,7 @@
selection=defaultdict(dict))
if model._fields:
names = ['%s,%s' % (model.__name__, f) for f in model._fields]
+ cursor = connection.cursor(row_factory=backend.dict_row)
cursor.execute(*ir_translation.select(ir_translation.id,
ir_translation.name, ir_translation.src,
ir_translation.type,
@@ -202,13 +204,14 @@
& ir_translation.type.in_(
('field', 'help', 'selection'))
& ir_translation.name.in_(names))))
- for trans in cursor_dict(cursor):
+ for trans in cursor:
sources = translations[trans['type']][trans['name']]
sources[trans['src']] = trans
columns = [ir_translation.name, ir_translation.lang,
ir_translation.type, ir_translation.src, ir_translation.value,
ir_translation.module, ir_translation.fuzzy, ir_translation.res_id]
+ cursor = connection.cursor()
def insert(field, type, name, string):
inserted = False
@@ -241,16 +244,19 @@
@classmethod
def register_wizard(cls, wizard, module_name):
- cursor = Transaction().connection.cursor()
+ connection = Transaction().connection
ir_translation = cls.__table__()
# Prefetch button translations
+ cursor = connection.cursor(row_factory=backend.dict_row)
cursor.execute(*ir_translation.select(
ir_translation.id, ir_translation.name, ir_translation.src,
where=((ir_translation.lang == INTERNAL_LANG)
& (ir_translation.type == 'wizard_button')
& (ir_translation.name.like(wizard.__name__ + ',%')))))
- trans_buttons = {t['name']: t for t in cursor_dict(cursor)}
+ trans_buttons = {t['name']: t for t in cursor}
+
+ cursor = connection.cursor()
def update_insert_button(state_name, button):
if not button.string:
@@ -1178,9 +1184,11 @@
else:
return
- cursor = Transaction().connection.cursor()
+ connection = Transaction().connection
translation = Translation.__table__()
report_strings = defaultdict(set)
+ cursor = connection.cursor()
+ cursor_select = connection.cursor(row_factory=backend.dict_row)
for report in reports:
content = None
if report.report:
@@ -1193,7 +1201,7 @@
if not content:
continue
- cursor.execute(*translation.select(
+ cursor_select.execute(*translation.select(
translation.id, translation.name,
translation.src, translation.src_plural,
where=(translation.lang == INTERNAL_LANG)
@@ -1202,7 +1210,7 @@
& (translation.module == module)))
trans_reports = {
(t['src'], t['src_plural']): t
- for t in cursor_dict(cursor)}
+ for t in cursor_select}
strings = report_strings[report.report_name, report.module]
func_name = 'extract_report_%s' % report.template_extension
@@ -1271,16 +1279,18 @@
else:
return
- cursor = Transaction().connection.cursor()
+ connection = Transaction().connection
translation = Translation.__table__()
+ cursor = connection.cursor()
+ cursor_select = connection.cursor(row_factory=backend.dict_row)
for view in views:
- cursor.execute(*translation.select(
+ cursor_select.execute(*translation.select(
translation.id, translation.name, translation.src,
where=(translation.lang == INTERNAL_LANG)
& (translation.type == 'view')
& (translation.name == view.model)
& (translation.module == view.module)))
- trans_views = {t['src']: t for t in cursor_dict(cursor)}
+ trans_views = {t['src']: t for t in cursor_select}
xml = (view.arch or '').strip()
if not xml:
@@ -1574,8 +1584,9 @@
Translation = pool.get('ir.translation')
Report = pool.get('ir.action.report')
View = pool.get('ir.ui.view')
- cursor = Transaction().connection.cursor()
- cursor_update = Transaction().connection.cursor()
+ connection = Transaction().connection
+ cursor = connection.cursor(row_factory=backend.dict_row)
+ cursor_update = connection.cursor()
translation = Translation.__table__()
lang = self.start.language.code
parent_lang = get_parent(lang)
@@ -1608,18 +1619,7 @@
where=(translation.lang == lang)
& source_clause
& translation.type.in_(self._source_types))))
- to_create = []
- for row in cursor_dict(cursor):
- to_create.append({
- 'name': row['name'],
- 'res_id': row['res_id'],
- 'lang': lang,
- 'type': row['type'],
- 'src': row['src'],
- 'src_plural': row['src_plural'],
- 'module': row['module'],
- })
- if to_create:
+ if to_create := list(cursor):
Translation.create(to_create)
if parent_lang:
@@ -1632,7 +1632,7 @@
where=(translation.lang == lang)
& source_clause
& translation.type.in_(self._source_types))))
- for row in cursor_dict(cursor):
+ for row in cursor:
cursor_update.execute(*translation.update(
[translation.value],
[''],
@@ -1647,8 +1647,11 @@
if self.model in {Report, View}:
return
- columns = [translation.name.as_('name'),
- translation.res_id.as_('res_id'), translation.type.as_('type'),
+ columns = [
+ translation.name.as_('name'),
+ translation.res_id.as_('res_id'),
+ Literal(lang).as_('lang'),
+ translation.type.as_('type'),
translation.module.as_('module')]
cursor.execute(*(translation.select(*columns,
where=(translation.lang == default_lang)
@@ -1656,16 +1659,7 @@
- translation.select(*columns,
where=(translation.lang == lang)
& translation.type.in_(self._ressource_types))))
- to_create = []
- for row in cursor_dict(cursor):
- to_create.append({
- 'name': row['name'],
- 'res_id': row['res_id'],
- 'lang': lang,
- 'type': row['type'],
- 'module': row['module'],
- })
- if to_create:
+ if to_create := list(cursor):
Translation.create(to_create)
if parent_lang:
@@ -1676,7 +1670,7 @@
& translation.select(*columns,
where=(translation.lang == lang)
& translation.type.in_(self._ressource_types))))
- for row in cursor_dict(cursor):
+ for row in cursor:
cursor_update.execute(*translation.update(
[translation.value],
[''],
@@ -1700,7 +1694,7 @@
- translation.select(*columns,
where=(translation.lang == lang)
& translation.type.in_(self._updatable_types))))
- for row in cursor_dict(cursor):
+ for row in cursor:
cursor_update.execute(*translation.update(
[translation.fuzzy, translation.src,
translation.src_plural],
@@ -1727,7 +1721,7 @@
& (translation.value != Null),
group_by=translation.src))
- for row in cursor_dict(cursor):
+ for row in cursor:
cursor_update.execute(*translation.update(
[translation.fuzzy, translation.value],
[True, row['value']],
diff -r 1d35758d7739 -r e1657467e49c trytond/trytond/model/modelsql.py
--- a/trytond/trytond/model/modelsql.py Thu Jun 04 23:07:09 2026 +0200
+++ b/trytond/trytond/model/modelsql.py Thu Jun 04 23:41:21 2026 +0200
@@ -21,7 +21,7 @@
from trytond.pyson import PYSONDecoder, PYSONEncoder
from trytond.rpc import RPC
from trytond.sql.functions import Range
-from trytond.tools import cursor_dict, grouped_slice
+from trytond.tools import grouped_slice
from trytond.tools.domain_inversion import simplify
from trytond.transaction import (
Transaction, inactive_records, record_cache_size, without_check_access)
@@ -997,7 +997,7 @@
@no_table_query
def create(cls, vlist):
transaction = Transaction()
- cursor = transaction.connection.cursor()
+ cursor = transaction.connection.cursor(row_factory=backend.scalar_row)
pool = Pool()
Translation = pool.get('ir.translation')
@@ -1039,7 +1039,7 @@
if transaction.database.has_returning():
cursor.execute(*table.insert(
cols, [val], [table.id]))
- yield from (r[0] for r in cursor)
+ yield from cursor
else:
id_new = transaction.database.nextid(
transaction.connection, cls._table)
@@ -1181,7 +1181,7 @@
Rule = pool.get('ir.rule')
Translation = pool.get('ir.translation')
transaction = Transaction()
- cursor = Transaction().connection.cursor()
+ connection = transaction.connection
ids, fields_names = cls._before_read(ids, fields_names)
@@ -1290,9 +1290,10 @@
where &= history_clause
if domain:
where &= dom_exp
+ cursor = connection.cursor(row_factory=backend.dict_row)
cursor.execute(*from_.select(*columns.values(), where=where,
order_by=history_order, limit=history_limit))
- fetchall = list(cursor_dict(cursor))
+ fetchall = list(cursor)
if not len(fetchall) == len({}.fromkeys(sub_ids)):
cls.__check_domain_rule(ids, 'read')
raise RuntimeError("Undetected access error")
@@ -1938,7 +1939,7 @@
def search(cls, domain, offset=0, limit=None, order=None, count=False,
query=False):
transaction = Transaction()
- cursor = transaction.connection.cursor()
+ connection = transaction.connection
super().search(
domain, offset=offset, limit=limit, order=order, count=count)
@@ -1965,6 +1966,7 @@
if query:
return select
else:
+ cursor = connection.cursor()
cursor.execute(*select)
return cursor.fetchone()[0]
@@ -2001,9 +2003,10 @@
if query:
return select
+ cursor = connection.cursor(row_factory=backend.dict_row)
cursor.execute(*select)
- rows = list(cursor_dict(cursor))
+ rows = list(cursor)
cache = transaction.get_cache()
delete_records = transaction.delete_records[cls.__name__]
diff -r 1d35758d7739 -r e1657467e49c trytond/trytond/tools/__init__.py
--- a/trytond/trytond/tools/__init__.py Thu Jun 04 23:07:09 2026 +0200
+++ b/trytond/trytond/tools/__init__.py Thu Jun 04 23:41:21 2026 +0200
@@ -54,20 +54,9 @@
return self.fget.__get__(None, owner)()
-def cursor_dict(cursor, size=None):
- size = cursor.arraysize if size is None else size
- while True:
- rows = cursor.fetchmany(size)
- if not rows:
- break
- for row in rows:
- yield {d[0]: v for d, v in zip(cursor.description, row)}
-
-
__all__ = [
ClassProperty,
cached_property,
- cursor_dict,
decistmt,
entry_points,
escape_wildcard,