A JsonAPI framework based on Django + FastAPI + Pydantic.
Project description
Bazis
A framework for rapid API development that combines the capabilities of Django and FastAPI to create flexible and high-performance solutions.
Quick Start
# Install the package
uv add bazis
# Create a model
from bazis.core.models_abstract import InitialBase
from django.db import models
class Organization(InitialBase):
name = models.CharField(max_length=255)
# Create a route
from bazis.core.routes_abstract.jsonapi import JsonapiRouteBase
from django.apps import apps
class OrganizationRouteSet(JsonapiRouteBase):
model = apps.get_model('myapp.Organization')
Table of Contents
- Description
- Core Concept
- Features
- Requirements
- Installation
- Deployment
- Auto-Documentation
- Usage
- API Features
- Examples
- Architecture
- Development
- Contributing
- License
- Links
Description
Bazis is a framework designed for rapid API development. It combines the capabilities of Django and FastAPI to provide a flexible and reliable solution for creating APIs. Django is used as the foundation for ORM and the administrative panel, while FastAPI ensures high performance when handling API requests.
This package serves as the core for other Bazis family packages.
To add additional functionality, you can use packages named bazis-<n>. All of these packages will require this core package.
Core Concept
The Bazis framework is built around combining Django's ORM capabilities with FastAPI's high-performance API handling. This hybrid approach allows developers to utilize Django's robust database management tools and admin interface while benefiting from the speed and simplicity of FastAPI.
The central entity in a project built using Bazis is the Django model. Once its fields are defined, it provides enough information to generate a working CRUD API. For describing the API, the OpenAPI protocol is a perfect fit, into which Pydantic schemas can be automatically converted.
Thus, the framework's task is to transform model and route definitions into Pydantic schemas.
In Bazis, these Pydantic schemas are generated as follows:
- A route class is declared, specifying the target model
- Optionally, field restrictions can be defined
- Specific CRUD operations for the route can be optionally specified
- Input and output Pydantic schemas are generated based on the route and related model
Features
- Hybrid Framework: Combines Django and FastAPI
- JSON:API Specification: Adheres to the JSON:API specification for building APIs
- Class-Based Routes: Routes are defined as class methods
- Modular Design: Easily extendable and customizable
- Settings Management System: Implementation via
django.conf.settingswith support for environment variables and admin panel - Automatic Schema Generation: Pydantic schemas are automatically generated based on Django models
- Nested Structures Support: Thanks to JSON:API, includes support for
included, multi-level filtering, and more - Dynamic Fields: Flexible field configuration at the route level
- High Performance: FastAPI's asynchronous capabilities provide low latency
- Calculated Fields: Powerful system for working with related data via
@calc_propertydecorator
Advantages Over Other Solutions
- Performance: FastAPI's asynchronous capabilities provide high performance and low latency
- Flexibility: Combines the best aspects of Django and FastAPI
- Ease of Use: Simplifies the development process through class-based routes and dependency injection
- Scalability: Designed to handle large-scale applications
- Standards Compliance: Adheres to JSON:API and OpenAPI specifications to ensure consistency and interoperability
Requirements
- Python: 3.12+
- PostgreSQL: 12+
- Redis: For caching
Note! The current implementation of the framework requires Redis as the cache backend and PostgreSQL as the database.
Installation
Using uv (recommended)
uv add bazis
Using pip
pip install bazis
For development
git clone git@github.com:ecofuture-tech/bazis.git
cd bazis
uv sync --dev
Deployment
The backend consists of 2 services:
- API service
- Admin service
Deployment Scripts
deploy/run/app_init.sh- initialization proceduresdeploy/run/app.sh- start API servicedeploy/run/admin.sh- start admin servicedeploy/run/tests.sh- run tests
Gunicorn Configs
deploy/config/app.py- API service configdeploy/config/admin.py- admin service config
Environment Variables
Required environment variables:
BS_DEBUG- debug modeBS_DB_HOST- database hostBS_DB_PORT- database portBS_DB_NAME- database nameBS_DB_USER- database userBS_DB_PASSWORD- database passwordBS_MEDIA_ROOT- full path to media files folderBS_STATIC_ROOT- full path to static files folderBS_APP_PORT- API service portBS_ADMIN_PORT- admin service portBS_MEDIA_URL- absolute URL for media files folderBS_STATIC_URL- absolute URL for static files folderBS_ADMIN_NAME- admin username for admin serviceBS_ADMIN_PASSWORD- admin password for admin service
Auto-Documentation
Swagger UI
Available at /api/swagger/
Features:
- Ability to authenticate and execute requests
- Click the "Authorize" button
- Enter username and password (leave other fields empty)
- List of all endpoints with schemas displayed below
ReDoc
Available at /api/redoc/
Features:
- More readable endpoint documentation
- Does not allow request execution
Usage
Creating Models
Create models by inheriting from base classes:
from bazis.core.models_abstract import InitialBase, DtMixin, UuidMixin, JsonApiMixin
from django.db import models
class VehicleBrand(DtMixin, UuidMixin, JsonApiMixin):
"""Vehicle brand."""
name = models.CharField('Brand Name', max_length=255, unique=True)
class Meta:
verbose_name = 'Vehicle Brand'
verbose_name_plural = 'Vehicle Brands'
def __str__(self):
return self.name
Important! Every model and mixin must inherit from
bazis.core.models_abstract.InitialBase.
Example model with Foreign Key:
class VehicleModel(DtMixin, UuidMixin, JsonApiMixin):
"""Vehicle model."""
brand = models.ForeignKey(VehicleBrand, verbose_name='Brand', on_delete=models.CASCADE)
model = models.CharField('Model', max_length=255, unique=True)
engine_type = models.CharField('Engine Type', max_length=50, null=True, blank=True)
capacity = models.DecimalField(
'Capacity, t', max_digits=6, decimal_places=2, null=True, blank=True
)
class Meta:
verbose_name = 'Vehicle Model'
verbose_name_plural = 'Vehicle Models'
unique_together = ('brand', 'model')
def __str__(self):
return self.model
Example model with ManyToMany:
class Driver(DtMixin, UuidMixin, JsonApiMixin):
"""Driver."""
first_name = models.CharField('First Name', max_length=255)
last_name = models.CharField('Last Name', max_length=255)
contact_phone = models.CharField('Phone', max_length=50, null=True, blank=True)
divisions = models.ManyToManyField(
'Division',
related_name='drivers',
blank=True,
)
org_owner = models.ForeignKey(
'Organization',
blank=True,
null=True,
db_index=True,
on_delete=models.SET_NULL,
)
class Meta:
verbose_name = 'Driver'
verbose_name_plural = 'Drivers'
def __str__(self):
return f'{self.first_name} {self.last_name}'
Example model with OneToOne:
class ExtendedEntity(ExtendedEntityBase, DtMixin, UuidMixin, JsonApiMixin):
parent_entity = models.OneToOneField(
'ParentEntity', on_delete=models.CASCADE, related_name='extended_entity'
)
Example model with calculated field:
from decimal import Decimal
from bazis.core.utils.orm import calc_property, FieldRelated
from bazis.core.utils.functools import get_attr
class Vehicle(DtMixin, UuidMixin, JsonApiMixin):
"""Vehicle with calculated fields."""
vehicle_model = models.ForeignKey(
VehicleModel, verbose_name='Vehicle Model', on_delete=models.CASCADE
)
country = models.ForeignKey(Country, verbose_name='Country', on_delete=models.CASCADE)
gnum = models.CharField('State Registration Number', max_length=50, unique=True)
@calc_property([FieldRelated('vehicle_model')])
def vehicle_capacity(self) -> Decimal:
return get_attr(self, 'vehicle_model.capacity', Decimal(0.00))
class Meta:
verbose_name = 'Vehicle'
verbose_name_plural = 'Vehicles'
def __str__(self):
return self.gnum
Creating Routes
Create routes for your models:
from django.apps import apps
from bazis.core.routes_abstract.jsonapi import JsonapiRouteBase
class VehicleBrandRouteSet(JsonapiRouteBase):
"""Route for VehicleBrand"""
model = apps.get_model('entity.VehicleBrand')
Route with additional fields:
from bazis.core.schemas.fields import SchemaField, SchemaFields
class ParentEntityRouteSet(JsonapiRouteBase):
model = apps.get_model('entity.ParentEntity')
# Add fields (extended_entity, dependent_entities and calculated properties) to schema
fields = {
None: SchemaFields(
include={
'extended_entity': None,
'dependent_entities': None,
'active_children': SchemaField(source='active_children', required=False),
'count_active_children': SchemaField(
source='count_active_children', required=False
),
'has_inactive_children': SchemaField(
source='has_inactive_children', required=False
),
'extended_entity_price': SchemaField(
source='extended_entity_price', required=False
),
},
),
}
Route with ManyToMany relationship inclusion:
class ChildEntityRouteSet(JsonapiRouteBase):
model = apps.get_model('entity.ChildEntity')
fields = {
None: SchemaFields(
include={
'parent_entities': None, # ManyToMany relationship
},
),
}
Registering Routes
Creating router.py in your application:
from bazis.core.routing import BazisRouter
from . import routes
# Main router with tags for grouping
router = BazisRouter(tags=['Entity'])
router.register(routes.ChildEntityRouteSet.as_router())
router.register(routes.DependentEntityRouteSet.as_router())
router.register(routes.ExtendedEntityRouteSet.as_router())
router.register(routes.ParentEntityRouteSet.as_router())
router.register(routes.VehicleModelRouteSet.as_router())
router.register(routes.VehicleBrandRouteSet.as_router())
router.register(routes.CountryRouteSet.as_router())
router.register(routes.CarrierTaskRouteSet.as_router())
router.register(routes.DivisionJsonRouteSet.as_router())
router.register(routes.DriverRouteSet.as_router())
# Create specialized routers with prefixes
router_context = BazisRouter(tags=['Context'])
router_context.register(routes.DriverContextRouteSet.as_router())
router_json = BazisRouter(tags=['Json'])
router_json.register(routes.DriverJsonHierarchyRouteSet.as_router())
router_json.register(routes.VehicleJsonRouteSet.as_router())
router_related = BazisRouter(tags=['Related'])
router_related.register(routes.VehicleRelatedRouteSet.as_router())
# Dictionary of routers with prefixes
routers_with_prefix = {
'context': router_context,
'json': router_json,
'related': router_related,
}
Main project router.py:
from bazis.core.routing import BazisRouter
router = BazisRouter(prefix='/api/v1')
# Register application modules
router.register('entity.router')
router.register('dynamic.router')
router.register('route_injection.router')
router.register('sparse_fieldsets.router')
Project Configuration
The settings management system is implemented through django.conf.settings. Values can be set either via environment variables or in the admin panel.
Each Bazis application defines a conf.py module containing a Pydantic Settings schema with configuration fields.
Example conf.py:
from pydantic import BaseSettings
class Settings(BaseSettings):
debug: bool = False
secret_key: str
database_url: str
class Config:
env_prefix = 'BS_'
API Features
Data Schema Analysis
OpenAPI Schema
- Primary API analysis (endpoints, attributes) can be obtained from
/api/openapi.json - When making requests as an authenticated user, some fields may be removed or have different access levels
- For each CRUD action, you can request the actual schema:
- Schemas are provided in OpenAPI format
- Available schemas:
schema_list- schema for listing itemsschema_create- schema for creating items{item_id}/schema_retrieve- schema for retrieving specific item{item_id}/schema_update- schema for updating specific item
Field Attributes
required- required fieldstitle- human-readable namedescription- extended field descriptionnullable- when true, field can accept "null" valuereadOnly- field is read-onlywriteOnly- field is write-onlyenum- list of values the field can acceptenumDict- dictionary mapping values to human-readable namesformat- non-standard field formatminLength- minimum allowed string lengthmaxLength- maximum allowed string lengthfilterLabel- field can be used in filtering with specified label
Filtering
The framework provides powerful filtering capabilities through query parameters:
Filter Types
Exact Match:
# Single value
?filter[type]=FIRST
# Multiple values
?filter[type]=FIRST&filter[type]=SECOND
Boolean Values:
# False value
?filter[is_active]=false
# True value (anything except 'false')
?filter[is_active]=true
Range Filters:
Available for numeric and datetime fields with postfixes:
gt- greater thangte- greater than or equallt- less thanlte- less than or equal
Examples:
?filter[dt_created__gte]=2022-05-16
?filter[price__lt]=5.2
Nested Filters:
For fields defined in the relationships block:
- Determine the type of nested object
- Find the nested object schema
- Get fields available for filtering
Example:
# For organization.organization type
?filter[org_owner__tin]=7845612348
Full-Text Search:
Use the $search modifier for full-text search:
# Search in nested relationships (two levels deep)
?filter[facility_operations__fkkos__$search]=test
# Search in root results
?filter[$search]=test
Included Resources (JSON:API)
Bazis implements the JSON:API specification for handling related resources through the included feature, allowing you to fetch related data in a single request.
Error Format
- Expected errors return status code
422 - Error message format follows JSON:API specification
Examples
Basic Route Usage
from django.apps import apps
from bazis.core.routes_abstract.jsonapi import JsonapiRouteBase
from bazis.core.routing import BazisRouter
class VehicleRouteSet(JsonapiRouteBase):
model = apps.get_model('entity.Vehicle')
# All CRUD operations are available by default
router = BazisRouter(tags=['Vehicles'])
router.register(VehicleRouteSet.as_router())
Limiting Fields in Routes
from bazis.core.schemas.fields import SchemaFields
class VehicleRouteSet(JsonapiRouteBase):
model = apps.get_model('entity.Vehicle')
fields = {
None: SchemaFields(
include={
# Specify only needed fields
'gnum': None,
'vehicle_model': None,
'country': None,
},
),
}
Adding Calculated Fields to API
from bazis.core.schemas.fields import SchemaField, SchemaFields
class VehicleRelatedRouteSet(JsonapiRouteBase):
model = apps.get_model('entity.Vehicle')
fields = {
None: SchemaFields(
include={
# FieldRelated - vehicle_capacity
'vehicle_capacity_1': SchemaField(source='vehicle_capacity_1', required=False),
'vehicle_capacity_2': SchemaField(source='vehicle_capacity_2', required=False),
'vehicle_capacity_3': SchemaField(source='vehicle_capacity_3', required=False),
# FieldRelated - brand, multiple fields
'brand_info': SchemaField(source='brand_info', required=False),
# FieldRelated - brand and country
'brand_and_country': SchemaField(source='brand_and_country', required=False),
},
),
}
Working with Admin Panel
from django.contrib import admin
from bazis.core.admin_abstract import DtAdminMixin
@admin.register(VehicleBrand)
class VehicleBrandAdmin(DtAdminMixin, admin.ModelAdmin):
list_display = ('id', 'name')
search_fields = ('name',)
Admin with inline models:
class ChildEntityInline(admin.TabularInline):
model = ParentEntity.child_entities.through
extra = 0
class DependentEntityInline(admin.TabularInline):
model = DependentEntity
extra = 0
@admin.register(ParentEntity)
class ParentEntityAdmin(DtAdminMixin, admin.ModelAdmin):
list_display = ('id', 'name', 'is_active')
list_filter = ('is_active',)
search_fields = ('name', 'description')
inlines = (ChildEntityInline, DependentEntityInline)
Architecture
Package Structure
The bazis package is the core project installed under the bazis.core namespace.
Extension packages are installed under the bazis.contrib namespace.
Application Structure
All files are optional:
app/
├── models_abstract.py # Mixins or abstractions for creating models
├── admin_abstract.py # Mixins for creating admin classes
├── routes_abstract.py # Mixins and base classes for routes
├── models.py # Actual application models
├── admin.py # Admin classes
├── routes.py # Route classes
├── router.py # URL routing
├── schemas.py # Static Pydantic schemas
├── triggers.py # Django-pgtrigger triggers
└── services/
└── services.py # Dependency services
Bazis Models Features
- Every model and mixin must inherit from
bazis.core.models_abstract.InitialBase - The logic of missing Meta class in a model has been changed:
- By default in Django, if a model does not define a Meta class, Meta parameters are considered unset
- Bazis adds default inheritance functionality from all parent classes, meaning if the Meta class is not defined, the model will receive a composite Meta class from all parents in the order of model inheritance
Dynamic Fields
Bazis supports dynamic fields that can be configured at the route level for greater flexibility.
JSON:API
The framework uses the JSON:API specification, which includes:
- Support for nested structures via
included - Multi-level filtering
- Standardized response format
- Relationships between resources
Development
Setting Up Development Environment
# Clone the repository
git clone git@github.com:ecofuture-tech/bazis.git
cd bazis
# Install dependencies
uv sync --dev
# Run tests (from sample directory)
cd sample
pytest ../tests
# Check code
ruff check .
# Format code
ruff format .
Contributing
Contributions are welcome! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
cd sample && pytest ../tests) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please make sure to:
- Write tests for new features
- Update documentation as needed
- Follow the existing code style
- Add your changes to the changelog
License
Apache License 2.0
See LICENSE file for details.
Links
- Bazis Documentation — main repository
- Issue Tracker — report bugs or request features
- Bazis Test Utils — testing utilities
Support
If you have questions or issues:
- Check the documentation
- Search existing issues
- Create a new issue with detailed information
Ecosystem Packages
Bazis supports extensions through additional packages:
bazis-test-utils— testing utilitiesbazis-<n>— other extensions (addbazis-prefix to the name)
All extension packages require the bazis core package to be installed.
Made with ❤️ by the Bazis team
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 bazis-2.2.6.tar.gz.
File metadata
- Download URL: bazis-2.2.6.tar.gz
- Upload date:
- Size: 299.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09a7cbc09c4a17fd28fab59e38029ae83de009c5b93f51fb54754e529a3bc07a
|
|
| MD5 |
d98e589b046def8b7ae27b54fb5ea640
|
|
| BLAKE2b-256 |
7c43f6f1b91a022096acb918d66d315568610e39b9edf8329ae8e03c06effd30
|
File details
Details for the file bazis-2.2.6-py3-none-any.whl.
File metadata
- Download URL: bazis-2.2.6-py3-none-any.whl
- Upload date:
- Size: 185.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f6d3a6d3e68ba87ed4e96b8f7b4e7d2cc52edfa16c894a34d6571eb67a2960b
|
|
| MD5 |
6a6744f2d7c58c0e895a9a8d0cddeb28
|
|
| BLAKE2b-256 |
440ff3a6b1f2512dc58d30bb503ed7b77a0c81c73d923cd830efcdf13eb105b8
|