details:   https://code.tryton.org/tryton/commit/5e7086dc7cc4
branch:    default
user:      Nicolas Évrard <[email protected]>
date:      Tue Apr 21 13:40:21 2026 +0200
description:
        Fetch limit records of ResourceAccessMixin models when they are 
available

        Closes #14788
diffstat:

 trytond/trytond/ir/resource.py         |  35 +++++++++++++++++++++++++--------
 trytond/trytond/tests/test_resource.py |  25 ++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 9 deletions(-)

diffs (85 lines):

diff -r 20e3adfa8812 -r 5e7086dc7cc4 trytond/trytond/ir/resource.py
--- a/trytond/trytond/ir/resource.py    Mon Apr 20 17:03:34 2026 +0200
+++ b/trytond/trytond/ir/resource.py    Tue Apr 21 13:40:21 2026 +0200
@@ -88,7 +88,12 @@
         result = super().search(
             domain, offset=offset, limit=limit, order=order,
             count=False if enforce_access else count, query=query)
-        if enforce_access:
+        if not enforce_access:
+            return result
+
+        loop = 0
+        fetched = []
+        while True:
             records = result
             resources = defaultdict(set)
             allowed = set()
@@ -104,15 +109,27 @@
                             ('id', 'in', ids),
                             ]))
 
-            records = [
+            fetched.extend([
                 r for r in records
-                if not r.resource or r.resource in allowed]
-            if count:
-                result = len(records)
-            else:
-                # re-browse to have same context
-                result = cls.browse(records)
-        return result
+                if not r.resource or r.resource in allowed])
+
+            if limit is None or len(fetched) >= limit:
+                if limit is not None:
+                    fetched = fetched[:limit]
+                break
+
+            loop += 1
+            result = super().search(
+                domain, offset=offset + loop * limit, limit=limit, order=order,
+                count=False, query=False)
+            if not result:
+                break
+
+        if count:
+            return len(fetched)
+        else:
+            # re-browse to have same context
+            return cls.browse(fetched)
 
     @classmethod
     def read(cls, ids, fields_names):
diff -r 20e3adfa8812 -r 5e7086dc7cc4 trytond/trytond/tests/test_resource.py
--- a/trytond/trytond/tests/test_resource.py    Mon Apr 20 17:03:34 2026 +0200
+++ b/trytond/trytond/tests/test_resource.py    Tue Apr 21 13:40:21 2026 +0200
@@ -235,3 +235,28 @@
                 Note.read([note.id], ['message'])
             with self.assertRaises(AccessError):
                 Note.delete([note])
+
+    @with_transaction()
+    def test_resources_search_limit(self):
+        "Test resource search limit work as expected"
+        pool = Pool()
+        Note = pool.get('ir.note')
+        Warning_ = pool.get('res.user.warning')
+
+        warning1 = Warning_(user=0, name="root")
+        warning1.save()
+        warning2 = Warning_(user=1, name="admin")
+        warning2.save()
+        for i in range(100):
+            note = Note(resource=warning2 if i % 3 else warning1)
+            note.save()
+
+        with Transaction().set_context(_check_access=True):
+            notes = Note.search([], limit=10, offset=0)
+            self.assertEqual(len(notes), 10)
+
+            notes = Note.search([], limit=200, offset=0)
+            self.assertEqual(len(notes), 66)
+
+            notes = Note.search([])
+            self.assertEqual(len(notes), 66)

Reply via email to