Skip to content

Commit 70cb77a

Browse files
authored
Merge pull request #48 from namespace-ee/detail-view-sideloading
Detail view sideloading
2 parents 1bd5b26 + df1dd3f commit 70cb77a

File tree

12 files changed

+1544
-366
lines changed

12 files changed

+1544
-366
lines changed

HISTORY.md

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

3+
## 2.0.0 (2021-12-10)
4+
5+
Major refactoring to allow for multi source fields.
6+
7+
- Add support for multi source fields
8+
- Add support for detail view sideloading
9+
- Dropped formless BrowsableAPIRenderer enforcement
10+
- Raises error in case invalid fields are requested for sideloading
11+
312
## 1.4.2 (2021-04-12)
413

514
- Add support for lists in filter_related_objects

README.md

Lines changed: 161 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ DRF-sideloading is an extension to provide side-loading functionality of related
1414

1515
## Quickstart
1616

17-
1. Install drf-sideloading:
17+
1. Install drf-sideloading:
1818

1919
```shell
2020
pip install drf-sideloading
@@ -26,29 +26,107 @@ DRF-sideloading is an extension to provide side-loading functionality of related
2626
from drf_sideloading.mixins import SideloadableRelationsMixin
2727
```
2828

29-
3. Write your SideLoadableSerializer
30-
You need to define the **primary** serializer in the Meta data and can define prefetching rules. Also notice the **many=True** on the sideloadable relationships.
29+
3. Write your SideLoadableSerializer:
30+
31+
You need to define the **primary** serializer in the Meta data and can define prefetching rules.
32+
Also notice the **many=True** on the sideloadable relationships.
3133

