<div align="center">
<h1>BetterCRUD</h1>
</div>
<p align="center" markdown=1>
<i>A better CRUD library for FastAPI.</i></br>
<sub>FastAPI CRUD routing library based on class view, you can control everything</sub>
</p>
<p align="center" markdown=1>
<a href="https://github.com/bigrivi/better_crud/actions/workflows/pytest.yml" target="_blank">
<img src="https://github.com/bigrivi/better_crud/actions/workflows/pytest.yml/badge.svg" alt="Tests"/>
</a>
<a href="https://pypi.org/project/better_crud/" target="_blank">
<img src="https://img.shields.io/pypi/v/better_crud?color=%2334D058&label=pypi%20package" alt="PyPi Version"/>
</a>
<a href="https://pypi.org/project/better_crud/" target="_blank">
<img src="https://img.shields.io/pypi/pyversions/better_crud.svg?color=%2334D058" alt="Supported Python Versions"/>
</a>
<a href="https://codecov.io/github/bigrivi/better_crud" target="_blank">
<img src="https://codecov.io/github/bigrivi/better_crud/graph/badge.svg?token=MEMUT1FH4K"/>
</a>
</p>
## Requirements
- **Python:** Version 3.9 or newer.
- **FastAPI:** BetterCRUD is built to work with FastAPI, so having FastAPI in your project is essential.
- <b>SQLAlchemy:</b> Version 2.0.30 or newer. BetterCRUD uses SQLAlchemy for database operations.
- <b>Pydantic:</b> Version 2.7.3 or newer. BetterCRUD leverages Pydantic models for data validation and serialization.
## Features
- Fully Async, Synchronization is not supported
- Less boilerplate code
- Configuring static type support
- More flexible custom configuration,Less invasive
- Compatible with both class views and functional views
- Rich filter, paging, and sorting support
- Automated relationship support, query, auto-build and update
- Extensible custom backend
## Default Routes
| Route | Method | Description |
| -------------------- | ---------- | ----------- |
| /resource | **GET** | Get Many |
| /resource/{id} | **GET** | Get One |
| /resource | **POST** | Create One |
| /resource/bulk | **POST** | Create Many |
| /resource | **PUT** | Update One |
| /resource/{ids}/bulk | **PUT** | Update Many |
| /resource/{ids} | **DELETE** | Delete Many |
## Installation
```bash
pip install better-crud
```
## Minimal Example
Prerequisites,Prepare our db, Only asynchronous mode is supported,aiomysql or aiosqlite
**db.py**
```python
from sqlalchemy.orm import DeclarativeBase, declared_attr
from typing import AsyncGenerator
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import NullPool
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
DATABASE_URL = "sqlite+aiosqlite:///crud.db"
class MappedBase(DeclarativeBase):
@declared_attr.directive
def __tablename__(cls) -> str:
return cls.__name__.lower()
class Base(MappedBase):
__abstract__ = True
engine = create_async_engine(
DATABASE_URL,
echo=False,
poolclass=NullPool
)
SessionLocal = sessionmaker(
autocommit=False,
autoflush=False,
bind=engine,
class_=AsyncSession,
expire_on_commit=False,
)
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async with SessionLocal() as session:
yield session
async def init_db():
async with engine.begin() as conn:
await conn.run_sync(MappedBase.metadata.create_all)
```
First Define Your Model And Schema
**model.py**
```python
from sqlalchemy import String, Integer, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from .db import Base
class Pet(Base):
__tablename__ = "pet"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(100))
description: Mapped[str] = mapped_column(String(100))
```
**schema.py**
```python
from typing import Optional, List
from pydantic import BaseModel
class PetBase(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
class PetPublic(PetBase):
id: int
class PetCreate(PetBase):
pass
class PetUpdate(PetBase):
pass
```
Next we need to create a service:
**service.py**
```python
from better_crud.service.sqlalchemy import SqlalchemyCrudService
from .model import Pet
class PetService(SqlalchemyCrudService[Pet]):
def __init__(self):
super().__init__(Pet)
```
Next we need to define the controller and decorate it with the crud decorator
Sure the controller is just a normal class,The crud decorator gives it super powers
**controller.py**
```python
from fastapi import APIRouter, Depends
from better_crud import crud
from .schema import PetCreate, PetUpdate, PetPublic
from .service import PetService
pet_router = APIRouter()
@crud(
pet_router,
dto={
"create": PetCreate,
"update": PetUpdate
},
serialize={
"base": PetPublic,
}
)
class PetController():
service: PetService = Depends(PetService)
```
Next we can register router to the fastapi routing system
**main.py**
```python
from better_crud import BetterCrudGlobalConfig
from fastapi import FastAPI
from contextlib import asynccontextmanager
from .db import get_session, init_db
BetterCrudGlobalConfig.init(
backend_config={
"sqlalchemy": {
"db_session": get_session
}
}
)
@asynccontextmanager
async def lifespan(_: FastAPI):
await init_db()
# Shutdown
yield
app = FastAPI(lifespan=lifespan)
def register_router():
from app.controller import pet_router
app.include_router(pet_router, prefix="/pet")
register_router()
```
Congratulations, your first CRUD route has been created!

