djresttoolkit 1.1.0__py3-none-any.whl → 1.2.0__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.
@@ -1,11 +1,13 @@
1
1
  import logging
2
2
  from typing import Any
3
3
 
4
- from django.db.models import QuerySet
4
+ from django.db.models import Model, QuerySet
5
5
  from rest_framework.exceptions import NotFound
6
6
  from rest_framework.request import Request
7
7
  from rest_framework.serializers import BaseSerializer
8
- from django.db.models import Model
8
+
9
+ from djresttoolkit.serializers import EnhancedModelSerializer
10
+
9
11
  from ._page_number_pagination import PageNumberPagination
10
12
 
11
13
  # Get logger from logging.
@@ -18,7 +20,7 @@ class PaginatedDataBuilder[T: Model]:
18
20
  def __init__(
19
21
  self,
20
22
  request: Request,
21
- serializer_class: type[BaseSerializer[T]],
23
+ serializer_class: type[BaseSerializer[T] | EnhancedModelSerializer[T]],
22
24
  queryset: QuerySet[T],
23
25
  ) -> None:
24
26
  """Initilize the PaginatedDataBuilder class."""
@@ -49,7 +51,7 @@ class PaginatedDataBuilder[T: Model]:
49
51
  )
50
52
 
51
53
  # Construct the paginated response
