djresttoolkit 0.16.1__py3-none-any.whl → 0.17.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.
Files changed (60) hide show
  1. djresttoolkit/views/mixins/__init__.py +16 -0
  2. djresttoolkit/views/mixins/_cache_action_mixin.py +44 -0
  3. djresttoolkit/views/mixins/_cache_invalidate_mixin.py +38 -0
  4. djresttoolkit/views/mixins/_cache_key_mixin.py +33 -0
  5. djresttoolkit/views/mixins/_cache_list_retrieve_mixin.py +57 -0
  6. djresttoolkit/views/mixins/_cache_ops_mixin.py +47 -0
  7. {djresttoolkit-0.16.1.dist-info → djresttoolkit-0.17.1.dist-info}/METADATA +91 -14
  8. djresttoolkit-0.17.1.dist-info/RECORD +55 -0
  9. README.md +0 -990
  10. demo/staticfiles/admin/img/LICENSE +0 -20
  11. djresttoolkit-0.16.1.dist-info/RECORD +0 -53
  12. djresttoolkit-0.16.1.dist-info/licenses/LICENSE +0 -23
  13. src/djresttoolkit/views/mixins/__init__.py +0 -6
  14. {src/djresttoolkit → djresttoolkit}/__init__.py +0 -0
  15. {src/djresttoolkit → djresttoolkit}/admin.py +0 -0
  16. {src/djresttoolkit → djresttoolkit}/apps.py +0 -0
  17. {src/djresttoolkit → djresttoolkit}/dbseed/__init__.py +0 -0
  18. {src/djresttoolkit → djresttoolkit}/dbseed/models/__init__.py +0 -0
  19. {src/djresttoolkit → djresttoolkit}/dbseed/models/_choice_field.py +0 -0
  20. {src/djresttoolkit → djresttoolkit}/dbseed/models/_gen.py +0 -0
  21. {src/djresttoolkit → djresttoolkit}/dbseed/models/_seed_model.py +0 -0
  22. {src/djresttoolkit → djresttoolkit}/envconfig/__init__.py +0 -0
  23. {src/djresttoolkit → djresttoolkit}/envconfig/_env_settings.py +0 -0
  24. {src/djresttoolkit → djresttoolkit}/mail/__init__.py +0 -0
  25. {src/djresttoolkit → djresttoolkit}/mail/_email_sender.py +0 -0
  26. {src/djresttoolkit → djresttoolkit}/mail/_models.py +0 -0
  27. {src/djresttoolkit → djresttoolkit}/mail/_types.py +0 -0
  28. {src/djresttoolkit → djresttoolkit}/management/__init__.py +0 -0
  29. {src/djresttoolkit → djresttoolkit}/management/commands/__init__.py +0 -0
  30. {src/djresttoolkit → djresttoolkit}/management/commands/dbflush.py +0 -0
  31. {src/djresttoolkit → djresttoolkit}/management/commands/dbseed.py +0 -0
  32. {src/djresttoolkit → djresttoolkit}/middlewares/__init__.py +0 -0
  33. {src/djresttoolkit → djresttoolkit}/middlewares/_response_time_middleware.py +0 -0
  34. {src/djresttoolkit → djresttoolkit}/migrations/__init__.py +0 -0
  35. {src/djresttoolkit → djresttoolkit}/models/__init__.py +0 -0
  36. {src/djresttoolkit → djresttoolkit}/models/mixins/__init__.py +0 -0
  37. {src/djresttoolkit → djresttoolkit}/models/mixins/_model_choice_fields_mixin.py +0 -0
  38. {src/djresttoolkit → djresttoolkit}/pagination/__init__.py +0 -0
  39. {src/djresttoolkit → djresttoolkit}/pagination/_page_number_pagination.py +0 -0
  40. {src/djresttoolkit → djresttoolkit}/pagination/_paginated_data_builder.py +0 -0
  41. {src/djresttoolkit → djresttoolkit}/py.typed +0 -0
  42. {src/djresttoolkit → djresttoolkit}/renderers/__init__.py +0 -0
  43. {src/djresttoolkit → djresttoolkit}/renderers/_throttle_info_json_renderer.py +0 -0
  44. {src/djresttoolkit → djresttoolkit}/serializers/__init__.py +0 -0
  45. {src/djresttoolkit → djresttoolkit}/serializers/mixins/__init__.py +0 -0
  46. {src/djresttoolkit → djresttoolkit}/serializers/mixins/_absolute_url_file_mixin.py +0 -0
  47. {src/djresttoolkit → djresttoolkit}/serializers/mixins/_bulk_create_mixin.py +0 -0
  48. {src/djresttoolkit → djresttoolkit}/throttling/__init__.py +0 -0
  49. {src/djresttoolkit → djresttoolkit}/throttling/_throttle_inspector.py +0 -0
  50. {src/djresttoolkit → djresttoolkit}/urls/__init__.py +0 -0
  51. {src/djresttoolkit → djresttoolkit}/urls/_build_absolute_uri.py +0 -0
  52. {src/djresttoolkit → djresttoolkit}/views/__init__.py +0 -0
  53. {src/djresttoolkit → djresttoolkit}/views/_api_views/__init__.py +0 -0
  54. {src/djresttoolkit → djresttoolkit}/views/_api_views/_choice_fields_apiview.py +0 -0
  55. {src/djresttoolkit → djresttoolkit}/views/_exceptions/__init__.py +0 -0
  56. {src/djresttoolkit → djresttoolkit}/views/_exceptions/_exception_handler.py +0 -0
  57. {src/djresttoolkit → djresttoolkit}/views/mixins/_retrieve_object_mixin.py +0 -0
  58. {djresttoolkit-0.16.1.dist-info → djresttoolkit-0.17.1.dist-info}/WHEEL +0 -0
  59. {djresttoolkit-0.16.1.dist-info → djresttoolkit-0.17.1.dist-info}/entry_points.txt +0 -0
  60. /LICENSE → /djresttoolkit-0.17.1.dist-info/licenses/LICENSE +0 -0