## Author
👤 **bigrivi**
* GitHub: [bigrivi](https://github.com/bigrivi)
## 🤝 Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
Don't forget to give the project a star! Thanks again!
1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
## License
[MIT](LICENSE)
Raw data
{
"_id": null,
"home_page": "https://github.com/bigrivi/better_crud",
"name": "better-crud",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "fastapi, crud, async, sqlalchemy, pydantic",
"author": "bigrivi",
"author_email": "sunjianghong@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/4a/e5/12807fcd1f19ab4eaa9c9c99efc82d2b2d102d0ed8b90cf091370a4bcae9/better_crud-0.0.6.tar.gz",
"platform": null,
"description": "<div align=\"center\">\n <h1>BetterCRUD</h1>\n</div>\n<p align=\"center\" markdown=1>\n <i>A better CRUD library for FastAPI.</i></br>\n <sub>FastAPI CRUD routing library based on class view, you can control everything</sub>\n</p>\n<p align=\"center\" markdown=1>\n<a href=\"https://github.com/bigrivi/better_crud/actions/workflows/pytest.yml\" target=\"_blank\">\n <img src=\"https://github.com/bigrivi/better_crud/actions/workflows/pytest.yml/badge.svg\" alt=\"Tests\"/>\n</a>\n<a href=\"https://pypi.org/project/better_crud/\" target=\"_blank\">\n <img src=\"https://img.shields.io/pypi/v/better_crud?color=%2334D058&label=pypi%20package\" alt=\"PyPi Version\"/>\n</a>\n<a href=\"https://pypi.org/project/better_crud/\" target=\"_blank\">\n <img src=\"https://img.shields.io/pypi/pyversions/better_crud.svg?color=%2334D058\" alt=\"Supported Python Versions\"/>\n</a>\n<a href=\"https://codecov.io/github/bigrivi/better_crud\" target=\"_blank\">\n <img src=\"https://codecov.io/github/bigrivi/better_crud/graph/badge.svg?token=MEMUT1FH4K\"/>\n </a>\n</p>\n\n\n## Requirements\n- **Python:** Version 3.9 or newer.\n- **FastAPI:** BetterCRUD is built to work with FastAPI, so having FastAPI in your project is essential.\n- <b>SQLAlchemy:</b> Version 2.0.30 or newer. BetterCRUD uses SQLAlchemy for database operations.\n- <b>Pydantic:</b> Version 2.7.3 or newer. BetterCRUD leverages Pydantic models for data validation and serialization.\n\n## Features\n- Fully Async, Synchronization is not supported\n- Less boilerplate code\n- Configuring static type support\n- More flexible custom configuration\uff0cLess invasive\n- Compatible with both class views and functional views\n- Rich filter, paging, and sorting support\n- Automated relationship support, query, auto-build and update\n- Extensible custom backend\n\n\n\n\n\n## Default Routes\n\n| Route | Method | Description |\n| -------------------- | ---------- | ----------- |\n| /resource | **GET** | Get Many |\n| /resource/{id} | **GET** | Get One |\n| /resource | **POST** | Create One |\n| /resource/bulk | **POST** | Create Many |\n| /resource | **PUT** | Update One |\n| /resource/{ids}/bulk | **PUT** | Update Many |\n| /resource/{ids} | **DELETE** | Delete Many |\n\n\n## Installation\n```bash\npip install better-crud\n```\n\n## Minimal Example\n\nPrerequisites,Prepare our db, Only asynchronous mode is supported,aiomysql or aiosqlite\n**db.py**\n```python\nfrom sqlalchemy.orm import DeclarativeBase, declared_attr\nfrom typing import AsyncGenerator\nfrom sqlalchemy.orm import sessionmaker\nfrom sqlalchemy.pool import NullPool\nfrom sqlalchemy.ext.asyncio import AsyncSession, create_async_engine\nDATABASE_URL = \"sqlite+aiosqlite:///crud.db\"\n\nclass MappedBase(DeclarativeBase):\n @declared_attr.directive\n def __tablename__(cls) -> str:\n return cls.__name__.lower()\n\n\nclass Base(MappedBase):\n __abstract__ = True\n\n\nengine = create_async_engine(\n DATABASE_URL,\n echo=False,\n poolclass=NullPool\n)\n\nSessionLocal = sessionmaker(\n autocommit=False,\n autoflush=False,\n bind=engine,\n class_=AsyncSession,\n expire_on_commit=False,\n)\n\n\nasync def get_session() -> AsyncGenerator[AsyncSession, None]:\n async with SessionLocal() as session:\n yield session\n\n\nasync def init_db():\n async with engine.begin() as conn:\n await conn.run_sync(MappedBase.metadata.create_all)\n```\n\nFirst Define Your Model And Schema\n\n**model.py**\n```python\nfrom sqlalchemy import String, Integer, ForeignKey\nfrom sqlalchemy.orm import Mapped, mapped_column\nfrom .db import Base\n\n\nclass Pet(Base):\n __tablename__ = \"pet\"\n id: Mapped[int] = mapped_column(Integer, primary_key=True)\n name: Mapped[str] = mapped_column(String(100))\n description: Mapped[str] = mapped_column(String(100))\n\n```\n\n**schema.py**\n```python\nfrom typing import Optional, List\nfrom pydantic import BaseModel\n\n\nclass PetBase(BaseModel):\n name: Optional[str] = None\n description: Optional[str] = None\n\n\nclass PetPublic(PetBase):\n id: int\n\n\nclass PetCreate(PetBase):\n pass\n\n\nclass PetUpdate(PetBase):\n pass\n\n```\n\nNext we need to create a service:\n\n**service.py**\n```python\nfrom better_crud.service.sqlalchemy import SqlalchemyCrudService\nfrom .model import Pet\n\n\nclass PetService(SqlalchemyCrudService[Pet]):\n def __init__(self):\n super().__init__(Pet)\n\n```\n\nNext we need to define the controller and decorate it with the crud decorator\nSure the controller is just a normal class,The crud decorator gives it super powers\n**controller.py**\n```python\nfrom fastapi import APIRouter, Depends\nfrom better_crud import crud\nfrom .schema import PetCreate, PetUpdate, PetPublic\nfrom .service import PetService\n\npet_router = APIRouter()\n\n\n@crud(\n pet_router,\n dto={\n \"create\": PetCreate,\n \"update\": PetUpdate\n },\n serialize={\n \"base\": PetPublic,\n }\n)\nclass PetController():\n service: PetService = Depends(PetService)\n\n```\n\nNext we can register router to the fastapi routing system\n\n**main.py**\n```python\nfrom better_crud import BetterCrudGlobalConfig\nfrom fastapi import FastAPI\nfrom contextlib import asynccontextmanager\nfrom .db import get_session, init_db\n\nBetterCrudGlobalConfig.init(\n backend_config={\n \"sqlalchemy\": {\n \"db_session\": get_session\n }\n }\n)\n\n\n@asynccontextmanager\nasync def lifespan(_: FastAPI):\n await init_db()\n # Shutdown\n yield\n\napp = FastAPI(lifespan=lifespan)\n\n\ndef register_router():\n from app.controller import pet_router\n app.include_router(pet_router, prefix=\"/pet\")\n\n\nregister_router()\n\n\n```\n\nCongratulations, your first CRUD route has been created\uff01\n\n\n\n\n## Author\n\n\ud83d\udc64 **bigrivi**\n* GitHub: [bigrivi](https://github.com/bigrivi)\n\n## \ud83e\udd1d Contributing\n\nContributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.\n\nIf you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag \"enhancement\".\nDon't forget to give the project a star! Thanks again!\n\n1. Fork the Project\n2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the Branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\n## License\n\n[MIT](LICENSE)\n",
"bugtrack_url": null,
"license": "Apache License 2.0",
"summary": "A better CRUD library for FastAPI.",
"version": "0.0.6",
"project_urls": {
"Homepage": "https://github.com/bigrivi/better_crud",
"Source Code": "https://github.com/bigrivi/better_crud"
},
"split_keywords": [
"fastapi",
" crud",
" async",
" sqlalchemy",
" pydantic"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "2c0c6ef9ea8996401e3bf1d6feb8aebcea1e1ceb6746fc406f709b602e21f83a",
"md5": "0006aa8282f432a82a330994090902f6",
"sha256": "e2ce2aa08227f0fedc0cd3f30d8305d30e80abda643d904f409f6cd56b4f5aca"
},
"downloads": -1,
"filename": "better_crud-0.0.6-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0006aa8282f432a82a330994090902f6",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 24746,
"upload_time": "2025-02-16T09:19:40",
"upload_time_iso_8601": "2025-02-16T09:19:40.746310Z",
"url": "https://files.pythonhosted.org/packages/2c/0c/6ef9ea8996401e3bf1d6feb8aebcea1e1ceb6746fc406f709b602e21f83a/better_crud-0.0.6-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "4ae512807fcd1f19ab4eaa9c9c99efc82d2b2d102d0ed8b90cf091370a4bcae9",
"md5": "a492e4be802ec5c99493e5eacd23edd7",
"sha256": "1fdf93dab11826f7613280bb833c499e5707a314bc42308cdaebc25eb3915bb9"
},
"downloads": -1,
"filename": "better_crud-0.0.6.tar.gz",
"has_sig": false,
"md5_digest": "a492e4be802ec5c99493e5eacd23edd7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 21869,
"upload_time": "2025-02-16T09:19:42",
"upload_time_iso_8601": "2025-02-16T09:19:42.822014Z",
"url": "https://files.pythonhosted.org/packages/4a/e5/12807fcd1f19ab4eaa9c9c99efc82d2b2d102d0ed8b90cf091370a4bcae9/better_crud-0.0.6.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-16 09:19:42",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "bigrivi",
"github_project": "better_crud",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"requirements": [
{
"name": "fastapi",
"specs": []
},
{
"name": "sqlalchemy",
"specs": [
[
"==",
"2.0.30"
]
]
},
{
"name": "fastapi_pagination",
"specs": [
[
"==",
"0.12.24"
]
]
},
{
"name": "pydantic",
"specs": [
[
"==",
"2.7.3"
]
]
},
{
"name": "sqlmodel",
"specs": [
[
"==",
"0.0.22"
]
]
},
{
"name": "sqlalchemy_utils",
"specs": [
[
"==",
"0.41.2"
]
]
},
{
"name": "passlib",
"specs": [
[
"==",
"1.7.4"
]
]
},
{
"name": "aiosqlite",
"specs": [
[
"==",
"0.20.0"
]
]
},
{
"name": "httpx",
"specs": []
},
{
"name": "pytest",
"specs": []
},
{
"name": "pytest-asyncio",
"specs": []
},
{
"name": "pytest-cov",
"specs": []
},
{
"name": "pytest-order",
"specs": []
}
],
"lcname": "better-crud"
}