Skip to content

Commit e3ff53e

Browse files
committed
Fixed prepare_objects being called before prefetch.
Patch queryset.fetch_all instead of queryset._iterable_class.
1 parent bbe48b4 commit e3ff53e

File tree

4 files changed

+40
-17
lines changed

4 files changed

+40
-17
lines changed

django_qserializer/serialization.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import types
22

33
from django.db import models
4-
from django.db.models.query import ModelIterable
54
from django.db.models.manager import BaseManager
65

76

@@ -96,24 +95,19 @@ def serialize(self, objs):
9695
yield from map(self._serialize_object, objs)
9796

9897

99-
class _SerializableModelIterable(ModelIterable):
100-
def __iter__(self):
101-
data = list(super().__iter__())
102-
self.queryset.serializer._prepare_objects(data)
103-
yield from data
104-
105-
10698
class SerializableQuerySet(models.QuerySet):
10799
@property
108100
def serializer(self):
109101
return getattr(self, '_serializer', None)
110102

111103
def to_serialize(self, serializer=None):
112104
self._serializer = _resolve_serializer(serializer)
113-
# https://github.com/django/django/blob/981a3426cf2f54f5282e79fb7f47726998c87cb2/django/db/models/query.py#L353
114-
self._iterable_class = _SerializableModelIterable
115105
return self._serializer._prepare_queryset(self)
116106

107+
def _fetch_all(self):
108+
super()._fetch_all()
109+
self._serializer._prepare_objects(self._result_cache)
110+
117111
def _clone(self):
118112
c = super()._clone()
119113
c._serializer = self.serializer

django_qserializer/tests/test_serializer.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22

33
from django_qserializer import BaseSerializer, serialize
4-
from django_qserializer.tests.testapp.models import Bus, Company
4+
from django_qserializer.tests.testapp.models import Bus, Company, Travel
55

66

77
@pytest.fixture
@@ -10,7 +10,12 @@ def bus_fixture(db):
1010
return Bus.objects.create(company=company, plate='BUSER')
1111

1212

13-
def test_magic_serialize_method(bus_fixture, db, django_assert_num_queries):
13+
@pytest.fixture
14+
def travel_fixture(db, bus_fixture):
15+
return Travel.objects.create(bus=bus_fixture)
16+
17+
18+
def test_magic_serialize_method(bus_fixture, django_assert_num_queries):
1419
class S(BaseSerializer):
1520
select_related = ['company']
1621

@@ -24,7 +29,7 @@ def serialize_object(self, bus):
2429
assert {'company': 'Hurricane Cart'} == bus.serialize()
2530

2631

27-
def test_global_serialize(bus_fixture, db, django_assert_num_queries):
32+
def test_global_serialize(bus_fixture, django_assert_num_queries):
2833
class S(BaseSerializer):
2934
select_related = ['company']
3035

@@ -42,13 +47,13 @@ def test_global_serialize_empty():
4247
assert [] == serialize([])
4348

4449

45-
def test_serialize_object_not_implemented(bus_fixture, db):
50+
def test_serialize_object_not_implemented(bus_fixture):
4651
bus = Bus.objects.to_serialize().first()
4752
with pytest.raises(NotImplementedError):
4853
bus.serialize()
4954

5055

51-
def test_extras(bus_fixture, db, django_assert_num_queries):
56+
def test_extras(bus_fixture, django_assert_num_queries):
5257
class Attr(BaseSerializer):
5358
select_related = ['company']
5459

@@ -91,7 +96,7 @@ def serialize_object(self, obj):
9196
assert expected == next(serialize([bus]))
9297

9398

94-
def test_extras_recursive(bus_fixture, db, django_assert_num_queries):
99+
def test_extras_recursive(bus_fixture, django_assert_num_queries):
95100
def city(obj):
96101
return {
97102
'city': 'SJK',
@@ -131,3 +136,23 @@ def serialize_object(self, obj):
131136

132137
with django_assert_num_queries(0):
133138
assert expected == bus.serialize()
139+
140+
141+
def test_prepare_objects_after_prefetch(travel_fixture):
142+
"""
143+
Regression test. Prior implementation ran prepare_objects before prefetchs.
144+
"""
145+
146+
class S(BaseSerializer):
147+
prefetch_related = ['travels']
148+
149+
def prepare_objects(self, objs):
150+
for obj in objs:
151+
assert obj._prefetched_objects_cache
152+
153+
def serialize_object(self, obj):
154+
return {
155+
'plate': obj.plate,
156+
}
157+
158+
Bus.objects.to_serialize(S).first()

django_qserializer/tests/testapp/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ class Bus(models.Model):
1111
plate = models.CharField(max_length=10)
1212
company = models.ForeignKey(Company, on_delete=models.SET_NULL)
1313
objects = SerializableManager()
14+
15+
16+
class Travel(models.Model):
17+
bus = models.ForeignKey(Bus, related_name='travels', on_delete=models.SET_NULL)

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = django_qserializer
3-
version = 0.2.4
3+
version = 0.2.5
44
url = https://github.com/buserbrasil/django-qserializer
55
license = MIT
66
author = Iuri de Silvio

0 commit comments

Comments
 (0)