Skip to content

Commit 2f2e559

Browse files
authored
Support multiple aspect ratios in DRF and fallback to field definition (#173)
1 parent ec91252 commit 2f2e559

File tree

2 files changed

+117
-34
lines changed

2 files changed

+117
-34
lines changed

pictures/contrib/rest_framework.py

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,28 +34,17 @@ def to_representation(self, obj: PictureFieldFile):
3434
"height": obj.height,
3535
}
3636

37-
payload = {
38-
**base_payload,
39-
"ratios": {
40-
ratio: {
41-
"sources": {
42-
f"image/{file_type.lower()}": sizes
43-
for file_type, sizes in sources.items()
44-
if file_type in self.file_types or not self.file_types
45-
},
46-
}
47-
for ratio, sources in obj.aspect_ratios.items()
48-
if ratio in self.aspect_ratios or not self.aspect_ratios
49-
},
50-
}
51-
5237
# if the request has query parameters, filter the payload
5338
try:
5439
query_params: QueryDict = self.context["request"].GET
5540
except KeyError:
56-
pass
41+
ratios = self.aspect_ratios
42+
container = get_settings().CONTAINER_WIDTH
43+
breakpoints = {}
5744
else:
58-
ratio = query_params.get(f"{self.field_name}_ratio")
45+
ratios = (
46+
query_params.getlist(f"{self.field_name}_ratio")
47+
) or self.aspect_ratios
5948
container = query_params.get(f"{self.field_name}_container")
6049
try:
6150
container = int(container)
@@ -68,16 +57,25 @@ def to_representation(self, obj: PictureFieldFile):
6857
for bp in get_settings().BREAKPOINTS
6958
if f"{self.field_name}_{bp}" in query_params
7059
}
71-
if ratio is not None:
72-
try:
73-
payload["ratios"] = {ratio: payload["ratios"][ratio]}
74-
except KeyError as e:
75-
raise ValueError(
76-
f"Invalid ratio: {ratio}. Choices are: {', '.join(filter(None, obj.aspect_ratios.keys()))}"
77-
) from e
78-
else:
79-
payload["ratios"][ratio]["media"] = utils.sizes(
80-
container_width=container, **breakpoints
81-
)
60+
if set(ratios) - set(self.aspect_ratios or obj.aspect_ratios.keys()):
61+
raise ValueError(
62+
f"Invalid ratios: {', '.join(ratios)}. Choices are: {', '.join(filter(None, obj.aspect_ratios.keys()))}"
63+
)
64+
65+
payload = {
66+
**base_payload,
67+
"ratios": {
68+
ratio: {
69+
"sources": {
70+
f"image/{file_type.lower()}": sizes
71+
for file_type, sizes in sources.items()
72+
if file_type in self.file_types or not self.file_types
73+
},
74+
"media": utils.sizes(container_width=container, **breakpoints),
75+
}
76+
for ratio, sources in obj.aspect_ratios.items()
77+
if ratio in ratios or not ratios
78+
},
79+
}
8280

8381
return json.loads(json.dumps(payload, default=default))

