From bd3d4e1df9087ffe8a1799b30b5a09d870511653 Mon Sep 17 00:00:00 2001 From: Derek Anderson Date: Fri, 21 Apr 2017 14:07:48 -0500 Subject: [PATCH 1/3] impl prefetched --- peewee.py | 10 +++++++ playhouse/tests/test_query_results.py | 39 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/peewee.py b/peewee.py index ae092b5bd..7286cb48d 100644 --- a/peewee.py +++ b/peewee.py @@ -5175,6 +5175,16 @@ def save(self, force_insert=False, only=None): def is_dirty(self): return bool(self._dirty) + def prefetched(self, fk): + ''' + Accepts a ForeignKeyField. + Returns true/false representing if accessing this attribute will trigger a database query. + ''' + if not isinstance(self, fk.model_class): + raise AttributeError('ForeignKeyField %s is not part of this model.' % fk) + if getattr(self, fk.db_column) is None: return True + return fk.name in self._obj_cache + @property def dirty_fields(self): return [f for f in self._meta.sorted_fields if f.name in self._dirty] diff --git a/playhouse/tests/test_query_results.py b/playhouse/tests/test_query_results.py index 3d726a61c..b7c6c0fb2 100644 --- a/playhouse/tests/test_query_results.py +++ b/playhouse/tests/test_query_results.py @@ -484,6 +484,45 @@ def test_aliasing_values(self): self.assertEqual(results, [('u1', 'u1'), ('u2', 'u2')]) +class TestPrefetchDetection(ModelTestCase): + requires = [Parent, Child, Orphan] + + def setUp(self): + super(TestPrefetchDetection, self).setUp() + u1 = Parent.create(data='u1') + u2 = Child.create(parent=u1, data='u2') + u3 = Orphan.create(data='u3') + + def test_prefetched(self): + u2 = (Child + .select(Child, Parent) + .join(Parent) + .first()) + self.assertTrue(u2.prefetched(Child.parent)) + + def test_not_prefetched(self): + u2 = Child.select().first() + self.assertFalse(u2.prefetched(Child.parent)) + + def test_prefetched_null(self): + u3 = (Orphan + .select(Orphan, Parent) + .join(Parent, JOIN.LEFT_OUTER) + .first()) + self.assertTrue(u3.prefetched(Orphan.parent)) + + def test_not_prefetched_null(self): + u3 = Orphan.select().first() + # even though it's not joined, we know the model + # is null because the FK value is null. + self.assertTrue(u3.prefetched(Orphan.parent)) + + def test_prefetched_nonsense(self): + u1 = Parent.select().first() + with self.assertRaises(AttributeError): + u1.prefetched(Orphan.parent) + + class TestJoinedInstanceConstruction(ModelTestCase): requires = [Blog, User, Relationship] From fab9771f1cdadedc8f32f1face2d3406f85a349b Mon Sep 17 00:00:00 2001 From: Derek Anderson Date: Fri, 21 Apr 2017 14:42:42 -0500 Subject: [PATCH 2/3] impl prefetched for reverse relationships --- peewee.py | 10 ++++++---- playhouse/tests/test_query_results.py | 9 +++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/peewee.py b/peewee.py index 7286cb48d..29fc64612 100644 --- a/peewee.py +++ b/peewee.py @@ -5180,10 +5180,12 @@ def prefetched(self, fk): Accepts a ForeignKeyField. Returns true/false representing if accessing this attribute will trigger a database query. ''' - if not isinstance(self, fk.model_class): - raise AttributeError('ForeignKeyField %s is not part of this model.' % fk) - if getattr(self, fk.db_column) is None: return True - return fk.name in self._obj_cache + if isinstance(self, fk.model_class): + if getattr(self, fk.db_column) is None: return True + return fk.name in self._obj_cache + if isinstance(self, fk.rel_model): + return isinstance(getattr(self, fk._get_related_name()), list) + raise AttributeError('ForeignKeyField %s is not related to this model.' % fk) @property def dirty_fields(self): diff --git a/playhouse/tests/test_query_results.py b/playhouse/tests/test_query_results.py index b7c6c0fb2..1e6c7ee25 100644 --- a/playhouse/tests/test_query_results.py +++ b/playhouse/tests/test_query_results.py @@ -518,9 +518,14 @@ def test_not_prefetched_null(self): self.assertTrue(u3.prefetched(Orphan.parent)) def test_prefetched_nonsense(self): - u1 = Parent.select().first() + u2 = Child.select().first() with self.assertRaises(AttributeError): - u1.prefetched(Orphan.parent) + u2.prefetched(Orphan.parent) + + def test_prefetched_set(self): + u2 = Parent.ALL.plus(Child.parent).first() + self.assertTrue(u2.prefetched(Child.parent)) + class TestJoinedInstanceConstruction(ModelTestCase): From 6b55503f710523db7fa764d3e4532b312aa5dc2d Mon Sep 17 00:00:00 2001 From: Derek Anderson Date: Fri, 21 Apr 2017 14:45:02 -0500 Subject: [PATCH 3/3] python2.6 fix --- playhouse/tests/test_query_results.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playhouse/tests/test_query_results.py b/playhouse/tests/test_query_results.py index 1e6c7ee25..0795b478f 100644 --- a/playhouse/tests/test_query_results.py +++ b/playhouse/tests/test_query_results.py @@ -519,8 +519,9 @@ def test_not_prefetched_null(self): def test_prefetched_nonsense(self): u2 = Child.select().first() - with self.assertRaises(AttributeError): + def f(): u2.prefetched(Orphan.parent) + self.assertRaises(AttributeError, f) def test_prefetched_set(self): u2 = Parent.ALL.plus(Child.parent).first()