@@ -0,0 +1,16 @@
1
+ from ._retrieve_object_mixin import RetrieveObjectMixin, QuerysetNotDefinedError
2
+ from ._cache_action_mixin import CacheActionMixin
3
+ from ._cache_invalidate_mixin import CacheInvalidateMixin
4
+ from ._cache_key_mixin import CacheKeyMixin
5
+ from ._cache_list_retrieve_mixin import CacheListRetrieveMixin
6
+ from ._cache_ops_mixin import CacheOpsMixin
7
+
8
+ __all__ = [
9
+ "RetrieveObjectMixin",
10
+ "QuerysetNotDefinedError",
11
+ "CacheActionMixin",
12
+ "CacheInvalidateMixin",
13
+ "CacheKeyMixin",
14
+ "CacheListRetrieveMixin",
15
+ "CacheOpsMixin",
16
+ ]
@@ -0,0 +1,44 @@
1
+ from functools import wraps
2
+ from typing import Any, Callable
3
+
4
+ from rest_framework.response import Response
5
+ from ._cache_ops_mixin import CacheOpsMixin
6
+ from rest_framework.request import Request
7
+
8
+
9
+ class CacheActionMixin(CacheOpsMixin):
10
+ """Provides decorator for caching custom @action methods."""
11
+
12
+ def cache_action(
13
+ self,
14
+ detail: bool = False,
15
+ action_name: str | None = None,
16
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
17
+ def decorator(view_method: Callable[..., Any]) -> Callable[..., Any]:
18
+ @wraps(view_method)
19
+ def wrapper(
20
+ viewset: Any,
21
+ request: Request,
22
+ *args: Any,
23
+ **kwargs: Any,
24
+ ) -> Response:
25
+ name = action_name or view_method.__name__
26
+ pk = kwargs.get("pk") if detail else None
27
+ action_type = "custom-detail" if detail else "custom-list"
28
+ key = viewset.get_cache_key(action_type, pk=pk, action_name=name)
29
+
30
+ def get_data() -> None | Any:
31
+ response = view_method(
32
+ viewset,
33
+ request,
34
+ *args,
35
+ **kwargs,
36
+ )
37
+ return response.data if isinstance(response, Response) else response
38
+
39
+ data = viewset.get_or_set_cache(key, get_data)
40
+ return Response(data)
41
+
42
+ return wrapper
43
+
44
+ return decorator
@@ -0,0 +1,38 @@
1
+ from typing import Any
2
+ from rest_framework.request import Request
3
+ from rest_framework.response import Response
4
+ from ._cache_list_retrieve_mixin import CacheListRetrieveMixin
5
+
6
+
7
+ class CacheInvalidateMixin(CacheListRetrieveMixin):
8
+ """Invalidate caches after create, update, destroy."""
9
+
10
+ def create(
11
+ self,
12
+ request: Request,
13
+ *args: Any,
14
+ **kwargs: Any,
15
+ ) -> Response:
16
+ response = super().create(request, *args, **kwargs) # type: ignore
17
+ self.invalidate_cache()
18
+ return response # type: ignore
19
+
20
+ def update(
21
+ self,
22
+ request: Request,
23
+ *args: Any,
24
+ **kwargs: Any,
25
+ ) -> Response:
26
+ response = super().update(request, *args, **kwargs) # type: ignore
27
+ self.invalidate_cache(pk=self.kwargs.get("pk")) # type: ignore
28
+ return response # type: ignore
29
+
30
+ def destroy(
31
+ self,
32
+ request: Request,
33
+ *args: Any,
34
+ **kwargs: Any,
35
+ ) -> Response:
36
+ response = super().destroy(request, *args, **kwargs) # type: ignore
37
+ self.invalidate_cache(pk=self.kwargs.get("pk")) # type: ignore
38
+ return response # type: ignore
@@ -0,0 +1,33 @@
1
+ import hashlib
2
+ import json
3
+ from typing import Any
4
+
5
+
6
+ class CacheKeyMixin:
7
+ """Handles generating unique cache keys for views."""
8
+
9
+ cache_timeout: int = 300
10
+
11
+ def get_cache_timeout(self) -> int:
12
+ return self.cache_timeout
13
+
14
+ def get_cache_key(
15
+ self,
16
+ action_type: str,
17
+ pk: Any | str = None,
18
+ action_name: str | None = None,
19
+ ) -> str | None:
20
+ if action_type in ("list", "custom-list"):
21
+ query_params = dict(sorted(self.request.query_params.items())) # type: ignore
22
+ query_string = json.dumps(query_params, separators=(",", ":"))
23
+ query_hash = hashlib.md5(query_string.encode()).hexdigest()
24
+ if action_type == "list":
25
+ return f"{self.basename}_list_{query_hash}" # type: ignore
26
+ return f"{self.basename}_{action_name}_list_{query_hash}" # type: ignore
27
+
28
+ if action_type in ("retrieve", "custom-detail") and pk is not None:
29
+ if action_type == "retrieve":
30
+ return f"{self.basename}_detail_{pk}" # type: ignore
31
+ return f"{self.basename}_{action_name}_detail_{pk}" # type: ignore
32
+
33
+ return None
@@ -0,0 +1,57 @@
1
+ from typing import Any
2
+
3
+ from rest_framework.response import Response
4
+ from rest_framework.request import Request
5
+ from ._cache_action_mixin import CacheActionMixin
6
+
7
+
8
+ class CacheListRetrieveMixin(CacheActionMixin):
9
+ """Caches list() and retrieve() responses."""
10
+
11
+ def list(
12
+ self,
13
+ request: Request,
14
+ *args: Any,
15
+ **kwargs: Any,
16
+ ) -> Response:
17
+ cache_key = self.get_cache_key("list")
18
+ if not cache_key:
19
+ return super().list(request, *args, **kwargs) # type: ignore
20
+
21
+ data = self.get_or_set_cache(
22
+ cache_key,
23
+ lambda: self._get_list_data(request), # type: ignore
24
+ )
25
+ return Response(data)
26
+
27
+ def _get_list_data(self, request: Response) -> Any:
28
+ queryset = self.filter_queryset(self.get_queryset()) # type: ignore
29
+ page = self.paginate_queryset(queryset) # type: ignore
30
+ if page is not None:
31
+ serializer = self.get_serializer(page, many=True) # type: ignore
32
+ return self.get_paginated_response(serializer.data).data # type: ignore
33
+ else:
34
+ serializer = self.get_serializer(queryset, many=True) # type: ignore
35
+ return serializer.data # type: ignore
36
+
37
+ def retrieve(
38
+ self,
39
+ request: Request,
40
+ *args: Any,
41
+ **kwargs: Any,
42
+ ) -> Response:
43
+ pk = self.kwargs.get("pk") # type: ignore
44
+ cache_key = self.get_cache_key("retrieve", pk=pk) # type: ignore
45
+ if not cache_key:
46
+ return super().retrieve(request, *args, **kwargs) # type: ignore
47
+
48
+ data = self.get_or_set_cache(
49
+ cache_key,
50
+ lambda: self._get_detail_data(),
51
+ )
52
+ return Response(data)
53
+
54
+ def _get_detail_data(self) -> Any:
55
+ instance = self.get_object() # type: ignore
56
+ serializer = self.get_serializer(instance) # type: ignore
57
+ return serializer.data # type: ignore
@@ -0,0 +1,47 @@
1
+ from typing import Any, Callable
2
+
3
+ from django.core.cache import cache
4
+
5
+ from ._cache_key_mixin import CacheKeyMixin
6
+
7
+
8
+ class CacheOpsMixin(CacheKeyMixin):
9
+ """Handles getting, setting, and invalidating cache."""
10
+
11
+ def get_or_set_cache(
12
+ self,
13
+ cache_key: str,
14
+ data_fn: Callable[[], Any],
15
+ timeout: int | None = None,
16
+ ) -> Any:
17
+ data = cache.get(cache_key)
18
+ if data is None:
19
+ data = data_fn()
20
+ cache.set(cache_key, data, timeout or self.get_cache_timeout())
21
+ return data
22
+
23
+ def invalidate_cache(
24
+ self,
25
+ pk: Any | None = None,
26
+ custom_actions: list[str] | None = None,
27
+ ) -> None:
28
+ if pk:
29
+ key = self.get_cache_key("retrieve", pk=pk)
30
+ if key:
31
+ cache.delete(key)
32
+
33
+ if custom_actions:
34
+ for action in custom_actions:
35
+ key = self.get_cache_key(
36
+ "custom-detail",
37
+ pk=pk,
38
+ action_name=action,
39
+ )
40
+ if key:
41
+ cache.delete(key)
42
+
43
+ if hasattr(cache, "delete_pattern"):
44
+ cache.delete_pattern(f"{self.basename}_list_*") # type: ignore
45
+ if custom_actions:
46
+ for action in custom_actions:
47
+ cache.delete_pattern(f"{self.basename}_{action}_list_*") # type: ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: djresttoolkit
3
- Version: 0.16.1
3
+ Version: 0.17.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
@@ -112,6 +112,9 @@ djresttoolkit is a collection of utilities and helpers for Django and Django RES
112
112
  - **PaginatedDataBuilder**
113
113
  Builder that combines `PageNumberPagination` + serializers to return standardized paginated responses with `"page"` + `"results"`.
114
114
 
115
+ - **Caching Mixins**
116
+ This module provides a set of DRF mixins to handle caching for `list`, `retrieve`, and `custom actions` with automatic invalidation on create, update, and destroy.
117
+
115
118
  ## 📦 Installation
116
119
 
117
120
  - **By using uv:**
@@ -312,7 +315,7 @@ print(settings.database_url)
312
315
  #### Features
313
316
 
314
317
  - Prioritizes `.env` variables over YAML.
315
- - Supports nested keys: `DATABASE__HOST` `settings.database.host`.
318
+ - Supports nested keys: `DATABASE__HOST`:- `settings.database.host`.
316
319
  - Designed to be subclassed for project-specific settings.
317
320
 
318
321
  ### 4. EmailSender — API Reference
@@ -928,17 +931,17 @@ from djresttoolkit.pagination import PageNumberPagination
928
931
  - Clients can control items per page using `?page-size=`.
929
932
  - Structured pagination metadata:
930
933
 
931
- - `current` current page number
932
- - `total` total number of pages
933
- - `size` number of items per page
934
- - `total_items` total number of items across all pages
935
- - `next` next page URL
936
- - `previous` previous page URL
934
+ - `current`:- current page number
935
+ - `total`:- total number of pages
936
+ - `size`:- number of items per page
937
+ - `total_items`:- total number of items across all pages
938
+ - `next`:- next page URL
939
+ - `previous`:- previous page URL
937
940
  - Standardized API response format.
938
941
 
939
942
  ### Attributes of Page Number Pagination
940
943
 
941
- - `page_size_query_param: str` Query parameter name (`"page-size"`).
944
+ - `page_size_query_param: str`:- Query parameter name (`"page-size"`).
942
945
 
943
946
  ### Page Number Pagination Methods
944
947
 
@@ -983,8 +986,8 @@ from djresttoolkit.pagination import PaginatedDataBuilder
983
986
  - Integrates with **DRF serializers**.
984
987
  - Handles **invalid pages** gracefully by raising `NotFound`.
985
988
  - Returns both:
986
- - `"page"` pagination metadata
987
- - `"results"` serialized data.
989
+ - `"page"`:- pagination metadata
990
+ - `"results"`:- serialized data.
988
991
  - Provides **structured pagination response format**.
989
992
 
990
993
  ---
@@ -999,9 +1002,9 @@ builder = PaginatedDataBuilder(
999
1002
  )
1000
1003
  ```
1001
1004
 
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
+ - `request: Request`:- DRF request object.
1006
+ - `serializer_class: type[BaseSerializer]`:- DRF serializer class for the model.
1007
+ - `queryset: QuerySet`:- Django queryset to paginate.
1005
1008
 
1006
1009
  ### Paginated Data Builder Methods
1007
1010
 
@@ -1031,6 +1034,80 @@ builder = PaginatedDataBuilder(
1031
1034
  }
1032
1035
  ```
1033
1036
 
1037
+ ### 16. Caching Mixins — API Reference
1038
+
1039
+ This module provides a set of DRF mixins to handle **caching for list, retrieve, and custom actions** with automatic invalidation on create, update, and destroy.
1040
+
1041
+ #### 1️ `CacheKeyMixin`
1042
+
1043
+ - **Purpose**: Generate unique cache keys for DRF viewset actions.
1044
+ - **Attributes**:
1045
+ - `cache_timeout: int = 300`:- default cache duration in seconds.
1046
+ - **Methods**:
1047
+ - `get_cache_timeout()`:- returns the cache timeout.
1048
+ - `get_cache_key(action_type, pk=None, action_name=None)`:- returns a cache key string based on action type:**
1049
+ - `list` or `custom-list`:- hash of query parameters.
1050
+ - `retrieve` or `custom-detail`:- uses primary key (`pk`).
1051
+
1052
+ #### 2️ `CacheOpsMixin`
1053
+
1054
+ - **Purpose**: Get, set, and invalidate cache.
1055
+ - **Methods**:
1056
+ - `get_or_set_cache(cache_key, data_fn, timeout=None)`:- fetch from cache or compute and set.
1057
+ - `invalidate_cache(pk=None, custom_actions=None)`:- delete cached items:
1058
+ - Deletes retrieve/detail caches for a `pk`.
1059
+ - Deletes list caches (supports `delete_pattern` if available).
1060
+
1061
+ #### 3️ `CacheActionMixin`
1062
+
1063
+ - **Purpose**: Decorator for caching custom DRF `@action` methods.
1064
+ - **Methods**:
1065
+ - `cache_action(detail=False, action_name=None)`:- returns a decorator that caches action results automatically.
1066
+ - Works for:
1067
+ - `detail=False`:- custom-list action cache.
1068
+ - `detail=True`:- custom-detail action cache.
1069
+
1070
+ #### 4️ `CacheListRetrieveMixin`
1071
+
1072
+ - **Purpose**: Caches DRF `list()` and `retrieve()` responses.
1073
+ - **Methods**:
1074
+ - `list(request, *args, **kwargs)`:- caches list responses.
1075
+ - `retrieve(request, *args, **kwargs)`:- caches detail responses.
1076
+ - `_get_list_data(request)`:- internal method to fetch paginated list data.
1077
+ - `_get_detail_data()`:- internal method to fetch a single object.
1078
+
1079
+ #### 5️ `CacheInvalidateMixin`
1080
+
1081
+ - **Purpose**: Automatically invalidates caches on write operations.
1082
+ - **Methods**:
1083
+ - `create(request, *args, **kwargs)`:- invalidates list caches.
1084
+ - `update(request, *args, **kwargs)`:- invalidates detail caches for `pk`.
1085
+ - `destroy(request, *args, **kwargs)`:- invalidates detail caches for `pk`.
1086
+
1087
+ #### Example of Caching Mixins
1088
+
1089
+ ```python
1090
+ from rest_framework.viewsets import ModelViewSet
1091
+ from myapp.models import Book
1092
+ from myapp.serializers import BookSerializer
1093
+ from djresttoolkit.cache_mixins import CacheInvalidateMixin, CacheActionMixin
1094
+
1095
+ class BookViewSet(CacheInvalidateMixin, ModelViewSet):
1096
+ queryset = Book.objects.all()
1097
+ serializer_class = BookSerializer
1098
+ basename = "book"
1099
+
1100
+ @CacheActionMixin.cache_action(detail=False)
1101
+ def popular(self, request):
1102
+ data = Book.objects.filter(is_popular=True)
1103
+ serializer = self.get_serializer(data, many=True)
1104
+ return Response(serializer.data)
1105
+ ```
1106
+
1107
+ - Automatically caches `list`, `retrieve`, and `popular` actions.
1108
+ - Invalidates caches when books are created, updated, or deleted.
1109
+ - Supports custom cache keys per action.
1110
+
1034
1111
  ## 🛠️ Planned Features
1035
1112
 
1036
1113
  - Add more utils
@@ -0,0 +1,55 @@
1
+ djresttoolkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ djresttoolkit/admin.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ djresttoolkit/apps.py,sha256=nKb5GUIEhAB3IL3lTmEXNc5XuvvaZupH-1CCuYKFrEQ,158
4
+ djresttoolkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ djresttoolkit/dbseed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ djresttoolkit/dbseed/models/__init__.py,sha256=uuynQIcfVqEaZN9hF_caI24zm8az23JdXLDrv7xOKTQ,180
7
+ djresttoolkit/dbseed/models/_choice_field.py,sha256=T7LAzbyXqlYp2mtCAKL8E1Da_MEh9RzgLZrFJ7fa4gM,446
8
+ djresttoolkit/dbseed/models/_gen.py,sha256=qBPQaLvh1rcEam0YmE4JBJqpa-Vv5IFlIIagkEMHDVw,206
9
+ djresttoolkit/dbseed/models/_seed_model.py,sha256=0cmbi0VNKjmJbwhjeCFsvb3iKYjok6TJOk6Y2MF_3N4,2443
10
+ djresttoolkit/envconfig/__init__.py,sha256=PcLaPaVfQmz3-4m6SwoOQF2W4U7F0agBGJ4Qjqbcyfw,74
11
+ djresttoolkit/envconfig/_env_settings.py,sha256=X-pgHqNtUNQLBHiGpbYB4ViCuY14n6aff5HZqYL4Tlc,3236
12
+ djresttoolkit/mail/__init__.py,sha256=tB9SdMlhfWQ640q4aobZ0H1c7fTWalpDL2I-onkr2VI,268
13
+ djresttoolkit/mail/_email_sender.py,sha256=bPMqgD5HibJcOZgO6xxHOhdK9HEhnGNC6BoMPpo-h7k,3096
14
+ djresttoolkit/mail/_models.py,sha256=of5KsLGvsN2OWgDYgdtLEijulg817TXgsLKuUdsnDQc,1447
15
+ djresttoolkit/mail/_types.py,sha256=zf6CcXR1ei_UmZ1nLAJa378OAJ6ftnBICqEOkzXPNw8,646
16
+ djresttoolkit/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ djresttoolkit/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ djresttoolkit/management/commands/dbflush.py,sha256=v7rXcuNQDiXLV7p0xzKMNXVeWAN7QKmq2qQQAbhHEEw,3423
19
+ djresttoolkit/management/commands/dbseed.py,sha256=sY87TvvzECG7aQXxsDjr45VzCzJpofLacBK49EYFK8M,4225
20
+ djresttoolkit/middlewares/__init__.py,sha256=GZHU3Yy4xXoEi62tHn0UJNxN6XgGM2_HES8Bt5AS5Lk,100
21
+ djresttoolkit/middlewares/_response_time_middleware.py,sha256=1wCwdkW5Ng6HJo8zx0F7ylms84OGP-1K0kbyG6Vacuk,908
22
+ djresttoolkit/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ djresttoolkit/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ djresttoolkit/models/mixins/__init__.py,sha256=MHwv36f3nHwI0bXeejuO7MTYuV93ln2tSyCCipS2xVw,223
25
+ djresttoolkit/models/mixins/_model_choice_fields_mixin.py,sha256=9FZbe3PwrtIUZYGQh1gcOix5bfeyvKEOaNmkemvZX8E,2843
26
+ djresttoolkit/pagination/__init__.py,sha256=lQhyyX381RbWBsYV9Os3OQIbY7Z6aouL0QE5kI_u5SU,176
27
+ djresttoolkit/pagination/_page_number_pagination.py,sha256=NHPdMZfmTurKLdgpMBT2usTiGAoZMyA3dYXq_n11y34,2358
28
+ djresttoolkit/pagination/_paginated_data_builder.py,sha256=N4JaJwmfmC2NiC8MqOpkxV8-itKTueaOdawnv_it4bU,2239
29
+ djresttoolkit/renderers/__init__.py,sha256=kmFMPRiMfD8CuJTN1_-6Z_Hqil3x8GBM0IN1roZESm0,107
30
+ djresttoolkit/renderers/_throttle_info_json_renderer.py,sha256=aP2cN4cB_Imcpy732zsPBQrMQqcKEs5R3dld5Y_4AMU,1089
31
+ djresttoolkit/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ djresttoolkit/serializers/mixins/__init__.py,sha256=dRT0kXDckOkZo1RQHrT1gXbGFMIv5M8TBHGF2uF-81Q,225
33
+ djresttoolkit/serializers/mixins/_absolute_url_file_mixin.py,sha256=5ewael0_RsJZ9b36IfXacxjb-Vx1eQ9Dk6dWuj5D_dc,3261
34
+ djresttoolkit/serializers/mixins/_bulk_create_mixin.py,sha256=9ZWm2MNaZOhmhKlWOu6VECtlDbUtaPeceGHmivDYwYQ,3248
35
+ djresttoolkit/throttling/__init__.py,sha256=01sjMymjx8XjqnAw3bEBLc-JtfhCDrp5dGxSNXMvPpU,84
36
+ djresttoolkit/throttling/_throttle_inspector.py,sha256=Kss6ZxKy-EXq9UGaGprGDhpSuJ5992bmEYZSWmUVBHo,6480
37
+ djresttoolkit/urls/__init__.py,sha256=u-Hj8GARO0mWZwiehCi3VslMXqtkdAFff7VcXVuiN8w,86
38
+ djresttoolkit/urls/_build_absolute_uri.py,sha256=X9kdoTRGIJ2ordpkHgSXmqwmmMT-OZBgluV5ULUhJEk,915
39
+ djresttoolkit/views/__init__.py,sha256=QuJ9C0Vfzkfrwyqzunxh-A-aErmS6yOoy0uzjDu3oG8,177
40
+ djresttoolkit/views/_api_views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
+ djresttoolkit/views/_api_views/_choice_fields_apiview.py,sha256=zABPgqxMVaWd814B_sC64bWL61fDJkyYQZmJXQCa6Xc,1395
42
+ djresttoolkit/views/_exceptions/__init__.py,sha256=DrCUxuPNyBR4WhzNutn5HDxLa--q51ykIxSG7_bFsOI,83
43
+ djresttoolkit/views/_exceptions/_exception_handler.py,sha256=_o7If47bzWLl57LeSXSWsIDsJGo2RIpwYAwNQ-hsHVY,2839
44
+ djresttoolkit/views/mixins/__init__.py,sha256=Ge-Dwa6S5-4BpfzMjLfxEkkepk8F3IJn0DNWBETq1Hw,537
45
+ djresttoolkit/views/mixins/_cache_action_mixin.py,sha256=Vle_xbHc6dzI2SqxLiM5HsrDkOzSgqj8GFB85MyPdaw,1516
46
+ djresttoolkit/views/mixins/_cache_invalidate_mixin.py,sha256=Ax6VC1lg8qYGW5IK339hqq40SRKj61J3Jp-mB_2gPWc,1170
47
+ djresttoolkit/views/mixins/_cache_key_mixin.py,sha256=0kt1rgz34q43DCLFwlR4rkyHtoWDYCMLXaTe7XfRZu4,1170
48
+ djresttoolkit/views/mixins/_cache_list_retrieve_mixin.py,sha256=nxJpfkNZWuOpf7wDDkGwq_ZQNT_17-iRisdTB2Cc0iY,1935
49
+ djresttoolkit/views/mixins/_cache_ops_mixin.py,sha256=veaBwH3Bp3GAzFizIxXT8EhmUF1AY3kCZBcF7rHPfyc,1435
50
+ djresttoolkit/views/mixins/_retrieve_object_mixin.py,sha256=Q9znYPb07YXXUhsL7VIrk3BC-zDwjOhwLJKe2GPJ-k0,1155
51
+ djresttoolkit-0.17.1.dist-info/METADATA,sha256=hnb1so12F-UOfjK4704Qlk6gQrArmZJDxzfoBby7B3s,32930
52
+ djresttoolkit-0.17.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
+ djresttoolkit-0.17.1.dist-info/entry_points.txt,sha256=YMhfTF-7mYppO8QqqWnvR_hyMWvoYxD6XI94_ViFu3k,60
54
+ djresttoolkit-0.17.1.dist-info/licenses/LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
55
+ djresttoolkit-0.17.1.dist-info/RECORD,,