tests/contrib/test_rest_framework.py

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ def test_to_representation(self, image_upload_file, settings):
7878
"600": "/media/testapp/profile/image/600w.webp",
7979
"700": "/media/testapp/profile/image/700w.webp",
8080
}
81-
}
81+
},
82+
"media": "(min-width: 0px) and (max-width: 1199px) 100vw, 1200px",
8283
},
8384
"1/1": {
8485
"sources": {
@@ -92,7 +93,8 @@ def test_to_representation(self, image_upload_file, settings):
9293
"600": "/media/testapp/profile/image/1/600w.webp",
9394
"700": "/media/testapp/profile/image/1/700w.webp",
9495
}
95-
}
96+
},
97+
"media": "(min-width: 0px) and (max-width: 1199px) 100vw, 1200px",
9698
},
9799
"3/2": {
98100
"sources": {
@@ -106,7 +108,8 @@ def test_to_representation(self, image_upload_file, settings):
106108
"600": "/media/testapp/profile/image/3_2/600w.webp",
107109
"700": "/media/testapp/profile/image/3_2/700w.webp",
108110
}
109-
}
111+
},
112+
"media": "(min-width: 0px) and (max-width: 1199px) 100vw, 1200px",
110113
},
111114
"16/9": {
112115
"sources": {
@@ -120,7 +123,8 @@ def test_to_representation(self, image_upload_file, settings):
120123
"600": "/media/testapp/profile/image/16_9/600w.webp",
121124
"700": "/media/testapp/profile/image/16_9/700w.webp",
122125
}
123-
}
126+
},
127+
"media": "(min-width: 0px) and (max-width: 1199px) 100vw, 1200px",
124128
},
125129
},
126130
}
@@ -179,7 +183,7 @@ def test_to_representation__raise_value_error(
179183
with pytest.raises(ValueError) as e:
180184
serializer.data["image"]
181185

182-
assert str(e.value) == "Invalid ratio: 21/11. Choices are: 1/1, 3/2, 16/9"
186+
assert str(e.value) == "Invalid ratios: 21/11. Choices are: 1/1, 3/2, 16/9"
183187

184188
@pytest.mark.django_db
185189
def test_to_representation__blank(self, rf, image_upload_file, settings):
@@ -195,6 +199,86 @@ def test_to_representation__blank(self, rf, image_upload_file, settings):
195199

196200
assert serializer.data["image"] is None
197201

202+
@pytest.mark.django_db
203+
def test_to_representation__no_get_params(self, rf, image_upload_file, settings):
204+
settings.PICTURES["USE_PLACEHOLDERS"] = False
205+
206+
profile = models.Profile.objects.create(picture=image_upload_file)
207+
request = rf.get("/")
208+
request.GET._mutable = True
209+
request.GET["foo"] = "bar"
210+
serializer = ProfileSerializer(profile, context={"request": request})
211+
assert serializer.data["image_mobile"] == {
212+
"url": "/media/testapp/profile/image.png",
213+
"width": 800,
214+
"height": 800,
215+
"ratios": {
216+
"3/2": {
217+
"sources": {
218+
"image/webp": {
219+
"800": "/media/testapp/profile/image/3_2/800w.webp",
220+
"100": "/media/testapp/profile/image/3_2/100w.webp",
221+
"200": "/media/testapp/profile/image/3_2/200w.webp",
222+
"300": "/media/testapp/profile/image/3_2/300w.webp",
223+
"400": "/media/testapp/profile/image/3_2/400w.webp",
224+
"500": "/media/testapp/profile/image/3_2/500w.webp",
225+
"600": "/media/testapp/profile/image/3_2/600w.webp",
226+
"700": "/media/testapp/profile/image/3_2/700w.webp",
227+
}
228+
},
229+
"media": "(min-width: 0px) and (max-width: 1199px) 100vw, 1200px",
230+
}
231+
},
232+
}
233+
234+
@pytest.mark.django_db
235+
def test_to_representation__multiple_ratios(self, rf, image_upload_file, settings):
236+
settings.PICTURES["USE_PLACEHOLDERS"] = False
237+
238+
profile = models.Profile.objects.create(picture=image_upload_file)
239+
request = rf.get("/")
240+
request.GET._mutable = True
241+
request.GET.setlist("image_ratio", ["3/2", "16/9"])
242+
serializer = ProfileSerializer(profile, context={"request": request})
243+
print(serializer.data["image"])
244+
assert serializer.data["image"] == {
245+
"url": "/media/testapp/profile/image.png",
246+
"width": 800,
247+
"height": 800,
248+
"ratios": {
249+
"3/2": {
250+
"sources": {
251+
"image/webp": {
252+
"800": "/media/testapp/profile/image/3_2/800w.webp",
253+
"100": "/media/testapp/profile/image/3_2/100w.webp",
254+
"200": "/media/testapp/profile/image/3_2/200w.webp",
255+
"300": "/media/testapp/profile/image/3_2/300w.webp",
256+
"400": "/media/testapp/profile/image/3_2/400w.webp",
257+
"500": "/media/testapp/profile/image/3_2/500w.webp",
258+
"600": "/media/testapp/profile/image/3_2/600w.webp",
259+
"700": "/media/testapp/profile/image/3_2/700w.webp",
260+
}
261+
},
262+
"media": "(min-width: 0px) and (max-width: 1199px) 100vw, 1200px",
263+
},
264+
"16/9": {
265+
"sources": {
266+
"image/webp": {
267+
"800": "/media/testapp/profile/image/16_9/800w.webp",
268+
"100": "/media/testapp/profile/image/16_9/100w.webp",
269+
"200": "/media/testapp/profile/image/16_9/200w.webp",
270+
"300": "/media/testapp/profile/image/16_9/300w.webp",
271+
"400": "/media/testapp/profile/image/16_9/400w.webp",
272+
"500": "/media/testapp/profile/image/16_9/500w.webp",
273+
"600": "/media/testapp/profile/image/16_9/600w.webp",
274+
"700": "/media/testapp/profile/image/16_9/700w.webp",
275+
}
276+
},
277+
"media": "(min-width: 0px) and (max-width: 1199px) 100vw, 1200px",
278+
},
279+
},
280+
}
281+
198282
@pytest.mark.django_db
199283
def test_to_representation__with_container(self, rf, image_upload_file, settings):
200284
settings.PICTURES["USE_PLACEHOLDERS"] = False
@@ -304,7 +388,8 @@ def test_to_representation__with_prefiltered_aspect_ratio_and_source(
304388
"600": "/media/testapp/profile/image/3_2/600w.webp",
305389
"700": "/media/testapp/profile/image/3_2/700w.webp",
306390
}
307-
}
391+
},
392+
"media": "(min-width: 0px) and (max-width: 1199px) 100vw, 1200px",
308393
}
309394
},
310395
}

0 commit comments

Comments
 (0)