Easy to use django package for building APIs quicker than ever, built on top of django-ninja-extra
Project description
Django SwiftAPI Documentation
Overview
Django SwiftAPI, a fully async API framework, provides a powerful yet simple abstraction for automatically generating CRUD APIs, schema generation and robust file handling, built on top of django-ninja-extra. The core of this system is the use of:
SwiftBaseModel: A base model with built-in support for controlling request & responses, CRUD specifications, file fields, ownership, schema customization, object validations etc all out-of-the-box.SwiftBaseModelController: A customizable controller that automates schema generations & CRUD operations. All you need to do is plug-in yourSwiftBaseModel& it handles everything in the background.
This documentation explains how to use these components, configure your project, and extend the system for your needs.
Guide
- Installation
- Database Recommendation
- Usage
- Model Definition
- Model-Controller Setup
- URL Configuration
- File Handling
- Authentication & Permissions
Installation
Prerequisites: Before you start working, you need some familiarity on django, django-ninja & django-ninja-extra with it's modelcontrolling capabilities.
Install it using:
pip install django_swiftapi
Then, add these in your INSTALLED_APPS:
INSTALLED_APPS = [
...,
'ninja_extra',
'django_swiftapi',
]
Database Recommendation
django_swiftapiheavily relies on theArrayFieldfor managing file-fields. So you need to use a database that supports ArrayField. Normally, PostgreSQL is a good fit.
Usage
(TODO)
Model Definition
SwiftBaseModel
SwiftBaseModel is an abstract Django model that provides powerful hooks and configurations for automated CRUD operations, user ownership enforcement, and file upload handling when used with the crud_handler and SwiftBaseModelController from django-swiftapi.
Key Features
- Auto-included
created,updated, andcreated_byfields - Ownership-based object access control
- Field-level validation before save/update
- Built-in file handling for
ArrayField-based file storage - Built-in integration with both local and S3-based file systems
Full Model Example Using SwiftBaseModel
from django.db import models
from django.contrib.postgres.fields import ArrayField
from django_swiftapi.modelcontrol.models import SwiftBaseModel
from django_swiftapi.crud_operation.file_operations.storage_operations import local_storage
from django_swiftapi.crud_operation.file_operations.files_handlers import Files_Param
from django_swiftapi.crud_operation.file_operations.files_validators import validate_images, validate_file_sizes
class Product(SwiftBaseModel):
name = models.CharField(max_length=100)
images = ArrayField(models.CharField(max_length=200), default=list, blank=True, null=True)
files_fields = ["images"]
files_params_list = [
FilesParam(
field_name="images",
access="public",
storage=local_storage,
validator_funcs={
file_sizes_valid: {"limit": 5},
images_valid: {},
}
)
]
Model Fields
| Field | Type | Description |
|---|---|---|
created |
DateTimeField |
Auto timestamp when instance is created |
updated |
DateTimeField |
Auto timestamp on every update |
created_by |
ForeignKey(User) |
Automatically assigned user who created the object |
created_by_field |
str (default: 'created_by') |
Custom field to use for ownership checking |
Configuration Attributes
These are class-level attributes, not DB fields.
| Attribute | Type | Description |
|---|---|---|
required_to_create |
list[str] |
List of field names required during object creation |
required_to_update |
list[str] |
List of field names required during update |
exclude_in_request |
list[str] |
Fields to exclude while generating request schemas |
exclude_in_response |
list[str] |
Fields to exclude from response schemas |
obj_owner_check_before_save |
bool |
If True, ownership will be verified before saving |
files_fields |
list[str] |
Names of file fields (typically ArrayFields) |
files_params_list |
list[FilesParam] |
Full configuration for file handling per field |
File Handling Example
To manage file uploads, downloads, deletion etc (via ArrayField), follow this approach:
from django.contrib.postgres.fields import ArrayField
from django_swiftapi.crud_operation.file_operations.storage_operations import local_storage
from django_swiftapi.crud_operation.file_operations.files_handlers import Files_Param
from django_swiftapi.crud_operation.file_operations.files_validators import validate_images, validate_file_sizes
# Define file field in your model:
images = ArrayField(
models.CharField(max_length=200),
default=list,
size=5,
blank=True,
null=True
)
# Register it as a file field:
files_fields = ["images"]
# Provide full configuration for how files should be handled:
files_params_list = [
FilesParam(
field_name="images",
access="public",
storage=local_storage,
validator_funcs={
file_sizes_valid: {"limit": 10}, # limit in MB
images_valid: {}
}
),
]
Ownership Enforcement
By default, created_by is used to check whether the requesting user has access to modify or delete the object.
To enable this behavior, set:
obj_owner_check_before_save = True
If you use a different field for ownership, specify it with:
created_by_field = "your_owner_field_name"
Summary of Key Attributes
| Attribute | Type | Description |
|---|---|---|
required_to_create |
list[str] |
Fields required when creating an object (only applies during crud_handler operations) |
required_to_update |
list[str] |
Fields required when updating an object (only applies during crud_handler operations) |
exclude_in_request |
list[str] |
Fields to exclude from request schema generation |
exclude_in_response |
list[str] |
Fields to exclude from response schema generation |
files_fields |
list[str] |
List of fields representing file arrays (usually Django ArrayField) |
files_params_list |
list[FilesParam] |
List of FilesParam configurations for each file field |
obj_owner_check_before_save |
bool |
Whether to enforce ownership validation before saving an object |
created_by_field |
str |
Name of the field used for ownership validation (default "created_by") |
This documentation outlines how to utilize the SwiftBaseModel to build models that seamlessly integrate with the CRUD operations and file handling mechanisms provided by django-swiftapi.
For more details on file validations and storage options, refer to the respective modules.
Model-Controller Setup
Create modelcontrollers by inheriting from SwiftBaseModelController:
Full Configurations Example:
from ninja_extra import api_controller
from django_swiftapi.modelcontrol.modelcontrollers import SwiftBaseModelController
from .models import MyDocument
@api_controller("/documents",)
class DocumentController(SwiftBaseModelController):
model_to_control = MyDocument
create_enabled: bool = False
create_path: str = 'create'
create_info: str = 'create an item'
create_request_schemas: list[tuple[str, str, Schema, bool]] = None
create_response_schemas: dict[int, Schema] = None
create_custom_permissions_list: list = []
retrieve_one_enabled: bool = False
retrieve_one_path: str = 'retrieveone/{id}'
retrieve_one_info: str = 'retrieve an item'
retrieve_one_depth = 0
retrieve_one_response_schemas: dict[int, Schema] = None
retrieve_one_custom_permissions_list: list = []
retrieve_one_obj_permission_check: bool = False
filter_enabled: bool = False
filter_path: str = 'filter'
filter_info: str = 'filter & get the listed result'
filter_depth = 0
filter_request_schemas: list[tuple[str, str, Schema, bool]] = None
filter_response_schemas: dict[int, Schema] = None
filter_custom_permissions_list: list = []
filter_obj_permission_check: bool = False
update_enabled: bool = False
update_path: str = '{id}/update'
update_info: str = 'update or add files to an item'
update_request_schemas: list[tuple[str, str, Schema, bool]] = None
update_response_schemas: dict[int, Schema] = None
update_custom_permissions_list: list = []
update_obj_permission_check: bool = False
file_retrieve_enabled: bool = False
file_retrieve_path: str = '{id}/file/retrieve'
file_retrieve_info: str = 'retrieve a single file of an item'
file_retrieve_request_schemas: list[tuple[str, str, Schema, bool]] = None
file_retrieve_response_schemas: dict[int, Schema] = None
file_retrieve_custom_permissions_list: list = []
file_retrieve_obj_permission_check: bool = False
files_remove_enabled: bool = False
files_remove_path: str = '{id}/files/remove'
files_remove_info: str = 'remove files of an item',
files_remove_request_schemas: list[tuple[str, str, Schema, bool]] = None
files_remove_response_schemas: dict[int, Schema] = None
files_remove_custom_permissions_list: list = []
files_remove_obj_permission_check: bool = False
delete_enabled: bool = False
delete_path: str = '{id}/delete'
delete_info: str = 'delete an item with all its files'
delete_response_schemas: dict[int, Schema] = None
delete_custom_permissions_list: list = []
delete_obj_permission_check: bool = False
Model-Controller Options
CRUD Operations
| Option | Type | Default | Description |
|---|---|---|---|
create_enabled |
bool | False |
Enable create endpoint |
retrieve_one_enabled |
bool | False |
Enable retrieve endpoint |
filter_enabled |
bool | False |
Enable filter/search endpoint |
update_enabled |
bool | False |
Enable update endpoint |
delete_enabled |
bool | False |
Enable delete endpoint |
File Operations
| Option | Type | Default | Description |
|---|---|---|---|
file_retrieve_enabled |
bool | False |
Enable file retrieval endpoint |
files_remove_enabled |
bool | False |
Enable file removal endpoint |
Permission Options
| Option | Type | Default | Description |
|---|---|---|---|
*_custom_permissions_list |
list | [] |
Custom permissions for specific operation |
*_obj_permission_check |
bool | False |
Check object ownership for operation |
Path Customization (You can leave it to the default)
| Option | Type | Default | Description |
|---|---|---|---|
create_path |
str | 'create' |
Custom path for create endpoint |
retrieve_one_path |
str | 'retrieveone/{id}' |
Custom path for retrieve endpoint |
filter_path |
str | 'filter' |
Custom path for filter endpoint |
update_path |
str | '{id}/update' |
Custom path for update endpoint |
delete_path |
str | '{id}/delete' |
Custom path for delete endpoint |
file_retrieve_path |
str | '{id}/file/retrieve' |
Custom path for file retrieve |
files_remove_path |
str | '{id}/files/remove' |
Custom path for file removal |
You can also customize their info and schemas. just set the variables properly.
URL Configuration
Configure your URLs to include the API endpoints (Reference).
Example:
# urls.py
from django.contrib import admin
from django.urls import path, include
from ninja_extra import NinjaExtraAPI
from your_app.controllers import DocumentController
api = NinjaExtraAPI()
api.register_controllers(DocumentController)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', api.urls),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
That's it! Thanks to ninja & ninja-extra, now you can see the auto-generated documentation at http://127.0.0.1:8000/api/docs.
File Handling
Easier than ever!
File Configuration
Configure file-handling from inside your model's file configuration, specify a few attributes in setings.py and that's it! No extra work, no nothing. All CRUD functionalities (uploads, downloads, deletions etc) including authentications, permissions, individual-accesses are handled automatically by django-swiftapi.
In settings.py, just specify according to your needs. django-swiftapi will use these directories to write or remove files:
# if you are using local storage
PUBLIC_LOCAL_FILE_WRITE_LOCATION = "" # ensure this directory is public in your production server, ex: 'dummy_site_files/public'
PUBLIC_LOCAL_FILE_URL_PREFIX = "/media" # this prefix will be used in the file links, ex: '/media'
PRIVATE_LOCAL_FILE_WRITE_LOCATION = "" # ensure this directory is not publicly accessible in your production server, ex: 'dummy_site_files/private'
MEDIA_ROOT = PUBLIC_LOCAL_FILE_WRITE_LOCATION
# if you are using amazon s3
PUBLIC_AMAZONS3_BUCKET_NAME = ""
PUBLIC_AMAZONS3_FILE_WRITE_LOCATION = ""
PUBLIC_AMAZONS3_FILE_URL_PREFIX = ""
PRIVATE_AMAZONS3_BUCKET_NAME = ""
PRIVATE_AMAZONS3_FILE_WRITE_LOCATION = ""
# Needed in both cases
MEDIA_URL = '/media/' # the value '/media/' is necessary for serving files during development according to django-docs
File Operations
The system automatically provides these file operations:
- Upload: Files are uploaded during create/update operations
- Retrieve: Download files via
/file/retrieveendpoint - Remove: Delete specific files via
/files/removeendpoint
File Access Control
- Public files: Accessible without authentication
- Private files: Require authentication and if specified, ownership verification
Using Your Own Validation
It's super easy. Just define a function (django-swiftapi supports both sync & async) and put it into the dictionary variable validator_funcs like this:
async def your_validator(arg_name=default):
# if it validates, then return None
# if it fails to validate, return a single string containing the error message
return "error occurred"
validator_funcs={
file_sizes_valid: {"limit": 5},
images_valid: {},
your_validator: {"<arg_name>": <arg_value>}
}
Storage Support
django-swiftapi currently supports:
- local storage (
django_swiftapi.crud_operation.file_operations.storage_operations.local_storage) - aws s3 storage (
django_swiftapi.crud_operation.file_operations.storage_operations.aws_s3_storage)
However, if you want to create support for new platforms, you can do it just by inheriting the BaseStorage class and defining these methods below:
from django.db.models import Model
from django_swiftapi.crud_operation.file_operations.storage_operations.base import BaseStorage
class custom_storage_class(BaseStorage):
async def dir_maker(instance:Model, files_param):
"""
Create and return the directory path for storing files related to the model instance.
Used internally by the storage class.
"""
pass
async def url_maker(self, abs_path:str, files_param, source_dir:str=""):
"""
Generate a URL (or file identifier for private) from the absolute file path.
Used internally by the storage class.
"""
pass
async def _files_writer(self, instance:Model, files_param):
"""
Write uploaded files to the specified filesystem.
Args:
instance (Model): Django model instance.
files_param (Files_Param): Contains uploaded file list, chunk size, access level, etc.
Returns:
Two lists:
- List of successfully written file URLs.
- List of failed file names.
"""
pass
async def _files_remover(self, instance:Model, files_param, remove_dir=False):
"""
Remove files or entire directory from the specified filesystem.
Args:
instance (Model): Django model instance.
files_param (Files_Param): Contains file_links to remove.
remove_dir (bool, optional): Whether to remove the whole directory.
Returns:
Two lists:
- List of successfully removed file links.
- List of failed file links.
"""
pass
async def _files_retriever(self, instance:Model, files_param):
"""
Yields chunks of file data from the specified path for streaming purposes.
Args:
instance (Model): Django model instance.
files_param (Files_Param): Contains file_links to retrieve.
Yields:
Two lists:
- List of dictionaries mapping file names to file streams for successfully retrieved files.
- List of failed file names.
"""
pass
Authentication & Permissions
django_swiftapi is highly compatible with django-allauth. So, if you're using django-allauth, you can validate authentications directly in your modelcontrollers.
Using Built-in Authentication Classes
from ninja_extra import api_controller
from django_swiftapi.modelcontrol.modelcontrollers.base import SwiftBaseModelController
from django_swiftapi.modelcontrol.authenticators.django_allauth import djangoallauth_userauthentication
# Using allauth authentication
@api_controller("/api", permissions=[djangoallauth_userauthentication()])
class MyController(SwiftBaseModelController):
pass
IMPORTANT NOTE: Using @api_controller("/api", permissions=[djangoallauth_userauthentication()]) will enable authentication for all the routes of the corresponding modelcontroller. If you wish to allow certain routes to pass without authentication, you can do it simply like this:
from ninja_extra import api_controller, permissions
from django_swiftapi.modelcontrol.modelcontrollers.base import SwiftBaseModelController
from django_swiftapi.modelcontrol.authenticators.django_allauth import djangoallauth_userauthentication
@api_controller("/api", permissions=[djangoallauth_userauthentication()])
class MyController(SwiftBaseModelController):
create_enabled= True
create_custom_permissions_list = [permissions.AllowAny]
As simple as that! You can enable this functionality for others too or you can incorporate your own customized authentication classes for each operation, using:
retrieve_one_custom_permissions_list: list = []
filter_custom_permissions_list: list = []
update_custom_permissions_list: list = []
file_retrieve_custom_permissions_list: list = []
files_remove_custom_permissions_list: list = []
delete_custom_permissions_list: list = []
Enabling Object-Level Permissions
If you wish to give object-specific permissions like only the creator of that object can rerieve, filter, update, remove or delete that object, you can do so like this:
retrieve_one_obj_permission_check = True
filter_obj_permission_check = True
update_obj_permission_check = True
file_retrieve_obj_permission_check = True
files_remove_obj_permission_check = True
delete_obj_permission_check = True
Example:
from django_swiftapi.modelcontrol.modelcontrollers.base import SwiftBaseModelController
class DocumentController(SwiftBaseModelController):
retrieve_one_obj_permission_check = True # Only owner can retrieve
update_obj_permission_check = True # Only owner can update
delete_obj_permission_check = True # Only owner can delete
Customizing Authentication Class
If you're using any other user authentication system, you need to define your own authentication class overriding just one function:
from django_swiftapi.modelcontrol.authenticators.base import BaseUserAuthentication
# Create custom authentication
class CustomAuthentication(BaseUserAuthentication):
def has_permission(self, request, view):
# Your custom logic for verifying if the user is authenticated
# return the user object if authenticated else None
then use it like this:
@api_controller("/api", permissions=[CustomAuthentication()])
class MyController(SwiftBaseModelController):
pass
Permission Levels
- Controller Level: Applied to all endpoints in the controller
- Operation Level: Specific permissions per CRUD operation
- Object Level: Ownership-based permissions
Filtering and Search
The filter endpoint supports:
- Field-based filtering
- Search functionality via URL parameters
- Pagination [Can be set according to django-ninja specs]
- Custom filter expressions
Example filter request:
POST /api/documents/filter
{
"title": "My Document",
"created__gte": "2024-01-01"
}
You can basically use everything provided by django-ninja & django-ninja-extra!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_swiftapi-0.1.9.tar.gz.
File metadata
- Download URL: django_swiftapi-0.1.9.tar.gz
- Upload date:
- Size: 34.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fd2562e24d0390c4a5200bf98c5261e5e205a9645a14fa5570abb601f829652
|
|
| MD5 |
d16e6de96cd4c967b5559212ea9eeb29
|
|
| BLAKE2b-256 |
fd74e2d7057415bafeeacc761d24263f62030e2a66687905e758c74fd8fd9036
|
File details
Details for the file django_swiftapi-0.1.9-py3-none-any.whl.
File metadata
- Download URL: django_swiftapi-0.1.9-py3-none-any.whl
- Upload date:
- Size: 36.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1728123f6dfc0ccfdab72bcf6baa46918f0601e03b9ff5c26955aa49b1e4d155
|
|
| MD5 |
f8033e706f27545b8cda0c55045289d5
|
|
| BLAKE2b-256 |
da7005c0595bef420179283ef477d5baff8ad87a4d547e0c0f5e47656fc141cb
|