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. Built for rapid development, it eliminates the need to write views or serializers manually — just configure your models & controllers and deploy. 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.
- Installation
- Database Recommendation
- Usage
- Model Definition
- Model-Controller Setup
- URL Configuration
- File Handling
- Authentication & Permissions
Install it using:
pip install django-swiftapi
Then, add these in your INSTALLED_APPS:
INSTALLED_APPS = [
...,
'ninja_extra',
'django_swiftapi',
]
django-swiftapi
heavily relies on theArrayField
for managing file-fields. So you need to use a database that supports ArrayField. Normally, PostgreSQL is a good fit.
Welcome to the Django-SwiftAPI usage tutorial. This guide will walk you through setting up a fully functional async API server with minimal effort — from starting your project to building secure, filterable, paginated API endpoints backed by user authentication.
Django-SwiftAPI is designed for rapid API development. With just a few lines of code, you get models, controllers, CRUD, filtering, search, pagination, file-handling and authentication — all built on top of Django Ninja and Django Ninja Extra
python -m venv venv
source venv/bin/activate # On Windows: source venv/Scripts/activate
pip install django-swiftapi
django-admin startproject myproject
cd myproject
python manage.py startapp api
INSTALLED_APPS = [
...
"ninja_extra",
"django_swiftapi",
"api", # your app
]
# api/models.py
from django.db import models
from django_swiftapi.modelcontrol.models import SwiftBaseModel
class Product(SwiftBaseModel):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.CharField(max_length=50)
Run migrations:
python manage.py makemigrations
python manage.py migrate
Full functional CRUD APIs with automatic files-handling support
# api/modelcontrollers.py
from ninja_extra import api_controller
from django_swiftapi.modelcontrol.modelcontrollers import SwiftBaseModelController
from .models import Product
@api_controller("/product",)
class ProductController(SwiftBaseModelController):
model_to_control = Product
create_enabled = True
retrieve_one_enabled = True
search_enabled = True
filter_enabled = True
update_enabled = True
delete_enabled = True
from django.contrib import admin
from django.urls import path, include
from ninja_extra import NinjaExtraAPI
from api.modelcontrollers import ProductController
# Register the controller with your API
api_routes = NinjaExtraAPI(
title="myproject",
version="development",
description="my cool project!",
)
api_routes.register_controllers(ProductController,)
urlpatterns = [
path('admin/', admin.site.urls),
path("api/", api_routes.urls), # this is where all your endpoints live
]
✅ That's literally it! Now, Product
is available at /api/product
with full CRUD support!
First, let's start our server with uvicorn:
pip install uvicorn
uvicorn myproject.asgi:application --reload
Now, go to http://127.0.0.1:8000/api/docs
to find all the routes with request-response examples.
Thanks to ninja & ninja-extra, your project's full api documentation is accessible at /api/docs
.
Example url: http://127.0.0.1:8000/api/docs
You don't need to implement anything. Django-SwiftAPI automatically enables all filtering functionalities in the /filter
route using Django Ninja’s features if filter_enabled = True
in the model-controller.
Try accessing:
curl -X POST http://127.0.0.1:8000/api/product/filter \
-H "Content-Type: application/json" \
-d '{}'
This is basically a POST
request to the route http://127.0.0.1:8000/api/product/filter
with an empty json body. This will return all product instances with a default pagination of 100 items (you can change it here). You can easily filter items if a body is provided:
curl -X POST http://127.0.0.1:8000/api/product/filter \
-H "Content-Type: application/json" \
-d '{"name": "earbuds", "price": "10.0"}'
This will return all the results containing exact-matches with either the "name" or the "price" or both.
You can also control the pagination by putting parameters like:
http://127.0.0.1:8000/api/product/filter?limit=20&offset=0
It basically shows 20 results from the beginning. Go to Pagination for more details.
Django-SwiftAPI comes with built-in search functionality — no manual implementation needed.
If you set search_enabled = True
in your ModelController, SwiftAPI will automatically expose a /search
route using Django Ninja Extra's powerful Searching integration.
Try accessing:
http://127.0.0.1:8000/api/product/search?search=earbuds
This will return all records where any searchable field contains the word earbuds, either fully or partially.
Django-SwiftAPI directly integrates Ninja-Extra’s searching system, so you can follow their official searching guide for advanced use-cases like customizing searchable fields or handling complex lookups.
As like in the /filter
route, you can use controlled pagination here by using two parameters limit
and offset
.
When you use any /filter
or /search
endpoint in Django-SwiftAPI, results are paginated by default. The following functionality is provided by django-ninja
By default, 100 items per page are returned in each response. This is controlled by Django Ninja’s default setting.
You can change the default limit globally for your project by adding this to your settings.py
:
NINJA_PAGINATION_PER_PAGE = 50 # or any number you prefer
Now, every paginated endpoint will return 50 items per page unless manually overridden.
You can control pagination by passing the limit
and offset
query parameters.
Example:
/api/product/search?search=earbuds&limit=20&offset=40
This will return 20 results, starting from the 41st record.
If you're using django-allauth, django_swiftapi
has a built-in authentication class for it. You can use it directly in your modelcontrollers.
Install it if you haven't already. Then use like this:
from ninja_extra import api_controller
from django_swiftapi.modelcontrol.modelcontrollers import SwiftBaseModelController
from django_swiftapi.modelcontrol.authenticators import djangoallauth_userauthentication
# Using allauth authentication
@api_controller("/product", permissions=[djangoallauth_userauthentication()])
class ProductController(SwiftBaseModelController):
# your codes
This will enable authentication for all the routes in that modelcontroller.
If you prefer to allow certain routes without authentication, you can do it simply:
from ninja_extra import api_controller, permissions
from django_swiftapi.modelcontrol.modelcontrollers import SwiftBaseModelController
from django_swiftapi.modelcontrol.authenticators import djangoallauth_userauthentication
@api_controller("/product", permissions=[djangoallauth_userauthentication()])
class ProductController(SwiftBaseModelController):
create_enabled= True
create_custom_permissions_list = [permissions.AllowAny]
Now, the /create
route can be accessed by anyone. If you wanna use a custom authentication class, follow this guideline.
Go to Authentication & Permissions for more details.
If the built-in functionalities don't meet your needs and you want your own routes & custom functionalities, simply define a method inside the model-controller class and put your own logics. Follow the official documentation from django-ninja-extra.
One of the best features of Django is that it provides a robust and customizable Admin Panel. You can register the models in admin.py
as usual:
from django.contrib import admin
from .models import Product
admin.site.register(Product)
To access the admin panel, first you need to create a superuser from your terminal:
python manage.py createsuperuser
The admin panel is accessible at http://127.0.0.1:8000/admin/
Django-SwiftAPI removes boilerplate and makes you productive in minutes. Compared to Django Ninja or FastAPI, you write:
- ✅ No manual schema or serializers
- ✅ No explicit views
- ✅ No filters setup
- ✅ No pagination configuration
- ✅ Everything is automatic via configuration and base classes
Happy Hacking 🚀
SwiftBaseModel
is an abstract Django model that provides powerful hooks and configurations for automated CRUD operations, user ownership enforcement, and file upload/download/deletion handling when used with the SwiftBaseModelController
from django-swiftapi
.
- Auto-included
created
,updated
, andcreated_by
fields - 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
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={
validate_file_sizes: {"limit": 5},
validate_images: {},
}
)
]
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, the field referred here must point to a User model |
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. Meaning, only the user who created the object can save it |
obj_fields_to_check_owner |
list[str] |
Field names pointing to related objects whose created_by must match request.user |
files_fields |
list[str] |
Names of file fields (typically ArrayField s) |
files_params_list |
list[FilesParam] |
Full configuration for file handling per field |
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={
validate_file_sizes: {"limit": 5}, # limit in MegaBytes
validate_images: {},
}
),
]
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"
If you want to ensure that the requesting user is the owner not only of the main object but also of related objects referenced via ForeignKey fields, you can use the obj_fields_to_check_owner
attribute.
-
obj_fields_to_check_owner
:A list of string field names representing ForeignKey relationships on the main model. For each specified ForeignKey field, the ownership check will verify that the requesting user is also the owner of the related object.
This way, the permission or validation logic will recursively check ownership on those related objects as well, ensuring more secure and fine-grained access control.
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.
Create modelcontrollers by inheriting from SwiftBaseModelController
:
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
# These are default values
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
search_enabled: bool = False
search_path: str = 'search'
search_info: str = 'search & get the listed result'
search_depth = 0
search_response_schemas: dict[int, Schema] = None
search_custom_permissions_list: list = []
search_obj_permission_check: bool = False
search_premium_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
Option | Type | Default | Description |
---|---|---|---|
create_enabled |
bool | False |
Enable create endpoint |
retrieve_one_enabled |
bool | False |
Enable retrieve endpoint |
search_enabled |
bool | False |
Enable search endpoint |
filter_enabled |
bool | False |
Enable filter endpoint |
update_enabled |
bool | False |
Enable update endpoint |
delete_enabled |
bool | False |
Enable delete endpoint |
Option | Type | Default | Description |
---|---|---|---|
file_retrieve_enabled |
bool | False |
Enable file retrieval endpoint |
files_remove_enabled |
bool | False |
Enable file removal endpoint |
Option | Type | Default | Description |
---|---|---|---|
*_custom_permissions_list |
list | [] |
Custom permissions for specific operation |
*_obj_permission_check |
bool | False |
Check object ownership for operation |
Option | Type | Default | Description |
---|---|---|---|
create_path |
str | 'create' |
Custom path for create endpoint |
retrieve_one_path |
str | 'retrieveone/{id}' |
Custom path for retrieve endpoint |
search_path |
str | 'search' |
Custom path for search 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.
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.
Easier than ever!
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={
validate_file_sizes: {"limit": 10}, # limit in MB
validate_images: {}
}
),
]
Configure file-handling from inside your model's file configuration, specify a few attributes in setings.py like below 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 your settings.py:
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
MEDIA_URL = '/media/' # the value '/media/' is necessary for serving files during development according to django-docs
First, you need to install boto3 and set some configurations following AWS's official docs
Then in your settings.py, set these attributes:
PUBLIC_AMAZONS3_BUCKET_NAME = ""
PUBLIC_AMAZONS3_FILE_WRITE_LOCATION = ""
PUBLIC_AMAZONS3_FILE_URL_PREFIX = ""
PRIVATE_AMAZONS3_BUCKET_NAME = ""
PRIVATE_AMAZONS3_FILE_WRITE_LOCATION = ""
MEDIA_URL = '/media/' # the value '/media/' is necessary for serving files during development according to django-docs
Done!
The system automatically provides these file operations:
- Upload: Files are uploaded during
create
orupdate
operations - Retrieve: Download files via
/file/retrieve
endpoint - Remove: Delete specific files via
/files/remove
endpoint
- Public files: Accessible without authentication
- Private files: Require authentication and if specified, ownership verification
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:
# Define your validation function
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"
# Then, use it inside `FilesParam`
validator_funcs={
your_validator: {"<arg_name>": <arg_value>}
}
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 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
django_swiftapi
is highly compatible with django-allauth. So, if you're using django-allauth, you can validate authentications directly in your modelcontrollers.
from ninja_extra import api_controller
from django_swiftapi.modelcontrol.modelcontrollers import SwiftBaseModelController
from django_swiftapi.modelcontrol.authenticators import djangoallauth_userauthentication
# Using allauth authentication
@api_controller("/api", permissions=[djangoallauth_userauthentication()])
class MyController(SwiftBaseModelController):
pass
Under the hood, djangoallauth_userauthentication
takes the x-session-token
header from the request and verifies if the user is logged-in. That's how django-allauth
authenticates it's users.
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 import SwiftBaseModelController
from django_swiftapi.modelcontrol.authenticators 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 other routes too or 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 = []
You can enhance your endpoint protection by using the optional extra_permission_list
.
This lets you specify a list of boolean fields from your user model — all of which must be True
for a user to pass authentication.
Suppose your User
model has these custom fields:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
is_verified = models.BooleanField(default=False)
is_manager = models.BooleanField(default=False)
You can now protect any SwiftAPI controller like so:
from ninja_extra import api_controller
from django_swiftapi.modelcontrol.modelcontrollers import SwiftBaseModelController
from django_swiftapi.modelcontrol.authenticators import djangoallauth_userauthentication
@api_controller("/api", permissions=[djangoallauth_userauthentication(extra_permission_list=["is_verified", "is_manager"])])
class MyController(SwiftBaseModelController):
pass
In this case, the authenticated user must have both is_verified=True
and is_manager=True
to access this controller’s endpoints.
- Each string in
extra_permission_list
refers to boolean fields on your user model. - All listed fields must be
True
— otherwise, authentication fails. - Perfect for staff-only, verified-user, or gated-feature access control.
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 ninja_extra import api_controller
from django_swiftapi.modelcontrol.modelcontrollers import SwiftBaseModelController
@api_controller("/api", permissions=[djangoallauth_userauthentication()])
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
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 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
- Controller Level: Applied to all endpoints in the controller
- Operation Level: Specific permissions per CRUD operation
- Object Level: Ownership-based permissions