djresttoolkit 0.15.0__py3-none-any.whl → 0.16.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- README.md +115 -8
- {djresttoolkit-0.15.0.dist-info → djresttoolkit-0.16.1.dist-info}/METADATA +116 -9
- {djresttoolkit-0.15.0.dist-info → djresttoolkit-0.16.1.dist-info}/RECORD +8 -7
- src/djresttoolkit/pagination/__init__.py +2 -1
- src/djresttoolkit/pagination/_paginated_data_builder.py +65 -0
- {djresttoolkit-0.15.0.dist-info → djresttoolkit-0.16.1.dist-info}/WHEEL +0 -0
- {djresttoolkit-0.15.0.dist-info → djresttoolkit-0.16.1.dist-info}/entry_points.txt +0 -0
- {djresttoolkit-0.15.0.dist-info → djresttoolkit-0.16.1.dist-info}/licenses/LICENSE +0 -0
README.md
CHANGED
@@ -6,13 +6,53 @@
|
|
6
6
|
|
7
7
|
djresttoolkit is a collection of utilities and helpers for Django and Django REST Framework (DRF) that simplify common development tasks such as API handling, authentication, and email sending and much more.
|
8
8
|
|
9
|
-
##
|
9
|
+
## 📖 Feature Index (djresttoolkit)
|
10
10
|
|
11
|
-
-
|
12
|
-
|
13
|
-
|
14
|
-
-
|
15
|
-
|
11
|
+
- **DB Seed Command (`dbseed`)**
|
12
|
+
Seed your database with fake data using Pydantic models powered by **Faker**. Supports relationships, transactions, and a `manage.py dbseed` command.
|
13
|
+
|
14
|
+
- **DB Flush Command (`dbflush`)**
|
15
|
+
Management command to flush all models or a specific model, resetting auto-increment IDs safely with transaction support.
|
16
|
+
|
17
|
+
- **EnvBaseSettings**
|
18
|
+
Typed settings loader using **YAML + .env**, supports nested keys and overrides. Great for structured configuration management.
|
19
|
+
|
20
|
+
- **EmailSender**
|
21
|
+
Custom class to send templated emails (`text` and `html`) with context. Supports error handling and logging.
|
22
|
+
|
23
|
+
- **Custom DRF Exception Handler**
|
24
|
+
Centralized error handler for DRF that extends default behavior and adds throttle support (`429 Too Many Requests` with retry info).
|
25
|
+
|
26
|
+
- **Response Time Middleware**
|
27
|
+
Middleware to measure, log, and inject `X-Response-Time` headers into every response.
|
28
|
+
|
29
|
+
- **Throttle**
|
30
|
+
- `ThrottleInfoJSONRenderer`: Automatically adds throttle headers to responses.
|
31
|
+
- `ThrottleInspector`: Inspect view/request throttling and attach structured headers.
|
32
|
+
|
33
|
+
- **AbsoluteUrlFileMixin**
|
34
|
+
DRF serializer mixin that converts `FileField` / `ImageField` URLs to **absolute URLs** automatically.
|
35
|
+
|
36
|
+
- **BulkCreateMixin**
|
37
|
+
Serializer mixin that enables **bulk creation** of objects and syncs field error messages with model fields.
|
38
|
+
|
39
|
+
- **ModelChoiceFieldMixin**
|
40
|
+
Retrieve choice fields (`TextChoices`, etc.) from Django models as structured dictionaries for API responses.
|
41
|
+
|
42
|
+
- **ChoiceFieldsAPIView**
|
43
|
+
Generic API view that exposes model `choices` in a REST-friendly JSON format.
|
44
|
+
|
45
|
+
- **RetrieveObjectMixin**
|
46
|
+
Lightweight mixin to fetch a single object from a queryset with filters, raising a custom error if `queryset` is not defined.
|
47
|
+
|
48
|
+
- **build\_absolute\_uri**
|
49
|
+
Helper to build full absolute URLs for named routes with optional query params. Works with Django + DRF requests.
|
50
|
+
|
51
|
+
- **PageNumberPagination**
|
52
|
+
Custom paginator with a structured `"page"` metadata block and support for dynamic `page-size` query param.
|
53
|
+
|
54
|
+
- **PaginatedDataBuilder**
|
55
|
+
Builder that combines `PageNumberPagination` + serializers to return standardized paginated responses with `"page"` + `"results"`.
|
16
56
|
|
17
57
|
## 📦 Installation
|
18
58
|
|
@@ -30,7 +70,7 @@ djresttoolkit is a collection of utilities and helpers for Django and Django RES
|
|
30
70
|
|
31
71
|
## 📚 All API Reference
|
32
72
|
|
33
|
-
### 1. DB Seed
|
73
|
+
### 1. DB Seed Command — API Reference
|
34
74
|
|
35
75
|
#### `Generator`
|
36
76
|
|
@@ -353,7 +393,7 @@ X-Response-Time: 0.01234 seconds
|
|
353
393
|
INFO: Request processed in 0.01234 seconds
|
354
394
|
```
|
355
395
|
|
356
|
-
### 7. Throttle
|
396
|
+
### 7. Throttle — API Reference
|
357
397
|
|
358
398
|
#### `ThrottleInfoJSONRenderer`
|
359
399
|
|
@@ -866,6 +906,73 @@ from djresttoolkit.pagination import PageNumberPagination
|
|
866
906
|
}
|
867
907
|
```
|
868
908
|
|
909
|
+
### 15. PaginatedDataBuilder — API Reference
|
910
|
+
|
911
|
+
```python
|
912
|
+
from djresttoolkit.pagination import PaginatedDataBuilder
|
913
|
+
```
|
914
|
+
|
915
|
+
---
|
916
|
+
|
917
|
+
#### Description of Paginated Data Builder
|
918
|
+
|
919
|
+
- A **builder utility** to paginate and serialize Django QuerySets using DRF.
|
920
|
+
- Uses the custom **`PageNumberPagination`** class for consistent pagination responses.
|
921
|
+
- Designed for reusability inside DRF views and APIs.
|
922
|
+
|
923
|
+
#### Features of Paginated Data Builder
|
924
|
+
|
925
|
+
- Integrates with **DRF serializers**.
|
926
|
+
- Handles **invalid pages** gracefully by raising `NotFound`.
|
927
|
+
- Returns both:
|
928
|
+
- `"page"` → pagination metadata
|
929
|
+
- `"results"` → serialized data.
|
930
|
+
- Provides **structured pagination response format**.
|
931
|
+
|
932
|
+
---
|
933
|
+
|
934
|
+
#### Initialization of Paginated Data Builder
|
935
|
+
|
936
|
+
```python
|
937
|
+
builder = PaginatedDataBuilder(
|
938
|
+
request=request,
|
939
|
+
serializer_class=MySerializer,
|
940
|
+
queryset=MyModel.objects.all()
|
941
|
+
)
|
942
|
+
```
|
943
|
+
|
944
|
+
- `request: Request` → DRF request object.
|
945
|
+
- `serializer_class: type[BaseSerializer]` → DRF serializer class for the model.
|
946
|
+
- `queryset: QuerySet` → Django queryset to paginate.
|
947
|
+
|
948
|
+
### Paginated Data Builder Methods
|
949
|
+
|
950
|
+
- `get_paginated_data() -> dict[str, Any]`
|
951
|
+
|
952
|
+
- Applies pagination to the queryset.
|
953
|
+
- Serializes the paginated results.
|
954
|
+
- Returns a dictionary with `"page"` and `"results"`.
|
955
|
+
- Raises `NotFound` if no page data is found.
|
956
|
+
|
957
|
+
### Example Response of Paginated Data Builder
|
958
|
+
|
959
|
+
```json
|
960
|
+
{
|
961
|
+
"page": {
|
962
|
+
"current": 2,
|
963
|
+
"total": 5,
|
964
|
+
"size": 20,
|
965
|
+
"total_items": 100,
|
966
|
+
"next": "http://api.example.com/items/?page=3&page-size=20",
|
967
|
+
"previous": "http://api.example.com/items/?page=1&page-size=20"
|
968
|
+
},
|
969
|
+
"results": [
|
970
|
+
{ "id": 21, "name": "Item 21" },
|
971
|
+
{ "id": 22, "name": "Item 22" }
|
972
|
+
]
|
973
|
+
}
|
974
|
+
```
|
975
|
+
|
869
976
|
## 🛠️ Planned Features
|
870
977
|
|
871
978
|
- Add more utils
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: djresttoolkit
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.16.1
|
4
4
|
Summary: A collection of Django and DRF utilities to simplify API development.
|
5
5
|
Project-URL: Homepage, https://github.com/shaileshpandit141/djresttoolkit
|
6
6
|
Project-URL: Documentation, https://shaileshpandit141.github.io/djresttoolkit
|
@@ -64,13 +64,53 @@ Description-Content-Type: text/markdown
|
|
64
64
|
|
65
65
|
djresttoolkit is a collection of utilities and helpers for Django and Django REST Framework (DRF) that simplify common development tasks such as API handling, authentication, and email sending and much more.
|
66
66
|
|
67
|
-
##
|
67
|
+
## 📖 Feature Index (djresttoolkit)
|
68
68
|
|
69
|
-
-
|
70
|
-
|
71
|
-
|
72
|
-
-
|
73
|
-
|
69
|
+
- **DB Seed Command (`dbseed`)**
|
70
|
+
Seed your database with fake data using Pydantic models powered by **Faker**. Supports relationships, transactions, and a `manage.py dbseed` command.
|
71
|
+
|
72
|
+
- **DB Flush Command (`dbflush`)**
|
73
|
+
Management command to flush all models or a specific model, resetting auto-increment IDs safely with transaction support.
|
74
|
+
|
75
|
+
- **EnvBaseSettings**
|
76
|
+
Typed settings loader using **YAML + .env**, supports nested keys and overrides. Great for structured configuration management.
|
77
|
+
|
78
|
+
- **EmailSender**
|
79
|
+
Custom class to send templated emails (`text` and `html`) with context. Supports error handling and logging.
|
80
|
+
|
81
|
+
- **Custom DRF Exception Handler**
|
82
|
+
Centralized error handler for DRF that extends default behavior and adds throttle support (`429 Too Many Requests` with retry info).
|
83
|
+
|
84
|
+
- **Response Time Middleware**
|
85
|
+
Middleware to measure, log, and inject `X-Response-Time` headers into every response.
|
86
|
+
|
87
|
+
- **Throttle**
|
88
|
+
- `ThrottleInfoJSONRenderer`: Automatically adds throttle headers to responses.
|
89
|
+
- `ThrottleInspector`: Inspect view/request throttling and attach structured headers.
|
90
|
+
|
91
|
+
- **AbsoluteUrlFileMixin**
|
92
|
+
DRF serializer mixin that converts `FileField` / `ImageField` URLs to **absolute URLs** automatically.
|
93
|
+
|
94
|
+
- **BulkCreateMixin**
|
95
|
+
Serializer mixin that enables **bulk creation** of objects and syncs field error messages with model fields.
|
96
|
+
|
97
|
+
- **ModelChoiceFieldMixin**
|
98
|
+
Retrieve choice fields (`TextChoices`, etc.) from Django models as structured dictionaries for API responses.
|
99
|
+
|
100
|
+
- **ChoiceFieldsAPIView**
|
101
|
+
Generic API view that exposes model `choices` in a REST-friendly JSON format.
|
102
|
+
|
103
|
+
- **RetrieveObjectMixin**
|
104
|
+
Lightweight mixin to fetch a single object from a queryset with filters, raising a custom error if `queryset` is not defined.
|
105
|
+
|
106
|
+
- **build\_absolute\_uri**
|
107
|
+
Helper to build full absolute URLs for named routes with optional query params. Works with Django + DRF requests.
|
108
|
+
|
109
|
+
- **PageNumberPagination**
|
110
|
+
Custom paginator with a structured `"page"` metadata block and support for dynamic `page-size` query param.
|
111
|
+
|
112
|
+
- **PaginatedDataBuilder**
|
113
|
+
Builder that combines `PageNumberPagination` + serializers to return standardized paginated responses with `"page"` + `"results"`.
|
74
114
|
|
75
115
|
## 📦 Installation
|
76
116
|
|
@@ -88,7 +128,7 @@ djresttoolkit is a collection of utilities and helpers for Django and Django RES
|
|
88
128
|
|
89
129
|
## 📚 All API Reference
|
90
130
|
|
91
|
-
### 1. DB Seed
|
131
|
+
### 1. DB Seed Command — API Reference
|
92
132
|
|
93
133
|
#### `Generator`
|
94
134
|
|
@@ -411,7 +451,7 @@ X-Response-Time: 0.01234 seconds
|
|
411
451
|
INFO: Request processed in 0.01234 seconds
|
412
452
|
```
|
413
453
|
|
414
|
-
### 7. Throttle
|
454
|
+
### 7. Throttle — API Reference
|
415
455
|
|
416
456
|
#### `ThrottleInfoJSONRenderer`
|
417
457
|
|
@@ -924,6 +964,73 @@ from djresttoolkit.pagination import PageNumberPagination
|
|
924
964
|
}
|
925
965
|
```
|
926
966
|
|
967
|
+
### 15. PaginatedDataBuilder — API Reference
|
968
|
+
|
969
|
+
```python
|
970
|
+
from djresttoolkit.pagination import PaginatedDataBuilder
|
971
|
+
```
|
972
|
+
|
973
|
+
---
|
974
|
+
|
975
|
+
#### Description of Paginated Data Builder
|
976
|
+
|
977
|
+
- A **builder utility** to paginate and serialize Django QuerySets using DRF.
|
978
|
+
- Uses the custom **`PageNumberPagination`** class for consistent pagination responses.
|
979
|
+
- Designed for reusability inside DRF views and APIs.
|
980
|
+
|
981
|
+
#### Features of Paginated Data Builder
|
982
|
+
|
983
|
+
- Integrates with **DRF serializers**.
|
984
|
+
- Handles **invalid pages** gracefully by raising `NotFound`.
|
985
|
+
- Returns both:
|
986
|
+
- `"page"` → pagination metadata
|
987
|
+
- `"results"` → serialized data.
|
988
|
+
- Provides **structured pagination response format**.
|
989
|
+
|
990
|
+
---
|
991
|
+
|
992
|
+
#### Initialization of Paginated Data Builder
|
993
|
+
|
994
|
+
```python
|
995
|
+
builder = PaginatedDataBuilder(
|
996
|
+
request=request,
|
997
|
+
serializer_class=MySerializer,
|
998
|
+
queryset=MyModel.objects.all()
|
999
|
+
)
|
1000
|
+
```
|
1001
|
+
|
1002
|
+
- `request: Request` → DRF request object.
|
1003
|
+
- `serializer_class: type[BaseSerializer]` → DRF serializer class for the model.
|
1004
|
+
- `queryset: QuerySet` → Django queryset to paginate.
|
1005
|
+
|
1006
|
+
### Paginated Data Builder Methods
|
1007
|
+
|
1008
|
+
- `get_paginated_data() -> dict[str, Any]`
|
1009
|
+
|
1010
|
+
- Applies pagination to the queryset.
|
1011
|
+
- Serializes the paginated results.
|
1012
|
+
- Returns a dictionary with `"page"` and `"results"`.
|
1013
|
+
- Raises `NotFound` if no page data is found.
|
1014
|
+
|
1015
|
+
### Example Response of Paginated Data Builder
|
1016
|
+
|
1017
|
+
```json
|
1018
|
+
{
|
1019
|
+
"page": {
|
1020
|
+
"current": 2,
|
1021
|
+
"total": 5,
|
1022
|
+
"size": 20,
|
1023
|
+
"total_items": 100,
|
1024
|
+
"next": "http://api.example.com/items/?page=3&page-size=20",
|
1025
|
+
"previous": "http://api.example.com/items/?page=1&page-size=20"
|
1026
|
+
},
|
1027
|
+
"results": [
|
1028
|
+
{ "id": 21, "name": "Item 21" },
|
1029
|
+
{ "id": 22, "name": "Item 22" }
|
1030
|
+
]
|
1031
|
+
}
|
1032
|
+
```
|
1033
|
+
|
927
1034
|
## 🛠️ Planned Features
|
928
1035
|
|
929
1036
|
- Add more utils
|
@@ -1,5 +1,5 @@
|
|
1
1
|
LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
|
2
|
-
README.md,sha256=
|
2
|
+
README.md,sha256=67zRUEO0Ccp-TqVpe3FsgHqRi5JGfckiZlAjLyI2jSw,26809
|
3
3
|
demo/staticfiles/admin/img/LICENSE,sha256=0RT6_zSIwWwxmzI13EH5AjnT1j2YU3MwM9j3U19cAAQ,1081
|
4
4
|
src/djresttoolkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
src/djresttoolkit/admin.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -26,8 +26,9 @@ src/djresttoolkit/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
26
26
|
src/djresttoolkit/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
27
|
src/djresttoolkit/models/mixins/__init__.py,sha256=MHwv36f3nHwI0bXeejuO7MTYuV93ln2tSyCCipS2xVw,223
|
28
28
|
src/djresttoolkit/models/mixins/_model_choice_fields_mixin.py,sha256=9FZbe3PwrtIUZYGQh1gcOix5bfeyvKEOaNmkemvZX8E,2843
|
29
|
-
src/djresttoolkit/pagination/__init__.py,sha256=
|
29
|
+
src/djresttoolkit/pagination/__init__.py,sha256=lQhyyX381RbWBsYV9Os3OQIbY7Z6aouL0QE5kI_u5SU,176
|
30
30
|
src/djresttoolkit/pagination/_page_number_pagination.py,sha256=NHPdMZfmTurKLdgpMBT2usTiGAoZMyA3dYXq_n11y34,2358
|
31
|
+
src/djresttoolkit/pagination/_paginated_data_builder.py,sha256=N4JaJwmfmC2NiC8MqOpkxV8-itKTueaOdawnv_it4bU,2239
|
31
32
|
src/djresttoolkit/renderers/__init__.py,sha256=kmFMPRiMfD8CuJTN1_-6Z_Hqil3x8GBM0IN1roZESm0,107
|
32
33
|
src/djresttoolkit/renderers/_throttle_info_json_renderer.py,sha256=aP2cN4cB_Imcpy732zsPBQrMQqcKEs5R3dld5Y_4AMU,1089
|
33
34
|
src/djresttoolkit/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -45,8 +46,8 @@ src/djresttoolkit/views/_exceptions/__init__.py,sha256=DrCUxuPNyBR4WhzNutn5HDxLa
|
|
45
46
|
src/djresttoolkit/views/_exceptions/_exception_handler.py,sha256=_o7If47bzWLl57LeSXSWsIDsJGo2RIpwYAwNQ-hsHVY,2839
|
46
47
|
src/djresttoolkit/views/mixins/__init__.py,sha256=K-1tk5d8tCVViMynw5DdffJ3Oo5uHpEx32E3_4X2UxM,154
|
47
48
|
src/djresttoolkit/views/mixins/_retrieve_object_mixin.py,sha256=Q9znYPb07YXXUhsL7VIrk3BC-zDwjOhwLJKe2GPJ-k0,1155
|
48
|
-
djresttoolkit-0.
|
49
|
-
djresttoolkit-0.
|
50
|
-
djresttoolkit-0.
|
51
|
-
djresttoolkit-0.
|
52
|
-
djresttoolkit-0.
|
49
|
+
djresttoolkit-0.16.1.dist-info/METADATA,sha256=-GXgWkX-WeP5Mo3t1Id8QjeQnJWtHNts4PEAjahmePE,29819
|
50
|
+
djresttoolkit-0.16.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
51
|
+
djresttoolkit-0.16.1.dist-info/entry_points.txt,sha256=YMhfTF-7mYppO8QqqWnvR_hyMWvoYxD6XI94_ViFu3k,60
|
52
|
+
djresttoolkit-0.16.1.dist-info/licenses/LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
|
53
|
+
djresttoolkit-0.16.1.dist-info/RECORD,,
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from django.db.models import QuerySet
|
5
|
+
from rest_framework.exceptions import NotFound
|
6
|
+
from rest_framework.request import Request
|
7
|
+
from rest_framework.serializers import BaseSerializer
|
8
|
+
from django.db.models import Model
|
9
|
+
from ._page_number_pagination import PageNumberPagination
|
10
|
+
|
11
|
+
# Get logger from logging.
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
class PaginatedDataBuilder[T: Model]:
|
16
|
+
"""Builder class to handle pagination and serialization."""
|
17
|
+
|
18
|
+
def __init__(
|
19
|
+
self,
|
20
|
+
request: Request,
|
21
|
+
serializer_class: type[BaseSerializer[T]],
|
22
|
+
queryset: QuerySet[T],
|
23
|
+
) -> None:
|
24
|
+
"""Initilize the PaginatedDataBuilder class."""
|
25
|
+
self.request = request
|
26
|
+
self.serializer_class = serializer_class
|
27
|
+
self.queryset = queryset
|
28
|
+
|
29
|
+
def get_paginated_data(self) -> dict[str, Any]:
|
30
|
+
"""Paginate and serialize the queryset."""
|
31
|
+
|
32
|
+
logger.debug("Starting pagination with custom PageNumberPagination.")
|
33
|
+
paginator = PageNumberPagination()
|
34
|
+
page = paginator.paginate_queryset(
|
35
|
+
self.queryset,
|
36
|
+
self.request,
|
37
|
+
)
|
38
|
+
|
39
|
+
# If no data is returned from pagination, raise NotFound
|
40
|
+
if page is None:
|
41
|
+
logger.warning("No data returned from pagination. Possibly invalid page.")
|
42
|
+
raise NotFound("The requested records were not found.")
|
43
|
+
|
44
|
+
# Serialize the paginated data
|
45
|
+
serializer = self.serializer_class(
|
46
|
+
instance=page, # type: ignore
|
47
|
+
many=True,
|
48
|
+
context={"request": self.request},
|
49
|
+
)
|
50
|
+
|
51
|
+
# Construct the paginated response
|
52
|
+
paginated_data = {
|
53
|
+
"page": {
|
54
|
+
"current": paginator.page.number, # type: ignore
|
55
|
+
"total": paginator.page.paginator.num_pages, # type: ignore
|
56
|
+
"size": paginator.get_page_size(self.request),
|
57
|
+
"total_items": paginator.page.paginator.count, # type: ignore
|
58
|
+
"next": paginator.get_next_link(),
|
59
|
+
"previous": paginator.get_previous_link(),
|
60
|
+
},
|
61
|
+
"results": serializer.data,
|
62
|
+
}
|
63
|
+
|
64
|
+
logger.debug(f"Pagination result: {paginated_data}")
|
65
|
+
return paginated_data
|
File without changes
|
File without changes
|
File without changes
|