52
- paginated_data = {
54
+ paginated_data: dict[str, Any] = {
53
55
  "page": {
54
56
  "current": paginator.page.number, # type: ignore
55
57
  "total": paginator.page.paginator.num_pages, # type: ignore
@@ -0,0 +1,3 @@
1
+ from ._enhanced_model_serializer import EnhancedModelSerializer
2
+
3
+ __all__ = ["EnhancedModelSerializer"]
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ from copy import deepcopy
4
+ from typing import Any
5
+
6
+ from django.db.models import Field as DjangoField
7
+ from django.db.models import Model
8
+ from rest_framework.serializers import Field as DrfField
9
+ from rest_framework.serializers import ModelSerializer
10
+ from rest_framework.utils.model_meta import RelationInfo
11
+
12
+
13
+ class EnhancedModelSerializer[T: Model](ModelSerializer[Model]):
14
+ """
15
+ A DRF ModelSerializer that automatically applies Django model field
16
+ `error_messages` unless explicitly overridden in the serializer.
17
+ """
18
+
19
+ def _merge_error_messages(
20
+ self,
21
+ field_kwargs: dict[str, Any],
22
+ model_field: DjangoField[Any, Any] | None,
23
+ ) -> dict[str, Any]:
24
+ """Safely merge model field error_messages with serializer kwargs."""
25
+ model_errors: dict[str, str] | None = getattr(
26
+ model_field, "error_messages", None
27
+ )
28
+ if model_errors:
29
+ existing: dict[str, str] = field_kwargs.get("error_messages", {})
30
+ field_kwargs["error_messages"] = {**deepcopy(model_errors), **existing}
31
+ return field_kwargs
32
+
33
+ def build_standard_field(
34
+ self,
35
+ field_name: str,
36
+ model_field: DjangoField[Any, Any],
37
+ ) -> tuple[type[DrfField[Any, Any, Any, Any]], dict[str, Any]]:
38
+ field_class, field_kwargs = super().build_standard_field( # type: ignore
39
+ field_name,
40
+ model_field,
41
+ )
42
+ return field_class, self._merge_error_messages(
43
+ field_kwargs,
44
+ model_field,
45
+ ) # type: ignore
46
+
47
+ def build_relational_field(
48
+ self,
49
+ field_name: str,
50
+ relation_info: RelationInfo,
51
+ ) -> tuple[type[DrfField[Any, Any, Any, Any]], dict[str, Any]]:
52
+ field_class, field_kwargs = super().build_relational_field( # type: ignore
53
+ field_name,
54
+ relation_info,
55
+ )
56
+ return field_class, self._merge_error_messages(
57
+ field_kwargs,
58
+ relation_info.model_field, # type: ignore
59
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: djresttoolkit
3
- Version: 1.1.0
3
+ Version: 1.2.0
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
@@ -1081,6 +1081,27 @@ class BookViewSet(CacheInvalidateMixin, ModelViewSet):
1081
1081
  - Invalidates caches when books are created, updated, or deleted.
1082
1082
  - Supports custom cache keys per action.
1083
1083
 
1084
+ ### 17. EnhancedModelSerializer — API Reference
1085
+
1086
+ A subclass of Django REST Framework’s `ModelSerializer` that automatically merges Django model field `error_messages` into the serializer field, unless explicitly overridden.
1087
+ This helps maintain consistent validation messages between the model and the serializer.
1088
+
1089
+ #### Type Parameters
1090
+
1091
+ - `T` (`Model`): The Django model type that the serializer corresponds to.
1092
+
1093
+ #### Example of EnhancedModelSerializer
1094
+
1095
+ ```python
1096
+ from myapp.models import Book
1097
+ from myapp.serializers import EnhancedModelSerializer
1098
+
1099
+ class BookSerializer(EnhancedModelSerializer[Book]):
1100
+ class Meta:
1101
+ model = Book
1102
+ fields = "__all__"
1103
+ ```
1104
+
1084
1105
  ## 🛠️ Planned Features
1085
1106
 
1086
1107
  - Add more utils
@@ -32,10 +32,11 @@ djresttoolkit/models/mixins/__init__.py,sha256=MHwv36f3nHwI0bXeejuO7MTYuV93ln2tS
32
32
  djresttoolkit/models/mixins/_model_choice_fields_mixin.py,sha256=9FZbe3PwrtIUZYGQh1gcOix5bfeyvKEOaNmkemvZX8E,2843
33
33
  djresttoolkit/pagination/__init__.py,sha256=lQhyyX381RbWBsYV9Os3OQIbY7Z6aouL0QE5kI_u5SU,176
34
34
  djresttoolkit/pagination/_page_number_pagination.py,sha256=NHPdMZfmTurKLdgpMBT2usTiGAoZMyA3dYXq_n11y34,2358
35
- djresttoolkit/pagination/_paginated_data_builder.py,sha256=N4JaJwmfmC2NiC8MqOpkxV8-itKTueaOdawnv_it4bU,2239
35
+ djresttoolkit/pagination/_paginated_data_builder.py,sha256=_HjWbh0kUsn1WswONhqFI4dPCog3foqFQryLpmGarD8,2320
36
36
  djresttoolkit/renderers/__init__.py,sha256=kmFMPRiMfD8CuJTN1_-6Z_Hqil3x8GBM0IN1roZESm0,107
37
37
  djresttoolkit/renderers/_throttle_info_json_renderer.py,sha256=aP2cN4cB_Imcpy732zsPBQrMQqcKEs5R3dld5Y_4AMU,1089
38
- djresttoolkit/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ djresttoolkit/serializers/__init__.py,sha256=367CLluj8C15Zpr7BD-euP4DxKbwepm58gnGGcuYOJU,103
39
+ djresttoolkit/serializers/_enhanced_model_serializer.py,sha256=XNYM-zy5_ecfUNRtmBfaDKSuxEDhmoBV9HNx8iSaDpE,2055
39
40
  djresttoolkit/serializers/mixins/__init__.py,sha256=dRT0kXDckOkZo1RQHrT1gXbGFMIv5M8TBHGF2uF-81Q,225
40
41
  djresttoolkit/serializers/mixins/_absolute_url_file_mixin.py,sha256=5ewael0_RsJZ9b36IfXacxjb-Vx1eQ9Dk6dWuj5D_dc,3261
41
42
  djresttoolkit/serializers/mixins/_bulk_create_mixin.py,sha256=9ZWm2MNaZOhmhKlWOu6VECtlDbUtaPeceGHmivDYwYQ,3248
@@ -50,8 +51,8 @@ djresttoolkit/views/_exceptions/__init__.py,sha256=DrCUxuPNyBR4WhzNutn5HDxLa--q5
50
51
  djresttoolkit/views/_exceptions/_exception_handler.py,sha256=_o7If47bzWLl57LeSXSWsIDsJGo2RIpwYAwNQ-hsHVY,2839
51
52
  djresttoolkit/views/mixins/__init__.py,sha256=mHD49OUxuJ9v81tGfM0hLnUJuJlYi7E-5cTVdplh-vs,91
52
53
  djresttoolkit/views/mixins/_retrieve_object_mixin.py,sha256=v7CQDUkRWjtevFZnAYRBdDl7wcfYWF3evWoKWHAcckA,1749
53
- djresttoolkit-1.1.0.dist-info/METADATA,sha256=0Ln7HqEDc0-fHHiJZhZv0uipTH5TC9IsoWH9dWCswvI,31878
54
- djresttoolkit-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
- djresttoolkit-1.1.0.dist-info/entry_points.txt,sha256=YMhfTF-7mYppO8QqqWnvR_hyMWvoYxD6XI94_ViFu3k,60
56
- djresttoolkit-1.1.0.dist-info/licenses/LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
57
- djresttoolkit-1.1.0.dist-info/RECORD,,
54
+ djresttoolkit-1.2.0.dist-info/METADATA,sha256=ZLrUSwjbWip4-Rh6Mrkc2sD2sha_AHF8G-cR39VdiOo,32552
55
+ djresttoolkit-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
+ djresttoolkit-1.2.0.dist-info/entry_points.txt,sha256=YMhfTF-7mYppO8QqqWnvR_hyMWvoYxD6XI94_ViFu3k,60
57
+ djresttoolkit-1.2.0.dist-info/licenses/LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
58
+ djresttoolkit-1.2.0.dist-info/RECORD,,