Skip to content

Commit 613e54e

Browse files
authored
Merge pull request #46 from namespace-ee/renewal
supported versions renewal
2 parents c1a1f5c + 19a26e5 commit 613e54e

28 files changed

+126
-273
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ python:
55
- "3.6"
66
- "3.7"
77
- "3.8"
8+
- "3.9"
89

910
matrix:
1011
fast_finish: true
1112
include:
12-
- python: 3.7
13+
- python: 3.8
1314
env: TOXENV=lint
1415

1516
# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors

HISTORY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 1.4.0 (2021-04-07)
4+
5+
- Renewed **Python** supported versions `3.6 -> 3.9`
6+
- Renewed **Django** supported versions `2.2`, `3.1`, `3.2`
7+
- Renewed **Django-rest-framework** supported versions. `3.9 -> 3.12`
8+
39
## 1.3.1 (2021-04-07)
410

511
Added support for `django.db.models.Prefetch`

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
[![Code style Black](https://img.shields.io/badge/code%20style-black-000000.svg?maxAge=2592000)](https://github.com/ambv/black)
77

88
:warning: Note that there are major API changes since version 0.1.1 that have to be taken into account when upgrading!
9+
:warning: Python 2 and Django 1.11 are no longer supported from version 1.4.0!
910

1011
# Django rest framework sideloading
1112

@@ -145,10 +146,11 @@ $ make tox
145146

146147
```shell
147148
$ tox --listenvs
148-
py27-django18-drf34
149-
py27-django19-drf34
149+
py37-django22-drf39
150+
py38-django31-drf311
151+
py39-django32-drf312
150152
# ...
151-
$ tox -e py27-django19-drf34
153+
$ tox -e py39-django32-drf312
152154
```
153155

154156
#### Test coverage

drf_sideloading/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.3.1"
1+
__version__ = "1.4.0"

drf_sideloading/mixins.py

Lines changed: 27 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
from __future__ import unicode_literals
2-
3-
import six
41
import copy
5-
62
from itertools import chain
73

84
from django.db.models import Prefetch
@@ -31,9 +27,7 @@ def __init__(self, **kwargs):
3127
def check_sideloading_serializer_class(self):
3228
assert (
3329
self.sideloading_serializer_class is not None
34-
), "'{}' should either include a `sideloading_serializer_class` attribute, ".format(
35-
self.__class__.__name__
36-
)
30+
), "'{}' should either include a `sideloading_serializer_class` attribute, ".format(self.__class__.__name__)
3731
assert issubclass(
3832
self.sideloading_serializer_class, SideLoadableSerializer
3933
), "'{}' `sideloading_serializer_class` must be a SideLoadableSerializer subclass".format(
@@ -51,36 +45,26 @@ def check_sideloading_serializer_class(self):
5145
self.sideloading_serializer_class.Meta, "primary", None
5246
), "Sideloadable serializer must have a Meta attribute called primary!"
5347
assert (
54-
self.sideloading_serializer_class.Meta.primary
55-
in self.sideloading_serializer_class._declared_fields
48+
self.sideloading_serializer_class.Meta.primary in self.sideloading_serializer_class._declared_fields
5649
), "Sideloadable serializer Meta.primary must point to a field in the serializer!"
57-
if (
58-
getattr(self.sideloading_serializer_class.Meta, "prefetches", None)
59-
is not None
60-
):
50+
if getattr(self.sideloading_serializer_class.Meta, "prefetches", None) is not None:
6151
assert isinstance(
6252
self.sideloading_serializer_class.Meta.prefetches, dict
6353
), "Sideloadable serializer Meta attribute 'prefetches' must be a dict."
6454

6555
# check serializer fields:
6656
for name, field in self.sideloading_serializer_class._declared_fields.items():
67-
assert getattr(
68-
field, "many", None
69-
), "SideLoadable field '{}' must be set as many=True".format(name)
57+
assert getattr(field, "many", None), "SideLoadable field '{}' must be set as many=True".format(name)
7058

7159
# check serializer fields:
7260
for name, field in self.sideloading_serializer_class._declared_fields.items():
73-
assert getattr(
74-
field, "many", None
75-
), "SideLoadable field '{}' must be set as many=True".format(name)
61+
assert getattr(field, "many", None), "SideLoadable field '{}' must be set as many=True".format(name)
7662

7763
def get_primary_field_name(self):
7864
return self.sideloading_serializer_class.Meta.primary
7965

8066
def get_sideloadable_fields(self):
81-
sideloadable_fields = copy.deepcopy(
82-
self.sideloading_serializer_class._declared_fields
83-
)
67+
sideloadable_fields = copy.deepcopy(self.sideloading_serializer_class._declared_fields)
8468
sideloadable_fields.pop(self._primary_field_name, None)
8569
return sideloadable_fields
8670

@@ -94,44 +78,33 @@ def get_sideloading_prefetches(self):
9478
if not isinstance(v, list):
9579
v = [v]
9680
for vi in v:
97-
if not isinstance(vi, (six.string_types, Prefetch)):
81+
if not isinstance(vi, (str, Prefetch)):
9882
raise RuntimeError("Sideloadable prefetch values must be a list of strings or Prefetch objects")
9983
return cleaned_prefetches
10084

10185
def initialize_request(self, request, *args, **kwargs):
102-
request = super(SideloadableRelationsMixin, self).initialize_request(
103-
request=request, *args, **kwargs
104-
)
86+
request = super(SideloadableRelationsMixin, self).initialize_request(request=request, *args, **kwargs)
10587

106-
sideload_params = self.parse_query_param(
107-
sideload_parameter=request.query_params.get(self.query_param_name, "")
108-
)
88+
sideload_params = self.parse_query_param(sideload_parameter=request.query_params.get(self.query_param_name, ""))
10989
if request.method == "GET" and sideload_params:
11090
# When sideloading disable BrowsableAPIForms
11191
if BrowsableAPIRenderer in self.renderer_classes:
11292
renderer_classes = (
113-
list(self.renderer_classes)
114-
if isinstance(self.renderer_classes, tuple)
115-
else self.renderer_classes
93+
list(self.renderer_classes) if isinstance(self.renderer_classes, tuple) else self.renderer_classes
11694
)
11795
renderer_classes = [
118-
BrowsableAPIRendererWithoutForms if r == BrowsableAPIRenderer else r
119-
for r in renderer_classes
96+
BrowsableAPIRendererWithoutForms if r == BrowsableAPIRenderer else r for r in renderer_classes
12097
]
12198
self.renderer_classes = renderer_classes
12299

123100
return request
124101

125102
def list(self, request, *args, **kwargs):
126-
sideload_params = self.parse_query_param(
127-
sideload_parameter=request.query_params.get(self.query_param_name, "")
128-
)
103+
sideload_params = self.parse_query_param(sideload_parameter=request.query_params.get(self.query_param_name, ""))
129104

130105
# Do not sideload unless params and GET method
131106
if request.method != "GET" or not sideload_params:
132-
return super(SideloadableRelationsMixin, self).list(
133-
request, *args, **kwargs
134-
)
107+
return super(SideloadableRelationsMixin, self).list(request, *args, **kwargs)
135108

136109
# After this `relations_to_sideload` is safe to use
137110
queryset = self.get_queryset()
@@ -148,17 +121,15 @@ def list(self, request, *args, **kwargs):
148121
sideloadable_page = self.get_sideloadable_page(page)
149122
serializer = self.sideloading_serializer_class(
150123
instance=sideloadable_page,
151-
fields_to_load=[self._primary_field_name]
152-
+ list(self.relations_to_sideload),
124+
fields_to_load=[self._primary_field_name] + list(self.relations_to_sideload),
153125
context={"request": request},
154126
)
155127
return self.get_paginated_response(serializer.data)
156128
else:
157129
sideloadable_page = self.get_sideloadable_page_from_queryset(queryset)
158130
serializer = self.sideloading_serializer_class(
159131
instance=sideloadable_page,
160-
fields_to_load=[self._primary_field_name]
161-
+ list(self.relations_to_sideload),
132+
fields_to_load=[self._primary_field_name] + list(self.relations_to_sideload),
162133
context={"request": request},
163134
)
164135
return Response(serializer.data)
@@ -174,67 +145,45 @@ def parse_query_param(self, sideload_parameter):
174145
This function finds string match between requested names and defined relation in view
175146
176147
"""
177-
self.relations_to_sideload = set(sideload_parameter.split(",")) & set(
178-
self._sideloadable_fields.keys()
179-
)
148+
self.relations_to_sideload = set(sideload_parameter.split(",")) & set(self._sideloadable_fields.keys())
180149
return self.relations_to_sideload
181150

182151
def get_relevant_prefetches(self):
183152
if not self._prefetches:
184153
return set()
185-
return set(
186-
pf
187-
for relation in self.relations_to_sideload
188-
for pf in self._prefetches.get(relation, [])
189-
)
154+
return set(pf for relation in self.relations_to_sideload for pf in self._prefetches.get(relation, []))
190155

191156
def get_sideloadable_page_from_queryset(self, queryset):
192157
# this works wonders, but can't be used when page is paginated...
193158
sideloadable_page = {self._primary_field_name: queryset}
194159
for relation in self.relations_to_sideload:
195160
if not isinstance(self._sideloadable_fields[relation], ListSerializer):
196-
raise RuntimeError(
197-
"SideLoadable field '{}' must be set as many=True".format(relation)
198-
)
161+
raise RuntimeError("SideLoadable field '{}' must be set as many=True".format(relation))
199162

200163
source = self._sideloadable_fields[relation].source or relation
201164
rel_model = self._sideloadable_fields[relation].child.Meta.model
202-
rel_qs = rel_model.objects.filter(
203-
pk__in=queryset.values_list(source, flat=True)
204-
)
165+
rel_qs = rel_model.objects.filter(pk__in=queryset.values_list(source, flat=True))
205166
sideloadable_page[source] = rel_qs
206167
return sideloadable_page
207168

208169
def get_sideloadable_page(self, page):
209170
sideloadable_page = {self._primary_field_name: page}
210171
for relation in self.relations_to_sideload:
211172
if not isinstance(self._sideloadable_fields[relation], ListSerializer):
212-
raise RuntimeError(
213-
"SideLoadable field '{}' must be set as many=True".format(relation)
214-
)
173+
raise RuntimeError("SideLoadable field '{}' must be set as many=True".format(relation))
215174

216175
source = self._sideloadable_fields[relation].source or relation
217-
sideloadable_page[source] = self.filter_related_objects(
218-
related_objects=page, lookup=source
219-
)
176+
sideloadable_page[source] = self.filter_related_objects(related_objects=page, lookup=source)
220177
return sideloadable_page
221178

222179
def filter_related_objects(self, related_objects, lookup):
223-
current_lookup, remaining_lookup = (
224-
lookup.split("__", 1) if "__" in lookup else (lookup, None)
225-
)
180+
current_lookup, remaining_lookup = lookup.split("__", 1) if "__" in lookup else (lookup, None)
226181
related_objects_set = {getattr(r, current_lookup) for r in related_objects} - {None}
227-
if related_objects_set and next(
228-
iter(related_objects_set)
229-
).__class__.__name__ in ["ManyRelatedManager", "RelatedManager"]:
230-
related_objects_set = set(
231-
chain(
232-
*[
233-
related_queryset.all()
234-
for related_queryset in related_objects_set
235-
]
236-
)
237-
)
182+
if related_objects_set and next(iter(related_objects_set)).__class__.__name__ in [
183+
"ManyRelatedManager",
184+
"RelatedManager",
185+
]:
186+
related_objects_set = set(chain(*[related_queryset.all() for related_queryset in related_objects_set]))
238187
if remaining_lookup:
239188
return self.filter_related_objects(related_objects_set, remaining_lookup)
240189
return set(related_objects_set) - {"", None}

drf_sideloading/serializers.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
class SideLoadableSerializer(serializers.Serializer):
99
def __init__(self, instance=None, data=empty, fields_to_load=None, **kwargs):
1010
self.fields_to_load = fields_to_load
11-
super(SideLoadableSerializer, self).__init__(
12-
instance=instance, data=data, **kwargs
13-
)
11+
super(SideLoadableSerializer, self).__init__(instance=instance, data=data, **kwargs)
1412

1513
def to_representation(self, instance):
1614
"""
@@ -20,9 +18,7 @@ def to_representation(self, instance):
2018
fields = [
2119
f
2220
for f in self.fields.values()
23-
if not f.write_only
24-
and f.source in instance.keys()
25-
and f.field_name in self.fields_to_load
21+
if not f.write_only and f.source in instance.keys() and f.field_name in self.fields_to_load
2622
]
2723

2824
for field in fields:
@@ -36,9 +32,7 @@ def to_representation(self, instance):
3632
#
3733
# For related fields with `use_pk_only_optimization` we need to
3834
# resolve the pk value.
39-
check_for_none = (
40-
attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
41-
)
35+
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
4236
if check_for_none is None:
4337
ret[field.field_name] = None
4438
else:

example/manage.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/usr/bin/env python
21
import os
32
import sys
43

example/products/admin.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
3-
41
from django.contrib import admin

example/products/apps.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# -*- coding: utf-8 -*-
2-
from __future__ import unicode_literals
3-
41
from django.apps import AppConfig
52

63

example/products/migrations/0001_initial.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
# -*- coding: utf-8 -*-
21
# Generated by Django 1.11.3 on 2017-07-26 13:13
3-
from __future__ import unicode_literals
42

53
from django.db import migrations, models
64
import django.db.models.deletion

0 commit comments

Comments
 (0)