djresttoolkit 0.9.0__tar.gz → 0.11.0__tar.gz
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.
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/PKG-INFO +163 -9
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/README.md +162 -8
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/pyproject.toml +1 -1
- djresttoolkit-0.11.0/src/djresttoolkit/models/mixins/__init__.py +11 -0
- djresttoolkit-0.11.0/src/djresttoolkit/models/mixins/_model_choice_fields_mixin.py +84 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/serializers/mixins/__init__.py +2 -0
- djresttoolkit-0.9.0/src/djresttoolkit/serializers/_serializer_create_mixin.py → djresttoolkit-0.11.0/src/djresttoolkit/serializers/mixins/_bulk_create_mixin.py +1 -1
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/.gitignore +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/LICENSE +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/demo/staticfiles/admin/img/LICENSE +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/admin.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/apps.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/dbseed/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/dbseed/models/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/dbseed/models/_choice_field.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/dbseed/models/_gen.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/dbseed/models/_seed_model.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/envconfig/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/envconfig/_env_settings.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/mail/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/mail/_email_sender.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/mail/_models.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/mail/_types.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/management/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/management/commands/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/management/commands/dbflush.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/management/commands/dbseed.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/middlewares/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/middlewares/_response_time_middleware.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/migrations/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/models/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/py.typed +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/renderers/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/renderers/_throttle_info_json_renderer.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/serializers/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/serializers/mixins/_absolute_url_file_mixin.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/throttling/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/throttling/_throttle_inspector.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/views/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/views/_exceptions/__init__.py +0 -0
- {djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/views/_exceptions/_exception_handler.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: djresttoolkit
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.11.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
|
@@ -88,7 +88,7 @@ djresttoolkit is a collection of utilities and helpers for Django and Django RES
|
|
88
88
|
|
89
89
|
## 📚 All API Reference
|
90
90
|
|
91
|
-
### 1. DB Seed Utilities
|
91
|
+
### 1. DB Seed Utilities — API Reference
|
92
92
|
|
93
93
|
#### `Generator`
|
94
94
|
|
@@ -147,7 +147,7 @@ Here’s a **concise API reference** for your database flush management command
|
|
147
147
|
|
148
148
|
---
|
149
149
|
|
150
|
-
### 2. DB Flush Command
|
150
|
+
### 2. DB Flush Command — API Reference
|
151
151
|
|
152
152
|
```python
|
153
153
|
from djresttoolkit.management.commands import flush
|
@@ -203,7 +203,7 @@ or
|
|
203
203
|
Flushed 120 records from all models and reset IDs.
|
204
204
|
```
|
205
205
|
|
206
|
-
### 3. EnvBaseSettings
|
206
|
+
### 3. EnvBaseSettings — API Reference
|
207
207
|
|
208
208
|
```python
|
209
209
|
from djresttoolkit.envconfig import EnvBaseSettings
|
@@ -258,7 +258,7 @@ Loads configuration from **YAML first**, then overrides with **environment varia
|
|
258
258
|
```python
|
259
259
|
from djresttoolkit.envconfig import EnvBaseSettings
|
260
260
|
|
261
|
-
class EnvSettings(EnvBaseSettings):
|
261
|
+
class EnvSettings(EnvBaseSettings["EnvSettings"]):
|
262
262
|
debug: bool = False
|
263
263
|
database_url: str
|
264
264
|
|
@@ -275,7 +275,7 @@ print(settings.database_url)
|
|
275
275
|
- Supports nested keys: `DATABASE__HOST` → `settings.database.host`.
|
276
276
|
- Designed to be subclassed for project-specific settings.
|
277
277
|
|
278
|
-
### 4. EmailSender
|
278
|
+
### 4. EmailSender — API Reference
|
279
279
|
|
280
280
|
```python
|
281
281
|
from djresttoolkit.mail import EmailSender, EmailContent, EmailTemplate
|
@@ -324,7 +324,7 @@ EmailSender(content).send(to=["user@example.com"])
|
|
324
324
|
|
325
325
|
- `text`, `html` — template file paths
|
326
326
|
|
327
|
-
### 5. Custom DRF Exception Handler
|
327
|
+
### 5. Custom DRF Exception Handler — API Reference
|
328
328
|
|
329
329
|
```python
|
330
330
|
from djresttoolkit.views import exception_handler
|
@@ -364,7 +364,7 @@ REST_FRAMEWORK = {
|
|
364
364
|
- Tracks requests in cache and calculates `retry_after`.
|
365
365
|
- Cleans expired timestamps automatically.
|
366
366
|
|
367
|
-
### 6. Response Time Middleware
|
367
|
+
### 6. Response Time Middleware — API Reference
|
368
368
|
|
369
369
|
```python
|
370
370
|
from djresttoolkit.middlewares import ResponseTimeMiddleware
|
@@ -411,7 +411,7 @@ X-Response-Time: 0.01234 seconds
|
|
411
411
|
INFO: Request processed in 0.01234 seconds
|
412
412
|
```
|
413
413
|
|
414
|
-
### 7. Throttle Utilities
|
414
|
+
### 7. Throttle Utilities — API Reference
|
415
415
|
|
416
416
|
#### `ThrottleInfoJSONRenderer`
|
417
417
|
|
@@ -527,6 +527,160 @@ data = serializer.data
|
|
527
527
|
- Relative file paths are automatically converted to absolute URLs.
|
528
528
|
- Can manually specify fields via `file_fields` for non-model serializers.
|
529
529
|
|
530
|
+
### 9. BulkCreateMixin — API Reference
|
531
|
+
|
532
|
+
```python
|
533
|
+
from djresttoolkit.serializers.mixins import BulkCreateMixin
|
534
|
+
```
|
535
|
+
|
536
|
+
#### `BulkCreateMixin`
|
537
|
+
|
538
|
+
A **DRF serializer mixin** that adds support for:
|
539
|
+
|
540
|
+
- **Single instance creation** with extra context fields
|
541
|
+
- **Bulk creation** from a list of validated data dictionaries
|
542
|
+
- **Updating serializer field error messages** with model-specific messages
|
543
|
+
|
544
|
+
#### Bulk Create Mixin Notes
|
545
|
+
|
546
|
+
- `bulk_create()` does **not trigger model signals** or call `.save()` on instances.
|
547
|
+
- `Meta.model` **must** be defined in the serializer.
|
548
|
+
|
549
|
+
#### Bulk Create Mixin Methods
|
550
|
+
|
551
|
+
#### `create(self, validated_data: dict[str, Any] | list[dict[str, Any]]) -> Model | list[Model]`
|
552
|
+
|
553
|
+
- Creates single or multiple model instances.
|
554
|
+
- **Parameters:**
|
555
|
+
- `validated_data`: dict for single instance or list of dicts for bulk creation.
|
556
|
+
|
557
|
+
- **Returns:**
|
558
|
+
- Single model instance or list of instances.
|
559
|
+
|
560
|
+
- **Raises:**
|
561
|
+
- `AttributeError` if `Meta.model` is not defined.
|
562
|
+
- `NotImplementedError` if used with a serializer that does not implement `create()`.
|
563
|
+
|
564
|
+
#### `get_fields(self) -> dict[str, SerializerField]`
|
565
|
+
|
566
|
+
- Extends DRF serializer `get_fields()` to update **error messages** using model field definitions.
|
567
|
+
- **Returns:**
|
568
|
+
- Dictionary of serializer fields.
|
569
|
+
|
570
|
+
- **Warning:**
|
571
|
+
- Logs a warning if a serializer field is not present on the model.
|
572
|
+
|
573
|
+
### Bulk Create Mixin Example
|
574
|
+
|
575
|
+
```python
|
576
|
+
from rest_framework import serializers
|
577
|
+
from djresttoolkit.serializers.mixins import BulkCreateMixin
|
578
|
+
from myapp.models import Product
|
579
|
+
|
580
|
+
class ProductSerializer(BulkCreateMixin, serializers.ModelSerializer):
|
581
|
+
class Meta:
|
582
|
+
model = Product
|
583
|
+
fields = ["id", "name", "price"]
|
584
|
+
|
585
|
+
# Single creation
|
586
|
+
serializer = ProductSerializer(data={"name": "Item1", "price": 10})
|
587
|
+
serializer.is_valid(raise_exception=True)
|
588
|
+
product = serializer.save()
|
589
|
+
|
590
|
+
# Bulk creation
|
591
|
+
serializer = ProductSerializer(
|
592
|
+
data=[
|
593
|
+
{"name": "Item2", "price": 20},
|
594
|
+
{"name": "Item3", "price": 30},
|
595
|
+
],
|
596
|
+
many=True
|
597
|
+
)
|
598
|
+
serializer.is_valid(raise_exception=True)
|
599
|
+
products = serializer.save()
|
600
|
+
```
|
601
|
+
|
602
|
+
#### Bulk Create Mixin Features
|
603
|
+
|
604
|
+
- Works seamlessly with DRF `ModelSerializer`.
|
605
|
+
- Automatically updates field error messages based on Django model definitions.
|
606
|
+
- Bulk creation is optimized using `model.objects.bulk_create()` for efficiency.
|
607
|
+
|
608
|
+
### 10. ModelChoiceFieldMixin — API Reference
|
609
|
+
|
610
|
+
```python
|
611
|
+
from djresttoolkit.models.mixins import ModelChoiceFieldMixin
|
612
|
+
```
|
613
|
+
|
614
|
+
### `ModelChoiceFieldMixin`
|
615
|
+
|
616
|
+
A **Django model mixin** to retrieve **choice fields** from a model, designed to work seamlessly with Django's `TextChoices`.
|
617
|
+
|
618
|
+
#### Class Attributes in Model Choice Field Mixin
|
619
|
+
|
620
|
+
- `model: type[Model] | None` — The Django model class to inspect. **Must be set.**
|
621
|
+
- `choice_fields: list[str] | None` — List of model field names that contain choices. **Must be set.**
|
622
|
+
|
623
|
+
#### Model Choice Field Mixin Methods
|
624
|
+
|
625
|
+
`get_choices() -> dict[str, dict[str, str]]`
|
626
|
+
|
627
|
+
Retrieve the choice fields from the model as a dictionary.
|
628
|
+
|
629
|
+
- **Returns:**
|
630
|
+
|
631
|
+
```python
|
632
|
+
{
|
633
|
+
"field_name": {
|
634
|
+
"choice_value": "Choice Label",
|
635
|
+
...
|
636
|
+
},
|
637
|
+
...
|
638
|
+
}
|
639
|
+
```
|
640
|
+
|
641
|
+
- **Raises:**
|
642
|
+
|
643
|
+
- `AttributeDoesNotExist` — If `model` or `choice_fields` is not set.
|
644
|
+
- `ChoiceFieldNotFound` — If a field does not exist, has no choices, or has invalid choice format.
|
645
|
+
|
646
|
+
---
|
647
|
+
|
648
|
+
### Model Choice Field Mixin Example
|
649
|
+
|
650
|
+
```python
|
651
|
+
from django.db import models
|
652
|
+
from djresttoolkit.serializers.mixins import ModelChoiceFieldMixin
|
653
|
+
|
654
|
+
class Product(models.Model):
|
655
|
+
class Status(models.TextChoices):
|
656
|
+
DRAFT = "draft", "Draft"
|
657
|
+
PUBLISHED = "published", "Published"
|
658
|
+
|
659
|
+
status = models.CharField(max_length=20, choices=Status.choices)
|
660
|
+
category = models.CharField(max_length=50, choices=[
|
661
|
+
("a", "Category A"),
|
662
|
+
("b", "Category B"),
|
663
|
+
])
|
664
|
+
|
665
|
+
class ProductChoiceMixin(ModelChoiceFieldMixin):
|
666
|
+
model = Product
|
667
|
+
choice_fields = ["status", "category"]
|
668
|
+
|
669
|
+
choices = ProductChoiceMixin.get_choices()
|
670
|
+
print(choices)
|
671
|
+
# Output:
|
672
|
+
# {
|
673
|
+
# "status": {"draft": "Draft", "published": "Published"},
|
674
|
+
# "category": {"a": "Category A", "b": "Category B"}
|
675
|
+
# }
|
676
|
+
```
|
677
|
+
|
678
|
+
#### Features of Model Choice Field Mixin
|
679
|
+
|
680
|
+
- Safely validates that fields exist and have valid choices.
|
681
|
+
- Returns a ready-to-use dictionary mapping values to labels.
|
682
|
+
- Ideal for DRF serializers, forms, and admin customization.
|
683
|
+
|
530
684
|
## 🛠️ Planned Features
|
531
685
|
|
532
686
|
- Add more utils
|
@@ -30,7 +30,7 @@ djresttoolkit is a collection of utilities and helpers for Django and Django RES
|
|
30
30
|
|
31
31
|
## 📚 All API Reference
|
32
32
|
|
33
|
-
### 1. DB Seed Utilities
|
33
|
+
### 1. DB Seed Utilities — API Reference
|
34
34
|
|
35
35
|
#### `Generator`
|
36
36
|
|
@@ -89,7 +89,7 @@ Here’s a **concise API reference** for your database flush management command
|
|
89
89
|
|
90
90
|
---
|
91
91
|
|
92
|
-
### 2. DB Flush Command
|
92
|
+
### 2. DB Flush Command — API Reference
|
93
93
|
|
94
94
|
```python
|
95
95
|
from djresttoolkit.management.commands import flush
|
@@ -145,7 +145,7 @@ or
|
|
145
145
|
Flushed 120 records from all models and reset IDs.
|
146
146
|
```
|
147
147
|
|
148
|
-
### 3. EnvBaseSettings
|
148
|
+
### 3. EnvBaseSettings — API Reference
|
149
149
|
|
150
150
|
```python
|
151
151
|
from djresttoolkit.envconfig import EnvBaseSettings
|
@@ -200,7 +200,7 @@ Loads configuration from **YAML first**, then overrides with **environment varia
|
|
200
200
|
```python
|
201
201
|
from djresttoolkit.envconfig import EnvBaseSettings
|
202
202
|
|
203
|
-
class EnvSettings(EnvBaseSettings):
|
203
|
+
class EnvSettings(EnvBaseSettings["EnvSettings"]):
|
204
204
|
debug: bool = False
|
205
205
|
database_url: str
|
206
206
|
|
@@ -217,7 +217,7 @@ print(settings.database_url)
|
|
217
217
|
- Supports nested keys: `DATABASE__HOST` → `settings.database.host`.
|
218
218
|
- Designed to be subclassed for project-specific settings.
|
219
219
|
|
220
|
-
### 4. EmailSender
|
220
|
+
### 4. EmailSender — API Reference
|
221
221
|
|
222
222
|
```python
|
223
223
|
from djresttoolkit.mail import EmailSender, EmailContent, EmailTemplate
|
@@ -266,7 +266,7 @@ EmailSender(content).send(to=["user@example.com"])
|
|
266
266
|
|
267
267
|
- `text`, `html` — template file paths
|
268
268
|
|
269
|
-
### 5. Custom DRF Exception Handler
|
269
|
+
### 5. Custom DRF Exception Handler — API Reference
|
270
270
|
|
271
271
|
```python
|
272
272
|
from djresttoolkit.views import exception_handler
|
@@ -306,7 +306,7 @@ REST_FRAMEWORK = {
|
|
306
306
|
- Tracks requests in cache and calculates `retry_after`.
|
307
307
|
- Cleans expired timestamps automatically.
|
308
308
|
|
309
|
-
### 6. Response Time Middleware
|
309
|
+
### 6. Response Time Middleware — API Reference
|
310
310
|
|
311
311
|
```python
|
312
312
|
from djresttoolkit.middlewares import ResponseTimeMiddleware
|
@@ -353,7 +353,7 @@ X-Response-Time: 0.01234 seconds
|
|
353
353
|
INFO: Request processed in 0.01234 seconds
|
354
354
|
```
|
355
355
|
|
356
|
-
### 7. Throttle Utilities
|
356
|
+
### 7. Throttle Utilities — API Reference
|
357
357
|
|
358
358
|
#### `ThrottleInfoJSONRenderer`
|
359
359
|
|
@@ -469,6 +469,160 @@ data = serializer.data
|
|
469
469
|
- Relative file paths are automatically converted to absolute URLs.
|
470
470
|
- Can manually specify fields via `file_fields` for non-model serializers.
|
471
471
|
|
472
|
+
### 9. BulkCreateMixin — API Reference
|
473
|
+
|
474
|
+
```python
|
475
|
+
from djresttoolkit.serializers.mixins import BulkCreateMixin
|
476
|
+
```
|
477
|
+
|
478
|
+
#### `BulkCreateMixin`
|
479
|
+
|
480
|
+
A **DRF serializer mixin** that adds support for:
|
481
|
+
|
482
|
+
- **Single instance creation** with extra context fields
|
483
|
+
- **Bulk creation** from a list of validated data dictionaries
|
484
|
+
- **Updating serializer field error messages** with model-specific messages
|
485
|
+
|
486
|
+
#### Bulk Create Mixin Notes
|
487
|
+
|
488
|
+
- `bulk_create()` does **not trigger model signals** or call `.save()` on instances.
|
489
|
+
- `Meta.model` **must** be defined in the serializer.
|
490
|
+
|
491
|
+
#### Bulk Create Mixin Methods
|
492
|
+
|
493
|
+
#### `create(self, validated_data: dict[str, Any] | list[dict[str, Any]]) -> Model | list[Model]`
|
494
|
+
|
495
|
+
- Creates single or multiple model instances.
|
496
|
+
- **Parameters:**
|
497
|
+
- `validated_data`: dict for single instance or list of dicts for bulk creation.
|
498
|
+
|
499
|
+
- **Returns:**
|
500
|
+
- Single model instance or list of instances.
|
501
|
+
|
502
|
+
- **Raises:**
|
503
|
+
- `AttributeError` if `Meta.model` is not defined.
|
504
|
+
- `NotImplementedError` if used with a serializer that does not implement `create()`.
|
505
|
+
|
506
|
+
#### `get_fields(self) -> dict[str, SerializerField]`
|
507
|
+
|
508
|
+
- Extends DRF serializer `get_fields()` to update **error messages** using model field definitions.
|
509
|
+
- **Returns:**
|
510
|
+
- Dictionary of serializer fields.
|
511
|
+
|
512
|
+
- **Warning:**
|
513
|
+
- Logs a warning if a serializer field is not present on the model.
|
514
|
+
|
515
|
+
### Bulk Create Mixin Example
|
516
|
+
|
517
|
+
```python
|
518
|
+
from rest_framework import serializers
|
519
|
+
from djresttoolkit.serializers.mixins import BulkCreateMixin
|
520
|
+
from myapp.models import Product
|
521
|
+
|
522
|
+
class ProductSerializer(BulkCreateMixin, serializers.ModelSerializer):
|
523
|
+
class Meta:
|
524
|
+
model = Product
|
525
|
+
fields = ["id", "name", "price"]
|
526
|
+
|
527
|
+
# Single creation
|
528
|
+
serializer = ProductSerializer(data={"name": "Item1", "price": 10})
|
529
|
+
serializer.is_valid(raise_exception=True)
|
530
|
+
product = serializer.save()
|
531
|
+
|
532
|
+
# Bulk creation
|
533
|
+
serializer = ProductSerializer(
|
534
|
+
data=[
|
535
|
+
{"name": "Item2", "price": 20},
|
536
|
+
{"name": "Item3", "price": 30},
|
537
|
+
],
|
538
|
+
many=True
|
539
|
+
)
|
540
|
+
serializer.is_valid(raise_exception=True)
|
541
|
+
products = serializer.save()
|
542
|
+
```
|
543
|
+
|
544
|
+
#### Bulk Create Mixin Features
|
545
|
+
|
546
|
+
- Works seamlessly with DRF `ModelSerializer`.
|
547
|
+
- Automatically updates field error messages based on Django model definitions.
|
548
|
+
- Bulk creation is optimized using `model.objects.bulk_create()` for efficiency.
|
549
|
+
|
550
|
+
### 10. ModelChoiceFieldMixin — API Reference
|
551
|
+
|
552
|
+
```python
|
553
|
+
from djresttoolkit.models.mixins import ModelChoiceFieldMixin
|
554
|
+
```
|
555
|
+
|
556
|
+
### `ModelChoiceFieldMixin`
|
557
|
+
|
558
|
+
A **Django model mixin** to retrieve **choice fields** from a model, designed to work seamlessly with Django's `TextChoices`.
|
559
|
+
|
560
|
+
#### Class Attributes in Model Choice Field Mixin
|
561
|
+
|
562
|
+
- `model: type[Model] | None` — The Django model class to inspect. **Must be set.**
|
563
|
+
- `choice_fields: list[str] | None` — List of model field names that contain choices. **Must be set.**
|
564
|
+
|
565
|
+
#### Model Choice Field Mixin Methods
|
566
|
+
|
567
|
+
`get_choices() -> dict[str, dict[str, str]]`
|
568
|
+
|
569
|
+
Retrieve the choice fields from the model as a dictionary.
|
570
|
+
|
571
|
+
- **Returns:**
|
572
|
+
|
573
|
+
```python
|
574
|
+
{
|
575
|
+
"field_name": {
|
576
|
+
"choice_value": "Choice Label",
|
577
|
+
...
|
578
|
+
},
|
579
|
+
...
|
580
|
+
}
|
581
|
+
```
|
582
|
+
|
583
|
+
- **Raises:**
|
584
|
+
|
585
|
+
- `AttributeDoesNotExist` — If `model` or `choice_fields` is not set.
|
586
|
+
- `ChoiceFieldNotFound` — If a field does not exist, has no choices, or has invalid choice format.
|
587
|
+
|
588
|
+
---
|
589
|
+
|
590
|
+
### Model Choice Field Mixin Example
|
591
|
+
|
592
|
+
```python
|
593
|
+
from django.db import models
|
594
|
+
from djresttoolkit.serializers.mixins import ModelChoiceFieldMixin
|
595
|
+
|
596
|
+
class Product(models.Model):
|
597
|
+
class Status(models.TextChoices):
|
598
|
+
DRAFT = "draft", "Draft"
|
599
|
+
PUBLISHED = "published", "Published"
|
600
|
+
|
601
|
+
status = models.CharField(max_length=20, choices=Status.choices)
|
602
|
+
category = models.CharField(max_length=50, choices=[
|
603
|
+
("a", "Category A"),
|
604
|
+
("b", "Category B"),
|
605
|
+
])
|
606
|
+
|
607
|
+
class ProductChoiceMixin(ModelChoiceFieldMixin):
|
608
|
+
model = Product
|
609
|
+
choice_fields = ["status", "category"]
|
610
|
+
|
611
|
+
choices = ProductChoiceMixin.get_choices()
|
612
|
+
print(choices)
|
613
|
+
# Output:
|
614
|
+
# {
|
615
|
+
# "status": {"draft": "Draft", "published": "Published"},
|
616
|
+
# "category": {"a": "Category A", "b": "Category B"}
|
617
|
+
# }
|
618
|
+
```
|
619
|
+
|
620
|
+
#### Features of Model Choice Field Mixin
|
621
|
+
|
622
|
+
- Safely validates that fields exist and have valid choices.
|
623
|
+
- Returns a ready-to-use dictionary mapping values to labels.
|
624
|
+
- Ideal for DRF serializers, forms, and admin customization.
|
625
|
+
|
472
626
|
## 🛠️ Planned Features
|
473
627
|
|
474
628
|
- Add more utils
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "djresttoolkit"
|
7
|
-
version = "0.
|
7
|
+
version = "0.11.0"
|
8
8
|
description = "A collection of Django and DRF utilities to simplify API development."
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
10
10
|
license = { file = "LICENSE" }
|
@@ -0,0 +1,84 @@
|
|
1
|
+
from typing import Iterable, Tuple, cast
|
2
|
+
|
3
|
+
from django.db.models import Model
|
4
|
+
from django.core.exceptions import FieldDoesNotExist
|
5
|
+
|
6
|
+
|
7
|
+
class AttributeDoesNotExist(Exception):
|
8
|
+
"""
|
9
|
+
Exception raised when a required attribute is missing in the class.
|
10
|
+
"""
|
11
|
+
|
12
|
+
|
13
|
+
class ChoiceFieldNotFound(Exception):
|
14
|
+
"""
|
15
|
+
Exception raised when a specified choice field is missing
|
16
|
+
or has invalid/empty choices in the model.
|
17
|
+
"""
|
18
|
+
|
19
|
+
|
20
|
+
class ModelChoiceFieldMixin:
|
21
|
+
"""
|
22
|
+
Mixin to retrieve choice fields from a Django model.
|
23
|
+
Designed to work seamlessly with Django's TextChoices.
|
24
|
+
"""
|
25
|
+
|
26
|
+
model: type[Model] | None = None
|
27
|
+
choice_fields: list[str] | None = None
|
28
|
+
|
29
|
+
@classmethod
|
30
|
+
def get_choices(cls) -> dict[str, dict[str, str]]:
|
31
|
+
"""
|
32
|
+
Retrieve the choice fields from the model class.
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
dict[str, dict[str, str]]: A dictionary where keys are field names
|
36
|
+
and values are dictionaries of choices (value => label).
|
37
|
+
|
38
|
+
Raises:
|
39
|
+
ModelAttributeNotFound: If the model attribute is not set.
|
40
|
+
ChoiceFieldAttributeNotFound: If the choice_fields attribute is not set.
|
41
|
+
ChoiceFieldNotFound: If a field does not exist, has no choices,
|
42
|
+
or has an invalid choice format.
|
43
|
+
"""
|
44
|
+
|
45
|
+
if cls.model is None:
|
46
|
+
raise AttributeDoesNotExist("Model attribute is not set in the class.")
|
47
|
+
|
48
|
+
if cls.choice_fields is None:
|
49
|
+
raise AttributeDoesNotExist(
|
50
|
+
"The choice_fields attribute must be set in the class."
|
51
|
+
)
|
52
|
+
|
53
|
+
choices_as_dict: dict[str, dict[str, str]] = {}
|
54
|
+
|
55
|
+
for field in cls.choice_fields:
|
56
|
+
try:
|
57
|
+
field_obj = cls.model._meta.get_field(field) # type: ignore[attr-defined]
|
58
|
+
except FieldDoesNotExist as e:
|
59
|
+
raise ChoiceFieldNotFound(
|
60
|
+
f"The field '{field}' does not exist in model '{cls.model.__name__}'."
|
61
|
+
) from e
|
62
|
+
|
63
|
+
raw_choices = cast(
|
64
|
+
Iterable[Tuple[str, str]],
|
65
|
+
field_obj.choices or [], # type: ignore[union-attr]
|
66
|
+
)
|
67
|
+
|
68
|
+
if not raw_choices:
|
69
|
+
raise ChoiceFieldNotFound(
|
70
|
+
f"The field '{field}' in model '{cls.model.__name__}' has no choices defined."
|
71
|
+
)
|
72
|
+
|
73
|
+
if not all(
|
74
|
+
isinstance(choice, (list, tuple)) and len(choice) == 2 # type: ignore[misc]
|
75
|
+
for choice in raw_choices
|
76
|
+
):
|
77
|
+
raise ChoiceFieldNotFound(
|
78
|
+
f"The field '{field}' in model '{cls.model.__name__}' has invalid choice format. "
|
79
|
+
"Expected an iterable of 2-tuples (value, label)."
|
80
|
+
)
|
81
|
+
|
82
|
+
choices_as_dict[field] = dict(raw_choices)
|
83
|
+
|
84
|
+
return choices_as_dict
|
@@ -10,7 +10,7 @@ from django.db.models import Field as ModelField
|
|
10
10
|
logger = logging.getLogger(__name__)
|
11
11
|
|
12
12
|
|
13
|
-
class
|
13
|
+
class BulkCreateMixin:
|
14
14
|
"""
|
15
15
|
A mixin for DRF serializers that supports:
|
16
16
|
- Single instance creation with extra context fields
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/dbseed/models/_choice_field.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/management/commands/__init__.py
RENAMED
File without changes
|
{djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/management/commands/dbflush.py
RENAMED
File without changes
|
{djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/management/commands/dbseed.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/throttling/_throttle_inspector.py
RENAMED
File without changes
|
File without changes
|
{djresttoolkit-0.9.0 → djresttoolkit-0.11.0}/src/djresttoolkit/views/_exceptions/__init__.py
RENAMED
File without changes
|
File without changes
|