3234
```python
3335
from drf_sideloading.serializers import SideLoadableSerializer
3436
3537
class ProductSideloadableSerializer(SideLoadableSerializer):
3638
products = ProductSerializer(many=True)
3739
categories = CategorySerializer(source="category", many=True)
38-
suppliers = SupplierSerializer(source="supplier", many=True)
40+
primary_suppliers = SupplierSerializer(source="primary_supplier", many=True)
41+
secondary_suppliers = SupplierSerializer(many=True)
42+
suppliers = SupplierSerializer(many=True)
3943
partners = PartnerSerializer(many=True)
4044
4145
class Meta:
4246
primary = "products"
4347
prefetches = {
4448
"categories": "category",
45-
"suppliers": "supplier",
49+
"primary_suppliers": "primary_supplier",
50+
"secondary_suppliers": "secondary_suppliers",
51+
"suppliers": {
52+
"primary_suppliers": "primary_supplier",
53+
"secondary_suppliers": "secondary_suppliers",
54+
},
4655
"partners": "partners",
4756
}
4857
```
49-
50-
4. Configure sideloading
51-
Include **SideloadableRelationsMixin** mixin in ViewSet and define **sideloading_serializer_class** as shown in example below. Evrything else stays just like a regular ViewSet
58+
59+
4. Prefetches
60+
61+
For fields where the source is provided or where the source matches the field name, prefetches are not strictly required
62+
63+
Multiple prefetches can be added to a single sideloadable field, but when using Prefetch object check that they don't clash with prefetches made in the get_queryset() method
64+
```python
65+
from django.db.models import Prefetch
66+
67+
prefetches = {
68+
"categories": "category",
69+
"primary_suppliers": ["primary_supplier", "primary_supplier__some_related_object"],
70+
"secondary_suppliers": Prefetch(
71+
lookup="secondary_suppliers",
72+
queryset=Supplier.objects.prefetch_related("some_related_object")
73+
),
74+
"partners": Prefetch(
75+
lookup="partners",
76+
queryset=Partner.objects.select_related("some_related_object")
77+
)
78+
}
79+
```
80+
81+
Multiple sources can be added to a field using a dict.
82+
Each key is a source_key that can be used to filter what sources should be sideloaded.
83+
The values set the source and prefetches for this source.
84+
85+
Note that this prefetch reuses `primary_supplier` and `secondary_suppliers` if suppliers and primary_supplier or secondary_suppliers are sideloaded
86+
```python
87+
prefetches = {
88+
"primary_suppliers": "primary_supplier",
89+
"secondary_suppliers": "secondary_suppliers",
90+
"suppliers": {
91+
"primary_suppliers": "primary_supplier",
92+
"secondary_suppliers": "secondary_suppliers"
93+
}
94+
}
95+
```
96+
97+
Usage of Prefetch() objects is supported.
98+
Prefetch() objects can be used to filter a subset of some relations or just to prefetch or select complicated related objects
99+
In case there are prefetch conflicts, `to_attr` can be set but be aware that this prefetch will now be a duplicate of similar prefetches.
100+
prefetch conflicts can also come from prefetched made in the ViewSet.get_queryset() method.
101+
102+
Note that this prefetch noes not reuse `primary_supplier` and `secondary_suppliers` if **suppliers** and **primary_supplier** or **secondary_suppliers** are sideloaded at the same time.
103+
```python
104+
from django.db.models import Prefetch
105+
106+
prefetches = {
107+
"categories": "category",
108+
"primary_suppliers": "primary_supplier",
109+
"secondary_suppliers": "secondary_suppliers",
110+
"suppliers": {
111+
"primary_suppliers": Prefetch(
112+
lookup="secondary_suppliers",
113+
queryset=Supplier.objects.select_related("some_related_object"),
114+
to_attr="secondary_suppliers_with_preselected_relation"
115+
),
116+
"secondary_suppliers": Prefetch(
117+
lookup="secondary_suppliers",
118+
queryset=Supplier.objects.filter(created_at__gt=pendulum.now().subtract(days=10)).order_by("created_at"),
119+
to_attr="latest_secondary_suppliers"
120+
)
121+
},
122+
}
123+
```
124+
125+
5. Configure sideloading in ViewSet:
126+
127+
Include **SideloadableRelationsMixin** mixin in ViewSet and define **sideloading_serializer_class** as shown in example below.
128+
Everything else stays just like a regular ViewSet.
129+
Since version 2.0.0 there are 3 new methods that allow to overwrite the serializer used based on the request version for example
52130
53131
```python
54132
from drf_sideloading.mixins import SideloadableRelationsMixin
@@ -61,12 +139,31 @@ DRF-sideloading is an extension to provide side-loading functionality of related
61139
queryset = Product.objects.all()
62140
serializer_class = ProductSerializer
63141
sideloading_serializer_class = ProductSideloadableSerializer
142+
143+
def get_queryset(self):
144+
# Add prefetches for the viewset as normal
145+
return super().get_queryset().prefetch_related("created_by")
146+
147+
def get_sideloading_serializer_class(self):
148+
# use a different sideloadable serializer for older version
149+
if self.request.version < "1.0.0":
150+
return OldProductSideloadableSerializer
151+
return super().get_sideloading_serializer_class()
152+
153+
def get_sideloading_serializer(self, *args, **kwargs):
154+
# if modifications are required to the serializer initialization this method can be used.
155+
return super().get_sideloading_serializer(*args, **kwargs)
156+
157+
def get_sideloading_serializer_context(self):
158+
# Extra context provided to the serializer class.
159+
return {"request": self.request, "format": self.format_kwarg, "view": self}
64160
```
65161
66-
5. Enjoy your API with sideloading support
162+
6. Enjoy your API with sideloading support
67163
164+
Example request and response when fetching all possible values
68165
```http
69-
GET /api/products/?sideload=categories,partners,suppliers,products
166+
GET /api/products/?sideload=categories,partners,primary_suppliers,secondary_suppliers,suppliers,products
70167
```
71168
72169
```json
@@ -76,7 +173,8 @@ DRF-sideloading is an extension to provide side-loading functionality of related
76173
"id": 1,
77174
"name": "Product 1",
78175
"category": 1,
79-
"supplier": 1,
176+
"primary_supplier": 1,
177+
"secondary_suppliers": [2, 3],
80178
"partners": [1, 2, 3]
81179
}
82180
],
@@ -86,10 +184,34 @@ DRF-sideloading is an extension to provide side-loading functionality of related
86184
"name": "Category1"
87185
}
88186
],
187+
"primary_suppliers": [
188+
{
189+
"id": 1,
190+
"name": "Supplier1"
191+
}
192+
],
193+
"secondary_suppliers": [
194+
{
195+
"id": 2,
196+
"name": "Supplier2"
197+
},
198+
{
199+
"id": 3,
200+
"name": "Supplier3"
201+
}
202+
],
89203
"suppliers": [
90204
{
91205
"id": 1,
92206
"name": "Supplier1"
207+
},
208+
{
209+
"id": 2,
210+
"name": "Supplier2"
211+
},
212+
{
213+
"id": 3,
214+
"name": "Supplier3"
93215
}
94216
],
95217
"partners": [
@@ -108,7 +230,35 @@ DRF-sideloading is an extension to provide side-loading functionality of related
108230
]
109231
}
110232
```
233+
234+
The user can also select what sources to load to Multi source fields.
235+
Leaving the selections empty or omitting the brackets will load all the prefetched sources.
236+
237+
Example:
111238
239+
```http
240+
GET /api/products/?sideload=suppliers[primary_suppliers]
241+
```
242+
```json
243+
{
244+
"products": [
245+
{
246+
"id": 1,
247+
"name": "Product 1",
248+
"category": 1,
249+
"primary_supplier": 1,
250+
"secondary_suppliers": [2, 3],
251+
"partners": [1, 2, 3]
252+
}
253+
],
254+
"suppliers": [
255+
{
256+
"id": 1,
257+
"name": "Supplier1"
258+
}
259+
]
260+
}
261+
```
112262
## Example Project
113263
114264
Directory `example` contains an example project using django rest framework sideloading library. You can set it up and run it locally using following commands:

drf_sideloading/__init__.py

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

0 commit comments

Comments
 (0)