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
README.md DELETED
@@ -1,990 +0,0 @@
1
- # 🛠️ djresttoolkit (django rest toolkit)
2
-
3
- [![PyPI version](https://img.shields.io/pypi/v/djresttoolkit.svg)](https://pypi.org/project/djresttoolkit/)
4
- [![Python versions](https://img.shields.io/pypi/pyversions/djresttoolkit.svg)](https://pypi.org/project/djresttoolkit/)
5
- [![License](https://img.shields.io/pypi/l/djresttoolkit.svg)](https://github.com/shaileshpandit141/djresttoolkit/blob/main/LICENSE)
6
-
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
-
9
- ## 📖 Feature Index (djresttoolkit)
10
-
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"`.
56
-
57
- ## 📦 Installation
58
-
59
- - **By using uv:**
60
-
61
- ```bash
62
- uv add djresttoolkit
63
- ````
64
-
65
- - **By using pip:**
66
-
67
- ```bash
68
- pip install djresttoolkit
69
- ````
70
-
71
- ## 📚 All API Reference
72
-
73
- ### 1. DB Seed Command — API Reference
74
-
75
- #### `Generator`
76
-
77
- ```python
78
- from djresttoolkit.dbseed.models import Generator, Gen, Field
79
- ```
80
-
81
- - `Gen`: Pre-initialized **Faker** instance for generating fake data.
82
- - `Field`: Alias for `pydantic.Field` to define seed model fields.
83
-
84
- #### Example
85
-
86
- ```python
87
- from djresttoolkit.dbseed.models import SeedModel
88
- from myapp.models import User
89
-
90
- class UserSeedModel(SeedModel):
91
- __model__ = User
92
-
93
- username: str = Field(default_factory=lambda: Gen.user_name())
94
- email: str = Field(default_factory=lambda: Gen.email())
95
- ```
96
-
97
- #### `manage.py` Command: `dbseed`
98
-
99
- Seed the database from all `dbseed` directories in installed apps.
100
-
101
- ```bash
102
- python manage.py dbseed [--count 5] [--model User] [--seed 42]
103
- ```
104
-
105
- #### Options
106
-
107
- - `--count`: Number of records per model (default: 5).
108
- - `--model`: Specific model name to seed (optional).
109
- - `--seed`: Faker seed for reproducible data (optional).
110
-
111
- #### Behavior
112
-
113
- - Auto-discovers all `dbseed` models in installed apps.
114
- - Handles ForeignKey, OneToOneField, and ManyToMany relationships.
115
- - Uses transactions to ensure safe creation of records.
116
- - Logs errors for failed instance creation but continues seeding.
117
-
118
- #### Command Example
119
-
120
- ```bash
121
- # Seed 10 records for all models
122
- python manage.py dbseed --count 10
123
-
124
- # Seed only the User model with fixed Faker seed
125
- python manage.py dbseed --model User --seed 42
126
- ```
127
-
128
- Here’s a **concise API reference** for your database flush management command for `djresttoolkit`:
129
-
130
- ---
131
-
132
- ### 2. DB Flush Command — API Reference
133
-
134
- ```python
135
- from djresttoolkit.management.commands import flush
136
- ```
137
-
138
- #### `manage.py dbflush`
139
-
140
- Command to **delete all records** from the database for all models or a specific model and **reset auto-increment IDs**.
141
-
142
- #### Usage
143
-
144
- ```bash
145
- python manage.py flush [--model ModelName] [--yes]
146
- ```
147
-
148
- #### dbflush command options
149
-
150
- - `--model`: Name of the model to flush (case-sensitive, e.g., `User`). If omitted, flushes all models.
151
- - `--yes`: Skip confirmation prompt. Without this, the command asks for confirmation before deleting.
152
-
153
- #### dbflush command behavior
154
-
155
- - Deletes all records for the specified model or all models.
156
- - Resets primary key sequences for supported databases:
157
-
158
- - PostgreSQL: `ALTER SEQUENCE ... RESTART WITH 1`
159
- - SQLite: Deletes from `sqlite_sequence` table
160
- - Others: Logs a warning (not implemented).
161
- - Uses transactions to ensure safe operations.
162
-
163
- #### dbflush command example
164
-
165
- ```bash
166
- # Flush all models with confirmation
167
- python manage.py dbflush
168
-
169
- # Flush a specific model (User) with confirmation
170
- python manage.py dbflush --model User
171
-
172
- # Flush all models without prompt
173
- python manage.py dbflush --yes
174
- ```
175
-
176
- #### Output
177
-
178
- ```bash
179
- Flushed 10 records from model "User" and reset IDs.
180
- ```
181
-
182
- or
183
-
184
- ```bash
185
- Flushed 120 records from all models and reset IDs.
186
- ```
187
-
188
- ### 3. EnvBaseSettings — API Reference
189
-
190
- ```python
191
- from djresttoolkit.envconfig import EnvBaseSettings
192
- ```
193
-
194
- #### `EnvBaseSettings`
195
-
196
- A **base settings class** for managing application configuration using:
197
-
198
- - YAML files (default `.environ.yaml`)
199
- - Environment variables (default `.env`)
200
-
201
- Supports **nested configuration** using double underscores (`__`) in environment variable names.
202
-
203
- #### Class Attributes
204
-
205
- - Attributes
206
- - `env_file`
207
- - Type: `str`
208
- - Default: `.env`
209
- - Description: Environment variable file path.
210
- - `yaml_file`
211
- - Type: `str`
212
- - Default: `.environ.yaml`
213
- - Description: YAML configuration file path.
214
- - `model_config`
215
- - Type: `SettingsConfigDict`
216
- - Description: Pydantic settings configuration (file encoding, nested delimiter).
217
-
218
- #### Methods
219
-
220
- #### `load(cls, *, env_file: str | None = None, ymal_file: str | None = None, warning: bool = True) -> EnvBaseSettings`
221
-
222
- Loads configuration from **YAML first**, then overrides with **environment variables**.
223
-
224
- #### Parameters
225
-
226
- - `env_file` — Optional custom `.env` file path.
227
- - `ymal_file` — Optional custom YAML file path.
228
- - `warning` — Emit a warning if YAML file is missing (default `True`).
229
-
230
- #### Returns
231
-
232
- - Instance of `EnvBaseSettings` (or subclass) with loaded configuration.
233
-
234
- #### Raises
235
-
236
- - `UserWarning` if YAML file not found and `warning=True`.
237
-
238
- ### Usage Example
239
-
240
- ```python
241
- from djresttoolkit.envconfig import EnvBaseSettings
242
-
243
- class EnvSettings(EnvBaseSettings["EnvSettings"]):
244
- debug: bool = False
245
- database_url: str
246
-
247
- # Load settings
248
- settings = EnvSettings.load(warning=False)
249
-
250
- print(settings.debug)
251
- print(settings.database_url)
252
- ```
253
-
254
- #### Features
255
-
256
- - Prioritizes `.env` variables over YAML.
257
- - Supports nested keys: `DATABASE__HOST` → `settings.database.host`.
258
- - Designed to be subclassed for project-specific settings.
259
-
260
- ### 4. EmailSender — API Reference
261
-
262
- ```python
263
- from djresttoolkit.mail import EmailSender, EmailContent, EmailTemplate
264
- ```
265
-
266
- ### `EmailSender`
267
-
268
- Send templated emails.
269
-
270
- #### Init
271
-
272
- ```python
273
- EmailSender(email_content: EmailContent | EmailContentDict)
274
- ```
275
-
276
- #### EmailSender Methods
277
-
278
- ```python
279
- send(to: list[str], exceptions: bool = False) -> bool
280
- ```
281
-
282
- - `to`: recipient emails
283
- - `exceptions`: raise on error if `True`, else logs error
284
- - Returns `True` if sent, `False` on failure
285
-
286
- #### Example for sending an email
287
-
288
- ```python
289
- content = EmailContent(
290
- subject="Hello",
291
- from_email="noreply@example.com",
292
- context={"username": "Alice"},
293
- template=EmailTemplate(
294
- text="emails/welcome.txt",
295
- html="emails/welcome.html"
296
- )
297
- )
298
- EmailSender(content).send(to=["user@example.com"])
299
- ```
300
-
301
- #### `EmailContent`
302
-
303
- - `subject`, `from_email`, `context`, `template` (EmailTemplate)
304
-
305
- #### `EmailTemplate`
306
-
307
- - `text`, `html` — template file paths
308
-
309
- ### 5. Custom DRF Exception Handler — API Reference
310
-
311
- ```python
312
- from djresttoolkit.views import exception_handler
313
- ```
314
-
315
- ### `exception_handler(exc: Exception, context: dict[str, Any]) -> Response | None`
316
-
317
- A DRF exception handler that:
318
-
319
- - Preserves DRF’s default exception behavior.
320
- - Adds throttling support (defaults to `AnonRateThrottle`).
321
- - Returns **429 Too Many Requests** with `retry_after` if throttle limit is exceeded.
322
-
323
- #### Exception Handler Parameters
324
-
325
- - `exc`: Exception object.
326
- - `context`: DRF context dictionary containing `"request"` and `"view"`.
327
-
328
- #### Returns Type of Exception Handler
329
-
330
- - `Response` — DRF Response object (with throttling info if applicable), or `None`.
331
-
332
- #### Settings Configuration
333
-
334
- In `settings.py`:
335
-
336
- ```python
337
- REST_FRAMEWORK = {
338
- 'EXCEPTION_HANDLER': 'djresttoolkit.views.exception_handler',
339
- # Other DRF settings...
340
- }
341
- ```
342
-
343
- #### Throttle Behavior
344
-
345
- - Uses `view.throttle_classes` if defined, else defaults to `AnonRateThrottle`.
346
- - Tracks requests in cache and calculates `retry_after`.
347
- - Cleans expired timestamps automatically.
348
-
349
- ### 6. Response Time Middleware — API Reference
350
-
351
- ```python
352
- from djresttoolkit.middlewares import ResponseTimeMiddleware
353
- ```
354
-
355
- ### `ResponseTimeMiddleware`
356
-
357
- Middleware to calculate and log **HTTP response time** for each request.
358
-
359
- #### Constructor from ResponseTimeMiddleware
360
-
361
- ```python
362
- ResponseTimeMiddleware(get_response: Callable[[HttpRequest], HttpResponse])
363
- ```
364
-
365
- - `get_response`: The next middleware or view callable.
366
-
367
- #### Response Time Middleware Usage
368
-
369
- Add it to your Django `MIDDLEWARE` in `settings.py`:
370
-
371
- ```python
372
- MIDDLEWARE = [
373
- # Other middlewares...
374
- 'djresttoolkit.middlewares.ResponseTimeMiddleware',
375
- ]
376
- ```
377
-
378
- #### Response Time Middleware Behavior
379
-
380
- - Measures the time taken to process each request.
381
- - Adds a header `X-Response-Time` to each HTTP response.
382
- - Logs the response time using Django's logging system.
383
-
384
- #### The response headers will include
385
-
386
- ```json
387
- X-Response-Time: 0.01234 seconds
388
- ```
389
-
390
- #### Logs a message
391
-
392
- ```bash
393
- INFO: Request processed in 0.01234 seconds
394
- ```
395
-
396
- ### 7. Throttle — API Reference
397
-
398
- #### `ThrottleInfoJSONRenderer`
399
-
400
- ```python
401
- from djresttoolkit.renderers import ThrottleInfoJSONRenderer
402
- ```
403
-
404
- A custom DRF JSON renderer that **automatically attaches throttle information to response headers**.
405
-
406
- #### Usage (settings.py)
407
-
408
- ```python
409
- REST_FRAMEWORK = {
410
- "DEFAULT_RENDERER_CLASSES": [
411
- "djresttoolkit.renderers.ThrottleInfoJSONRenderer",
412
- "rest_framework.renderers.BrowsableAPIRenderer",
413
- ],
414
- }
415
- ```
416
-
417
- When enabled, every response includes throttle headers like:
418
-
419
- ```plaintext
420
- X-Throttle-User-Limit: 100
421
- X-Throttle-User-Remaining: 98
422
- X-Throttle-User-Reset: 2025-08-18T07:30:00Z
423
- X-Throttle-User-Retry-After: 0
424
- ```
425
-
426
- #### `ThrottleInspector`
427
-
428
- ```python
429
- from djresttoolkit.throttling import ThrottleInspector
430
- ```
431
-
432
- Utility class to **inspect DRF throttle usage** for a view or request.
433
-
434
- #### Constructor for ThrottleInspector
435
-
436
- ```python
437
- ThrottleInspector(
438
- view: APIView,
439
- request: Request | None = None,
440
- throttle_classes: list[type[BaseThrottle]] | None = None,
441
- )
442
- ```
443
-
444
- #### Key Methods
445
-
446
- - `get_details() -> dict[str, Any]`
447
- Returns structured throttle info: limit, remaining, reset time, retry\_after.
448
-
449
- - `attach_headers(response: Response, throttle_info: dict | None)`
450
- Attaches throttle data to HTTP headers.
451
-
452
- ### 8. AbsoluteUrlFileMixin — API Reference
453
-
454
- ```python
455
- from djresttoolkit.serializers.mixins import AbsoluteUrlFileMixin
456
- ```
457
-
458
- ### `AbsoluteUrlFileMixin`
459
-
460
- A **serializer mixin** that converts **FileField** and **ImageField** URLs to **absolute URLs**, ensuring compatibility with cloud storage backends.
461
-
462
- ---
463
-
464
- ### Attributes
465
-
466
- - `file_fields`
467
- - type: `list[str] | None`
468
- - default: `None`
469
- - description: Manual list of file field names for non-model serializers.
470
-
471
- ### Absolute Url File Mixin Methods
472
-
473
- #### `to_representation(self, instance: Any) -> dict[str, Any]`
474
-
475
- - Overrides default serializer `to_representation`.
476
- - Enhances all file-related fields in the serialized output to **absolute URLs**.
477
-
478
- #### `enhance_file_fields(self, instance: Any, representation: dict[str, Any], request: Any) -> dict[str, Any]`
479
-
480
- - Core logic to process each file field.
481
- - Converts relative URLs to absolute URLs using `request.build_absolute_uri()`.
482
- - Supports model serializers or manual `file_fields`.
483
- - Logs warnings if request context is missing or file is not found.
484
-
485
- #### Exceptions
486
-
487
- - `MissingRequestContext`: Raised if the request object is missing in serializer context and `DEBUG=True`.
488
-
489
- ### Absolute Url File Mixin Example
490
-
491
- ```python
492
- from rest_framework import serializers
493
- from djresttoolkit.serializers.mixins import AbsoluteUrlFileMixin
494
- from myapp.models import Document
495
-
496
- class DocumentSerializer(AbsoluteUrlFileMixin, serializers.ModelSerializer):
497
- class Meta:
498
- model = Document
499
- fields = ["id", "title", "file"]
500
-
501
- # Output will convert `file` field to an absolute URL
502
- serializer = DocumentSerializer(instance, context={"request": request})
503
- data = serializer.data
504
- ```
505
-
506
- #### Notes
507
-
508
- - Works with both Django model serializers and custom serializers.
509
- - Relative file paths are automatically converted to absolute URLs.
510
- - Can manually specify fields via `file_fields` for non-model serializers.
511
-
512
- ### 9. BulkCreateMixin — API Reference
513
-
514
- ```python
515
- from djresttoolkit.serializers.mixins import BulkCreateMixin
516
- ```
517
-
518
- #### `BulkCreateMixin`
519
-
520
- A **DRF serializer mixin** that adds support for:
521
-
522
- - **Single instance creation** with extra context fields
523
- - **Bulk creation** from a list of validated data dictionaries
524
- - **Updating serializer field error messages** with model-specific messages
525
-
526
- #### Bulk Create Mixin Notes
527
-
528
- - `bulk_create()` does **not trigger model signals** or call `.save()` on instances.
529
- - `Meta.model` **must** be defined in the serializer.
530
-
531
- #### Bulk Create Mixin Methods
532
-
533
- #### `create(self, validated_data: dict[str, Any] | list[dict[str, Any]]) -> Model | list[Model]`
534
-
535
- - Creates single or multiple model instances.
536
- - **Parameters:**
537
- - `validated_data`: dict for single instance or list of dicts for bulk creation.
538
-
539
- - **Returns:**
540
- - Single model instance or list of instances.
541
-
542
- - **Raises:**
543
- - `AttributeError` if `Meta.model` is not defined.
544
- - `NotImplementedError` if used with a serializer that does not implement `create()`.
545
-
546
- #### `get_fields(self) -> dict[str, SerializerField]`
547
-
548
- - Extends DRF serializer `get_fields()` to update **error messages** using model field definitions.
549
- - **Returns:**
550
- - Dictionary of serializer fields.
551
-
552
- - **Warning:**
553
- - Logs a warning if a serializer field is not present on the model.
554
-
555
- ### Bulk Create Mixin Example
556
-
557
- ```python
558
- from rest_framework import serializers
559
- from djresttoolkit.serializers.mixins import BulkCreateMixin
560
- from myapp.models import Product
561
-
562
- class ProductSerializer(BulkCreateMixin, serializers.ModelSerializer):
563
- class Meta:
564
- model = Product
565
- fields = ["id", "name", "price"]
566
-
567
- # Single creation
568
- serializer = ProductSerializer(data={"name": "Item1", "price": 10})
569
- serializer.is_valid(raise_exception=True)
570
- product = serializer.save()
571
-
572
- # Bulk creation
573
- serializer = ProductSerializer(
574
- data=[
575
- {"name": "Item2", "price": 20},
576
- {"name": "Item3", "price": 30},
577
- ],
578
- many=True
579
- )
580
- serializer.is_valid(raise_exception=True)
581
- products = serializer.save()
582
- ```
583
-
584
- #### Bulk Create Mixin Features
585
-
586
- - Works seamlessly with DRF `ModelSerializer`.
587
- - Automatically updates field error messages based on Django model definitions.
588
- - Bulk creation is optimized using `model.objects.bulk_create()` for efficiency.
589
-
590
- ### 10. ModelChoiceFieldMixin — API Reference
591
-
592
- ```python
593
- from djresttoolkit.models.mixins import ModelChoiceFieldMixin
594
- ```
595
-
596
- ### `ModelChoiceFieldMixin`
597
-
598
- A **Django model mixin** to retrieve **choice fields** from a model, designed to work seamlessly with Django's `TextChoices`.
599
-
600
- #### Class Attributes in Model Choice Field Mixin
601
-
602
- - `model: type[Model] | None` — The Django model class to inspect. **Must be set.**
603
- - `choice_fields: list[str] | None` — List of model field names that contain choices. **Must be set.**
604
-
605
- #### Model Choice Field Mixin Methods
606
-
607
- - `get_choices() -> dict[str, dict[str, str]]`
608
-
609
- Retrieve the choice fields from the model as a dictionary.
610
-
611
- - **Returns:**
612
-
613
- ```python
614
- {
615
- "field_name": {
616
- "choice_value": "Choice Label",
617
- ...
618
- },
619
- ...
620
- }
621
- ```
622
-
623
- - **Raises:**
624
-
625
- - `AttributeDoesNotExist` — If `model` or `choice_fields` is not set.
626
- - `ChoiceFieldNotFound` — If a field does not exist, has no choices, or has invalid choice format.
627
-
628
- ---
629
-
630
- ### Model Choice Field Mixin Example
631
-
632
- ```python
633
- from django.db import models
634
- from djresttoolkit.serializers.mixins import ModelChoiceFieldMixin
635
-
636
- class Product(models.Model):
637
- class Status(models.TextChoices):
638
- DRAFT = "draft", "Draft"
639
- PUBLISHED = "published", "Published"
640
-
641
- status = models.CharField(max_length=20, choices=Status.choices)
642
- category = models.CharField(max_length=50, choices=[
643
- ("a", "Category A"),
644
- ("b", "Category B"),
645
- ])
646
-
647
- class ProductChoiceMixin(ModelChoiceFieldMixin):
648
- model = Product
649
- choice_fields = ["status", "category"]
650
-
651
- choices = ProductChoiceMixin.get_choices()
652
- print(choices)
653
- # Output:
654
- # {
655
- # "status": {"draft": "Draft", "published": "Published"},
656
- # "category": {"a": "Category A", "b": "Category B"}
657
- # }
658
- ```
659
-
660
- #### Features of Model Choice Field Mixin
661
-
662
- - Safely validates that fields exist and have valid choices.
663
- - Returns a ready-to-use dictionary mapping values to labels.
664
- - Ideal for DRF serializers, forms, and admin customization.
665
-
666
- Here’s a concise **docs entry** for your `ChoiceFieldsAPIView` suitable for `djresttoolkit` documentation:
667
-
668
- ---
669
-
670
- ### 11. ChoiceFieldsAPIView — API Reference
671
-
672
- ```python
673
- from djresttoolkit.views import ChoiceFieldsAPIView
674
- ```
675
-
676
- #### `ChoiceFieldsAPIView`
677
-
678
- A **generic DRF API view** to return all choices for specified model fields.
679
-
680
- #### Class Attributes of Choice Fields APIView
681
-
682
- - `model_class: type[Model] | None` — The Django model to inspect. **Must be set.**
683
- - `choice_fields: list[str] | None` — List of fields on the model with choices. **Must be set.**
684
-
685
- ---
686
-
687
- #### Choice Fields APIView Methods
688
-
689
- - `get(request: Request) -> Response`
690
-
691
- Fetches the choices for the configured model fields.
692
-
693
- - **Returns:**
694
- - `200 OK` — JSON object containing all choices:
695
-
696
- ```json
697
- {
698
- "choices": {
699
- "status": {"draft": "Draft", "published": "Published"},
700
- "category": {"a": "Category A", "b": "Category B"}
701
- }
702
- }
703
- ```
704
-
705
- - `400 Bad Request` — If any error occurs while retrieving choices.
706
-
707
- - **Raises:**
708
- - `AttributeDoesNotExist` — If `model_class` or `choice_fields` is not set.
709
-
710
- ---
711
-
712
- ### Example of Choice Fields APIView
713
-
714
- ```python
715
- from django.urls import path
716
- from djresttoolkit.views import ChoiceFieldsAPIView
717
- from myapp.models import Product
718
-
719
- class ProductChoiceAPI(ChoiceFieldsAPIView):
720
- model_class = Product
721
- choice_fields = ["status", "category"]
722
-
723
- urlpatterns = [
724
- path(
725
- "api/v1/product-choices/",
726
- ProductChoiceAPI.as_view(),
727
- name="product-choices"
728
- ),
729
- ]
730
- ```
731
-
732
- #### Choice Fields APIView Features
733
-
734
- - Dynamically returns all choices for selected fields in a model.
735
- - Useful for frontend forms or API consumers that need selectable options.
736
- - Integrates seamlessly with `ModelChoiceFieldMixin` from `djresttoolkit`.
737
-
738
- ### 12. RetrieveObjectMixin — API Reference
739
-
740
- ```python
741
- from djresttoolkit.views.mixins import RetrieveObjectMixin
742
- ```
743
-
744
- #### `RetrieveObjectMixin[T: Model]`
745
-
746
- A **generic mixin** to retrieve a single Django model instance by filters.
747
-
748
- #### Class Attributes of Retrieve Object Mixin
749
-
750
- - `queryset: QuerySet[T] | None` — The queryset used to retrieve objects. **Must be set.**
751
-
752
- #### Raises of Retrieve Object Mixin
753
-
754
- - `QuerysetNotDefinedError` — If `queryset` is not set in the class.
755
-
756
- #### Retrieve Object Mixin Methods
757
-
758
- - `get_object(**filters: Any) -> T | None`
759
-
760
- Retrieve a single model object using the provided filter criteria.
761
-
762
- - **Parameters:**
763
- - `**filters` — Keyword arguments to filter the queryset (e.g., `id=1`, `slug="abc"`).
764
-
765
- - **Returns:**
766
- - Model instance matching the filters, or `None` if no match is found.
767
-
768
- #### Example of Retrieve Object Mixin
769
-
770
- ```python
771
- from rest_framework.views import APIView
772
- from django.http import JsonResponse
773
- from myapp.models import Book
774
- from djresttoolkit.mixins import RetrieveObjectMixin
775
-
776
- class BookDetailView(RetrieveObjectMixin[Book], APIView):
777
- queryset = Book.objects.all()
778
-
779
- def get(self, request, *args, **kwargs):
780
- book = self.get_object(id=kwargs["id"])
781
- if book:
782
- return JsonResponse({"title": book.title, "author": book.author})
783
- return JsonResponse({"detail": "Not found"}, status=404)
784
- ```
785
-
786
- #### Features of Retrieve Object Mixin
787
-
788
- - Simplifies object retrieval in class-based views or DRF views.
789
- - Returns `None` instead of raising `DoesNotExist`, making error handling easier.
790
- - Works with any Django model and queryset.
791
-
792
- ### 13. build_absolute_uri — API Reference
793
-
794
- ```python
795
- from djresttoolkit.urls import build_absolute_uri
796
- ```
797
-
798
- #### build absolute uri Description
799
-
800
- - Builds a **fully qualified absolute URL** for a Django or DRF view.
801
- - Optionally includes **query parameters**.
802
- - Works with both **Django `HttpRequest`** and **DRF `Request`** objects.
803
- - Uses Django's `reverse()` to dynamically resolve URL names.
804
-
805
- #### build absolute uri Function Signature
806
-
807
- ```python
808
- def build_absolute_uri(
809
- request: HttpRequest | Request,
810
- url_name: str,
811
- query_params: dict[str, Any] | None = None,
812
- *args: Any,
813
- **kwargs: Any,
814
- ) -> str:
815
- ...
816
- ```
817
-
818
- ---
819
-
820
- #### build absolute uri Parameters
821
-
822
- - `request` (`HttpRequest | Request`): The incoming Django or DRF request object.
823
- - `url_name` (`str`): Name of the URL pattern to reverse.
824
- - `query_params` (`dict[str, Any] | None`): Optional dictionary of query parameters to append to the URL.
825
- - `*args` (`Any`): Positional arguments for the URL reversal.
826
- - `**kwargs` (`Any`): Keyword arguments for the URL reversal.
827
-
828
- ---
829
-
830
- ### build absolute uri Returns
831
-
832
- - `str`: Absolute URI of the view including optional query parameters.
833
-
834
- ### Example of build absolute uri
835
-
836
- ```python
837
- from django.http import HttpRequest
838
- from djresttoolkit.utils import build_absolute_uri
839
-
840
- def my_view(request: HttpRequest):
841
- absolute_url = build_absolute_uri(
842
- request,
843
- url_name="book-detail",
844
- query_params={"ref": "newsletter"},
845
- pk=123
846
- )
847
- return HttpResponse(f"URL: {absolute_url}")
848
- ```
849
-
850
- **Output Example:**
851
-
852
- ```url
853
- https://example.com/api/books/123/?ref=newsletter
854
- ```
855
-
856
- ### 14. PageNumberPagination — API Reference
857
-
858
- ```python
859
- from djresttoolkit.pagination import PageNumberPagination
860
- ```
861
-
862
- #### Description of Page Number Pagination
863
-
864
- - Extends **DRF’s `PageNumberPagination`** with a **cleaner metadata structure**.
865
- - Adds support for **dynamic page size** via the `page-size` query parameter.
866
- - Returns pagination metadata inside a `"page"` object, separate from `"results"`.
867
-
868
- #### Features of Page Number Pagination
869
-
870
- - Clients can control items per page using `?page-size=`.
871
- - Structured pagination metadata:
872
-
873
- - `current` → current page number
874
- - `total` → total number of pages
875
- - `size` → number of items per page
876
- - `total_items` → total number of items across all pages
877
- - `next` → next page URL
878
- - `previous` → previous page URL
879
- - Standardized API response format.
880
-
881
- ### Attributes of Page Number Pagination
882
-
883
- - `page_size_query_param: str` → Query parameter name (`"page-size"`).
884
-
885
- ### Page Number Pagination Methods
886
-
887
- - `get_paginated_response(data: Any) -> Response`
888
- Returns a JSON response with both pagination metadata and results.
889
-
890
- ### Example Response of Page Number Pagination
891
-
892
- ```json
893
- {
894
- "page": {
895
- "current": 1,
896
- "total": 10,
897
- "size": 20,
898
- "total_items": 200,
899
- "next": "http://api.example.com/items/?page=2&page-size=20",
900
- "previous": null
901
- },
902
- "results": [
903
- { "id": 1, "name": "Item 1" },
904
- { "id": 2, "name": "Item 2" }
905
- ]
906
- }
907
- ```
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
-
976
- ## 🛠️ Planned Features
977
-
978
- - Add more utils
979
-
980
- ## 🤝 Contributing
981
-
982
- Contributions are welcome! Please open an issue or PR for any improvements.
983
-
984
- ## 📜 License
985
-
986
- MIT License — See [LICENSE](LICENSE).
987
-
988
- ## 👤 Author
989
-
990
- For questions or assistance, contact **Shailesh** at [shaileshpandit141@gmail.com](mailto:shaileshpandit141@gmail.com).