# Django Bulk DRF
High-performance bulk operations for Django REST Framework with a clean, RESTful API design.
**Note:** This package provides bulk operations through standard REST endpoints - no separate bulk endpoints needed. All collection-level operations are bulk by default with automatic detection of single vs. batch requests.
## Installation
```bash
pip install django-bulk-drf
```
### Requirements
- Python 3.11+
- Django 4.1+ (for native upsert support)
- Django REST Framework 3.14+
## Quick Setup
1. Add to your `INSTALLED_APPS`:
```python
INSTALLED_APPS = [
# ... your other apps
'rest_framework',
'django_bulk_drf',
]
```
2. (Optional) Configure bulk operations in your Django settings:
```python
BULK_DRF = {
'DEFAULT_BATCH_SIZE': 1000,
'MAX_BATCH_SIZE': 5000,
'ATOMIC_OPERATIONS': True,
'ENABLE_M2M_HANDLING': True,
# ... other settings
}
```
## Overview
This package extends Django REST Framework with high-performance bulk operations that work through your existing REST endpoints. No URL changes required!
### Key Features
1. **Bulk by Default**: Collection endpoints handle both single and bulk operations automatically
2. **Performance Optimized**: Single-query fetches, batch processing, query optimization
3. **Transaction Safety**: Atomic operations with configurable failure strategies
4. **Clean API Design**: No separate bulk endpoints - uses standard REST URLs
5. **DRF Integration**: Works seamlessly with existing DRF patterns
## Package Philosophy
This package provides a modern approach to bulk operations:
1. **Clean URLs**: Enhances existing endpoints rather than creating parallel ones
2. **Performance First**: Optimized database operations for maximum speed
3. **DRF Native**: Uses standard DRF patterns and integrates seamlessly
4. **Production Ready**: Built-in monitoring, error handling, and transaction management
## Usage
### Basic Setup
```python
# serializers.py
from django_bulk_drf import BulkModelSerializer
class ProductSerializer(BulkModelSerializer):
class Meta:
model = Product
fields = ['id', 'sku', 'name', 'price', 'category']
# views.py
from django_bulk_drf import BulkModelViewSet
class ProductViewSet(BulkModelViewSet):
"""
ViewSet with bulk operations on collection endpoints.
Collection endpoints (POST, PUT, PATCH, DELETE) handle bulk automatically.
Detail endpoints (/products/{id}/) work as standard DRF.
"""
queryset = Product.objects.all()
serializer_class = ProductSerializer
unique_fields = ['sku'] # For upsert operations
# urls.py
from django_bulk_drf import BulkRouter
from rest_framework.routers import DefaultRouter
# Option 1: Use BulkRouter (Recommended for bulk operations)
router = BulkRouter()
router.register('products', ProductViewSet)
# Option 2: Use standard DefaultRouter (only POST will work for bulk)
# router = DefaultRouter()
# router.register('products', ProductViewSet)
urlpatterns = router.urls
```
**Important:** To enable PATCH, PUT, and DELETE on collection endpoints (`/products/`), you must use `BulkRouter` or `BulkSimpleRouter` instead of DRF's standard routers. The standard DRF routers only map these methods to detail endpoints (`/products/{id}/`).
## API Design
### URL Structure
```
Collection Endpoints (Bulk by Default):
POST /products/ → Bulk create (accepts [...] or {...})
PUT /products/ → Bulk update (requires unique_fields)
PATCH /products/ → Bulk upsert (create or update)
DELETE /products/ → Bulk delete (by unique_fields)
Detail Endpoints (Standard DRF):
GET /products/{id}/ → Retrieve single
PUT /products/{id}/ → Update single
PATCH /products/{id}/ → Partial update single
DELETE /products/{id}/ → Delete single
```
**Key Insight**: The presence of `self.kwargs.get(self.lookup_field)` determines single vs. bulk operation.
### Request/Response Examples
#### Bulk Create
```bash
# Single create (backward compatible)
POST /products/
{
"sku": "PROD-001",
"name": "Widget",
"price": "19.99",
"category": 1
}
# Bulk create
POST /products/
[
{
"sku": "PROD-001",
"name": "Widget",
"price": "19.99",
"category": 1
},
{
"sku": "PROD-002",
"name": "Gadget",
"price": "29.99",
"category": 1
}
]
# Response
{
"created": 2,
"updated": 0,
"failed": 0,
"data": [...]
}
```
#### Bulk Upsert
```bash
# Bulk upsert (create or update)
PATCH /products/
[
{
"sku": "PROD-001", # Exists - will update
"name": "Updated Widget",
"price": "24.99"
},
{
"sku": "PROD-003", # New - will create
"name": "New Product",
"price": "39.99",
"category": 2
}
]
# Response
{
"created": 1,
"updated": 1,
"failed": 0,
"data": [...]
}
```
#### Bulk Delete
```bash
# Bulk delete by unique fields
DELETE /products/
[
{"sku": "PROD-001"},
{"sku": "PROD-002"},
{"id": 5}
]
# Response
{
"deleted": 3
}
```
#### Single Operations (Backward Compatible)
```bash
# Single create still works
POST /products/
{
"sku": "PROD-004",
"name": "Single Product",
"price": "49.99"
}
# Response (standard DRF format)
{
"id": 4,
"sku": "PROD-004",
"name": "Single Product",
"price": "49.99"
}
```
## Configuration
### Basic Settings
```python
# settings.py
BULK_DRF = {
'DEFAULT_BATCH_SIZE': 1000, # Records per database batch
'MAX_BATCH_SIZE': 5000, # Maximum request size
'ATOMIC_OPERATIONS': True, # Wrap operations in transactions
'ENABLE_M2M_HANDLING': True, # Handle many-to-many relationships
'ALLOW_SINGULAR': True, # Allow single-object requests
'PREFER_MINIMAL_RESPONSE': False, # Return minimal response format
'PARTIAL_FAILURE_STRATEGY': 'ROLLBACK_ALL', # How to handle partial failures
'ENABLE_PERFORMANCE_MONITORING': False, # Track performance metrics
'AUTO_OPTIMIZE_QUERIES': True, # Auto-optimize database queries
}
```
### Per-ViewSet Configuration
```python
class ProductViewSet(BulkModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
unique_fields = ['sku'] # Fields for upsert matching
batch_size = 500 # Override default batch size
def get_unique_fields(self):
"""Dynamic unique fields based on request"""
if self.request.query_params.get('match_by') == 'name':
return ['name', 'category']
return self.unique_fields
```
## Advanced Features
### Foreign Key Handling
The package automatically handles FK relationships with support for:
- **Integer PKs**: `{"category": 1}` → `{"category_id": 1}`
- **Slug Fields**: `{"category": "electronics"}` → resolves slug to ID in batch
### Many-to-Many Relationships
M2M fields are automatically handled:
```python
# Request
{
"name": "Product",
"tags": [1, 2, 3] # M2M field
}
# Automatically creates Product and sets M2M relationships
```
### Error Handling
#### Validation Errors
```python
# Request with errors
POST /products/
[
{"sku": "PROD-001", "name": "Valid Product"},
{"sku": "", "name": "Invalid Product"}, # Missing required field
{"sku": "PROD-003", "price": "invalid"} # Invalid data type
]
# Response
{
"created": 1,
"updated": 0,
"failed": 2,
"errors": {
"1": {"sku": ["This field may not be blank."]},
"2": {"price": ["A valid number is required."]}
}
}
```
#### Partial Failures
With `PARTIAL_FAILURE_STRATEGY = 'COMMIT_SUCCESSFUL'`:
```python
# Response when some items fail
{
"created": 1,
"updated": 0,
"failed": 2,
"data": [...], # Only successful items
"errors": {
"1": {"field": ["error"]},
"2": {"field": ["error"]}
}
}
```
## Performance Characteristics
### Bulk Create
- **Single INSERT query** per batch (default 1000 records)
- **Typical Speed**: 10,000 records/second
- **Memory**: O(batch_size) instances in memory
### Bulk Update/Upsert
- **Bulk Update**: Single SELECT query + single UPDATE query per batch
- Typical Speed: 7,000-8,000 records/second
- **Bulk Upsert** (Native): Single native upsert query (no SELECT needed)
- Uses Django's `bulk_create` with `update_conflicts=True`
- Typical Speed: 10,000-12,000 records/second
- True database-level atomicity
### Bulk Delete
- **Single DELETE query** with OR conditions
- **Typical Speed**: 15,000 records/second
### Native Upsert Performance
Bulk upsert operations use Django's native `bulk_create` with `update_conflicts=True` for maximum performance:
**How It Works:**
```python
# Single database operation handles both creates and updates
PATCH /products/
[
{"sku": "PROD-001", "name": "Updated", "price": "24.99"}, # Exists - updates
{"sku": "PROD-003", "name": "New Product", "price": "39.99"} # New - creates
]
# Response
{
"created": 2, # Native upsert doesn't distinguish created vs updated
"updated": 0,
"failed": 0,
"data": [...]
}
```
**Benefits:**
- **Single Database Query**: No SELECT needed - the database handles conflict detection
- **True Atomicity**: Database-level atomic operation (no race conditions)
- **Better Performance**: 10,000-12,000 records/second vs 7,000-8,000 for traditional update
- **Simpler Code**: One code path, easier to maintain
**Database Support:**
- PostgreSQL (all versions)
- SQLite 3.24+
- MySQL 8.0.19+
- Oracle (with limitations)
### Performance Tips
#### 1. Minimal Responses for Large Operations
```python
# Use Prefer header for large datasets
headers = {'Prefer': 'return=minimal'}
response = requests.post('/products/', json=large_dataset, headers=headers)
# Response: {"created": 10000, "updated": 0, "failed": 0}
```
#### 2. Optimize Batch Sizes
```python
# For smaller records (few fields)
BULK_DRF = {'DEFAULT_BATCH_SIZE': 2000}
# For larger records (many fields, large text)
BULK_DRF = {'DEFAULT_BATCH_SIZE': 500}
```
#### 3. Database Indexing
Ensure your `unique_fields` are properly indexed for optimal upsert performance.
## Migration Guide
### From Standard DRF
```python
# Before
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# After
class ProductViewSet(BulkModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
unique_fields = ['sku'] # Add for upsert support
```
### From drf-bulk
```python
# Before (drf-bulk)
class ProductViewSet(BulkModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# API: POST /products/bulk/
# After (django-bulk-drf)
class ProductViewSet(BulkModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
unique_fields = ['sku']
# API: POST /products/ (cleaner URLs)
```
## Limitations
- **Django Version**: Requires Django 4.1+ for native upsert support
- **Database Support**: PostgreSQL, MySQL 8.0.19+, SQLite 3.24+, Oracle (with limitations)
- **Not Supported**: Nested serializers, file uploads in bulk, complex validations requiring per-object database queries
- **Upsert Response**: Native upsert doesn't distinguish between created and updated records (all count as "created")
- **Best For**: Simple to medium complexity models with standard field types
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License.
Raw data
{
"_id": null,
"home_page": "https://github.com/AugendLimited/django-bulk-drf",
"name": "django-bulk-drf",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.11",
"maintainer_email": null,
"keywords": "django, drf, rest-framework, upsert, sync, operations, bulk",
"author": "Konrad Beck",
"author_email": "konrad.beck@merchantcapital.co.za",
"download_url": "https://files.pythonhosted.org/packages/ff/2f/e6ae46c5d19bef3a00107ee397939a9af1500d02fd9e967dad3082207ccc/django_bulk_drf-0.2.46.tar.gz",
"platform": null,
"description": "# Django Bulk DRF\n\nHigh-performance bulk operations for Django REST Framework with a clean, RESTful API design.\n\n**Note:** This package provides bulk operations through standard REST endpoints - no separate bulk endpoints needed. All collection-level operations are bulk by default with automatic detection of single vs. batch requests.\n\n## Installation\n\n```bash\npip install django-bulk-drf\n```\n\n### Requirements\n\n- Python 3.11+\n- Django 4.1+ (for native upsert support)\n- Django REST Framework 3.14+\n\n## Quick Setup\n\n1. Add to your `INSTALLED_APPS`:\n```python\nINSTALLED_APPS = [\n # ... your other apps\n 'rest_framework',\n 'django_bulk_drf',\n]\n```\n\n2. (Optional) Configure bulk operations in your Django settings:\n```python\nBULK_DRF = {\n 'DEFAULT_BATCH_SIZE': 1000,\n 'MAX_BATCH_SIZE': 5000,\n 'ATOMIC_OPERATIONS': True,\n 'ENABLE_M2M_HANDLING': True,\n # ... other settings\n}\n```\n\n## Overview\n\nThis package extends Django REST Framework with high-performance bulk operations that work through your existing REST endpoints. No URL changes required!\n\n### Key Features\n\n1. **Bulk by Default**: Collection endpoints handle both single and bulk operations automatically\n2. **Performance Optimized**: Single-query fetches, batch processing, query optimization\n3. **Transaction Safety**: Atomic operations with configurable failure strategies\n4. **Clean API Design**: No separate bulk endpoints - uses standard REST URLs\n5. **DRF Integration**: Works seamlessly with existing DRF patterns\n\n## Package Philosophy\n\nThis package provides a modern approach to bulk operations:\n\n1. **Clean URLs**: Enhances existing endpoints rather than creating parallel ones\n2. **Performance First**: Optimized database operations for maximum speed\n3. **DRF Native**: Uses standard DRF patterns and integrates seamlessly\n4. **Production Ready**: Built-in monitoring, error handling, and transaction management\n\n## Usage\n\n### Basic Setup\n\n```python\n# serializers.py\nfrom django_bulk_drf import BulkModelSerializer\n\nclass ProductSerializer(BulkModelSerializer):\n class Meta:\n model = Product\n fields = ['id', 'sku', 'name', 'price', 'category']\n\n# views.py\nfrom django_bulk_drf import BulkModelViewSet\n\nclass ProductViewSet(BulkModelViewSet):\n \"\"\"\n ViewSet with bulk operations on collection endpoints.\n \n Collection endpoints (POST, PUT, PATCH, DELETE) handle bulk automatically.\n Detail endpoints (/products/{id}/) work as standard DRF.\n \"\"\"\n queryset = Product.objects.all()\n serializer_class = ProductSerializer\n unique_fields = ['sku'] # For upsert operations\n\n# urls.py\nfrom django_bulk_drf import BulkRouter\nfrom rest_framework.routers import DefaultRouter\n\n# Option 1: Use BulkRouter (Recommended for bulk operations)\nrouter = BulkRouter()\nrouter.register('products', ProductViewSet)\n\n# Option 2: Use standard DefaultRouter (only POST will work for bulk)\n# router = DefaultRouter()\n# router.register('products', ProductViewSet)\n\nurlpatterns = router.urls\n```\n\n**Important:** To enable PATCH, PUT, and DELETE on collection endpoints (`/products/`), you must use `BulkRouter` or `BulkSimpleRouter` instead of DRF's standard routers. The standard DRF routers only map these methods to detail endpoints (`/products/{id}/`).\n\n## API Design\n\n### URL Structure\n\n```\nCollection Endpoints (Bulk by Default):\nPOST /products/ \u2192 Bulk create (accepts [...] or {...})\nPUT /products/ \u2192 Bulk update (requires unique_fields) \nPATCH /products/ \u2192 Bulk upsert (create or update)\nDELETE /products/ \u2192 Bulk delete (by unique_fields)\n\nDetail Endpoints (Standard DRF):\nGET /products/{id}/ \u2192 Retrieve single\nPUT /products/{id}/ \u2192 Update single\nPATCH /products/{id}/ \u2192 Partial update single\nDELETE /products/{id}/ \u2192 Delete single\n```\n\n**Key Insight**: The presence of `self.kwargs.get(self.lookup_field)` determines single vs. bulk operation.\n\n### Request/Response Examples\n\n#### Bulk Create\n```bash\n# Single create (backward compatible)\nPOST /products/\n{\n \"sku\": \"PROD-001\",\n \"name\": \"Widget\", \n \"price\": \"19.99\",\n \"category\": 1\n}\n\n# Bulk create\nPOST /products/\n[\n {\n \"sku\": \"PROD-001\",\n \"name\": \"Widget\",\n \"price\": \"19.99\", \n \"category\": 1\n },\n {\n \"sku\": \"PROD-002\",\n \"name\": \"Gadget\",\n \"price\": \"29.99\",\n \"category\": 1\n }\n]\n\n# Response\n{\n \"created\": 2,\n \"updated\": 0,\n \"failed\": 0,\n \"data\": [...]\n}\n```\n\n#### Bulk Upsert\n```bash\n# Bulk upsert (create or update)\nPATCH /products/\n[\n {\n \"sku\": \"PROD-001\", # Exists - will update\n \"name\": \"Updated Widget\",\n \"price\": \"24.99\"\n },\n {\n \"sku\": \"PROD-003\", # New - will create\n \"name\": \"New Product\", \n \"price\": \"39.99\",\n \"category\": 2\n }\n]\n\n# Response\n{\n \"created\": 1,\n \"updated\": 1,\n \"failed\": 0,\n \"data\": [...]\n}\n```\n\n#### Bulk Delete\n```bash\n# Bulk delete by unique fields\nDELETE /products/\n[\n {\"sku\": \"PROD-001\"},\n {\"sku\": \"PROD-002\"},\n {\"id\": 5}\n]\n\n# Response \n{\n \"deleted\": 3\n}\n```\n\n#### Single Operations (Backward Compatible)\n```bash\n# Single create still works\nPOST /products/\n{\n \"sku\": \"PROD-004\", \n \"name\": \"Single Product\",\n \"price\": \"49.99\"\n}\n\n# Response (standard DRF format)\n{\n \"id\": 4,\n \"sku\": \"PROD-004\",\n \"name\": \"Single Product\", \n \"price\": \"49.99\"\n}\n```\n\n## Configuration\n\n### Basic Settings\n\n```python\n# settings.py\nBULK_DRF = {\n 'DEFAULT_BATCH_SIZE': 1000, # Records per database batch\n 'MAX_BATCH_SIZE': 5000, # Maximum request size\n 'ATOMIC_OPERATIONS': True, # Wrap operations in transactions\n 'ENABLE_M2M_HANDLING': True, # Handle many-to-many relationships\n 'ALLOW_SINGULAR': True, # Allow single-object requests\n 'PREFER_MINIMAL_RESPONSE': False, # Return minimal response format\n 'PARTIAL_FAILURE_STRATEGY': 'ROLLBACK_ALL', # How to handle partial failures\n 'ENABLE_PERFORMANCE_MONITORING': False, # Track performance metrics\n 'AUTO_OPTIMIZE_QUERIES': True, # Auto-optimize database queries\n}\n```\n\n### Per-ViewSet Configuration\n\n```python\nclass ProductViewSet(BulkModelViewSet):\n queryset = Product.objects.all()\n serializer_class = ProductSerializer\n unique_fields = ['sku'] # Fields for upsert matching\n batch_size = 500 # Override default batch size\n \n def get_unique_fields(self):\n \"\"\"Dynamic unique fields based on request\"\"\"\n if self.request.query_params.get('match_by') == 'name':\n return ['name', 'category']\n return self.unique_fields\n```\n\n## Advanced Features\n\n### Foreign Key Handling\n\nThe package automatically handles FK relationships with support for:\n- **Integer PKs**: `{\"category\": 1}` \u2192 `{\"category_id\": 1}`\n- **Slug Fields**: `{\"category\": \"electronics\"}` \u2192 resolves slug to ID in batch\n\n### Many-to-Many Relationships\n\nM2M fields are automatically handled:\n```python\n# Request\n{\n \"name\": \"Product\",\n \"tags\": [1, 2, 3] # M2M field\n}\n\n# Automatically creates Product and sets M2M relationships\n```\n\n### Error Handling\n\n#### Validation Errors\n```python\n# Request with errors\nPOST /products/\n[\n {\"sku\": \"PROD-001\", \"name\": \"Valid Product\"},\n {\"sku\": \"\", \"name\": \"Invalid Product\"}, # Missing required field\n {\"sku\": \"PROD-003\", \"price\": \"invalid\"} # Invalid data type\n]\n\n# Response\n{\n \"created\": 1,\n \"updated\": 0,\n \"failed\": 2,\n \"errors\": {\n \"1\": {\"sku\": [\"This field may not be blank.\"]},\n \"2\": {\"price\": [\"A valid number is required.\"]}\n }\n}\n```\n\n#### Partial Failures\n\nWith `PARTIAL_FAILURE_STRATEGY = 'COMMIT_SUCCESSFUL'`:\n```python\n# Response when some items fail\n{\n \"created\": 1,\n \"updated\": 0,\n \"failed\": 2,\n \"data\": [...], # Only successful items\n \"errors\": {\n \"1\": {\"field\": [\"error\"]},\n \"2\": {\"field\": [\"error\"]}\n }\n}\n```\n\n## Performance Characteristics\n\n### Bulk Create\n- **Single INSERT query** per batch (default 1000 records)\n- **Typical Speed**: 10,000 records/second\n- **Memory**: O(batch_size) instances in memory\n\n### Bulk Update/Upsert \n- **Bulk Update**: Single SELECT query + single UPDATE query per batch\n - Typical Speed: 7,000-8,000 records/second\n- **Bulk Upsert** (Native): Single native upsert query (no SELECT needed)\n - Uses Django's `bulk_create` with `update_conflicts=True`\n - Typical Speed: 10,000-12,000 records/second\n - True database-level atomicity\n\n### Bulk Delete\n- **Single DELETE query** with OR conditions\n- **Typical Speed**: 15,000 records/second\n\n### Native Upsert Performance\n\nBulk upsert operations use Django's native `bulk_create` with `update_conflicts=True` for maximum performance:\n\n**How It Works:**\n```python\n# Single database operation handles both creates and updates\nPATCH /products/\n[\n {\"sku\": \"PROD-001\", \"name\": \"Updated\", \"price\": \"24.99\"}, # Exists - updates\n {\"sku\": \"PROD-003\", \"name\": \"New Product\", \"price\": \"39.99\"} # New - creates\n]\n\n# Response\n{\n \"created\": 2, # Native upsert doesn't distinguish created vs updated\n \"updated\": 0,\n \"failed\": 0,\n \"data\": [...]\n}\n```\n\n**Benefits:**\n- **Single Database Query**: No SELECT needed - the database handles conflict detection\n- **True Atomicity**: Database-level atomic operation (no race conditions)\n- **Better Performance**: 10,000-12,000 records/second vs 7,000-8,000 for traditional update\n- **Simpler Code**: One code path, easier to maintain\n\n**Database Support:**\n- PostgreSQL (all versions)\n- SQLite 3.24+\n- MySQL 8.0.19+\n- Oracle (with limitations)\n\n### Performance Tips\n\n#### 1. Minimal Responses for Large Operations\n```python\n# Use Prefer header for large datasets\nheaders = {'Prefer': 'return=minimal'}\nresponse = requests.post('/products/', json=large_dataset, headers=headers)\n\n# Response: {\"created\": 10000, \"updated\": 0, \"failed\": 0}\n```\n\n#### 2. Optimize Batch Sizes\n```python\n# For smaller records (few fields)\nBULK_DRF = {'DEFAULT_BATCH_SIZE': 2000}\n\n# For larger records (many fields, large text) \nBULK_DRF = {'DEFAULT_BATCH_SIZE': 500}\n```\n\n#### 3. Database Indexing\nEnsure your `unique_fields` are properly indexed for optimal upsert performance.\n\n## Migration Guide\n\n### From Standard DRF\n```python\n# Before\nclass ProductViewSet(viewsets.ModelViewSet):\n queryset = Product.objects.all()\n serializer_class = ProductSerializer\n\n# After \nclass ProductViewSet(BulkModelViewSet):\n queryset = Product.objects.all()\n serializer_class = ProductSerializer\n unique_fields = ['sku'] # Add for upsert support\n```\n\n### From drf-bulk\n```python\n# Before (drf-bulk)\nclass ProductViewSet(BulkModelViewSet):\n queryset = Product.objects.all()\n serializer_class = ProductSerializer\n# API: POST /products/bulk/\n\n# After (django-bulk-drf) \nclass ProductViewSet(BulkModelViewSet):\n queryset = Product.objects.all()\n serializer_class = ProductSerializer\n unique_fields = ['sku']\n# API: POST /products/ (cleaner URLs)\n```\n\n## Limitations\n\n- **Django Version**: Requires Django 4.1+ for native upsert support\n- **Database Support**: PostgreSQL, MySQL 8.0.19+, SQLite 3.24+, Oracle (with limitations)\n- **Not Supported**: Nested serializers, file uploads in bulk, complex validations requiring per-object database queries\n- **Upsert Response**: Native upsert doesn't distinguish between created and updated records (all count as \"created\")\n- **Best For**: Simple to medium complexity models with standard field types\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the MIT License.",
"bugtrack_url": null,
"license": "MIT",
"summary": "Django REST Framework for synchronous bulk operations with N+1 prevention",
"version": "0.2.46",
"project_urls": {
"Homepage": "https://github.com/AugendLimited/django-bulk-drf",
"Repository": "https://github.com/AugendLimited/django-bulk-drf"
},
"split_keywords": [
"django",
" drf",
" rest-framework",
" upsert",
" sync",
" operations",
" bulk"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "d3a40bb7d2888f24f2fb420df01c860bfe9d328233534f8efd64830d5762b9fb",
"md5": "25e6f9fd4b3fc288a97aca48a848fd7b",
"sha256": "d6a84c4516884a745cebe72153b79227214125c07155ab70b07c09159ff6fb87"
},
"downloads": -1,
"filename": "django_bulk_drf-0.2.46-py3-none-any.whl",
"has_sig": false,
"md5_digest": "25e6f9fd4b3fc288a97aca48a848fd7b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.11",
"size": 44842,
"upload_time": "2025-11-02T22:21:04",
"upload_time_iso_8601": "2025-11-02T22:21:04.392636Z",
"url": "https://files.pythonhosted.org/packages/d3/a4/0bb7d2888f24f2fb420df01c860bfe9d328233534f8efd64830d5762b9fb/django_bulk_drf-0.2.46-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ff2fe6ae46c5d19bef3a00107ee397939a9af1500d02fd9e967dad3082207ccc",
"md5": "df387921f1913a71d8174441bafc2d31",
"sha256": "c42df26499e5cec009a8c8ba2bf023938d477c7c46596a7f68643fa769f5bdd2"
},
"downloads": -1,
"filename": "django_bulk_drf-0.2.46.tar.gz",
"has_sig": false,
"md5_digest": "df387921f1913a71d8174441bafc2d31",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.11",
"size": 37954,
"upload_time": "2025-11-02T22:21:05",
"upload_time_iso_8601": "2025-11-02T22:21:05.981902Z",
"url": "https://files.pythonhosted.org/packages/ff/2f/e6ae46c5d19bef3a00107ee397939a9af1500d02fd9e967dad3082207ccc/django_bulk_drf-0.2.46.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-02 22:21:05",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "AugendLimited",
"github_project": "django-bulk-drf",
"github_not_found": true,
"lcname": "django-bulk-drf"
}