An open API service indexing awesome lists of open source software.

https://github.com/dan1elt0m/sadel

SQLModel helper class for upserting records.
https://github.com/dan1elt0m/sadel

alembic asyncio database pydantic sqlalchemy sqlmodel upsert

Last synced: about 1 month ago
JSON representation

SQLModel helper class for upserting records.

Awesome Lists containing this project

README

          

[![CodeQL](https://github.com/dan1elt0m/sadel/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/dan1elt0m/sadel/actions/workflows/codeql-analysis.yml)
[![Dependabot Updates](https://github.com/dan1elt0m/sadel/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/dan1elt0m/sadel/actions/workflows/dependabot/dependabot-updates)
[![test](https://github.com/dan1elt0m/sadel/actions/workflows/test.yml/badge.svg)](https://github.com/dan1elt0m/sadel/actions/workflows/test.yml)
[![codecov](https://codecov.io/github/dan1elt0m/sadel/graph/badge.svg?token=NECZRE656C)](https://codecov.io/github/dan1elt0m/sadel)
![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fdan1elt0m%2Fsadel%2Fmain%2Fpyproject.toml)

# Sadel

Sadel is a helper class for upserting records with [SQLModel](https://sqlmodel.tiangolo.com/).

### Installation
```bash
pip install sadel
```

#### Example upsert
```python
from sadel import Sadel
from sqlalchemy.ext.asyncio import create_async_engine
from sqlmodel import Field, create_engine, select, or_
from sqlmodel.ext.asyncio.session import AsyncSession

class Hero(Sadel, table=True):
__tablename__ = "hero"
_upsert_index_elements = {"id"}

id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None

# Create
sqlite_url_async = "sqlite+aiosqlite:///database.db"
async_engine = create_async_engine(sqlite_url_async, echo=True, future=True)

hero = Hero(name="Deadpond", secret_name="Dive Wilson")

async with AsyncSession(async_engine) as session:
# Upsert the record
await Hero.upsert(hero, session)

# Fetch the upserted record
result = (
(await session.exec(select(Hero).where(Hero.name == "Deadpond")))
.all()
)

print(result)
```
Output:
```text
[Hero(id=1, name='Deadpond', secret_name='Dive Wilson', age=None, created_on=datetime.datetime(2024, 8, 1, 19, 39, 7), modified_on=None)]
```
### Example batch upsert
```python
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

async with AsyncSession(async_engine) as session:
await Hero.batch_upsert([hero_1, hero_2, hero_3], session)
result = (
(
await session.exec(
select(Hero).where(or_(Hero.name == "Deadpond", Hero.name == "Spider-Boy", Hero.name == "Rusty-Man"))
)
)
.all()
)
print(result)
```
Output:
```text
[Hero(id=1, name='Deadpond', secret_name='Dive Wilson',age=None, created_on=datetime.datetime(2024, 8, 1, 19, 39, 7), modified_on=None),
Hero(id=2, name='Spider-Boy", secret_name='Pedro Parqueador',age=None, created_on=datetime.datetime(2024, 8, 1, 19, 39, 7), modified_on=None),
Hero(id=3, name='Rusty-Man', secret_name='Tommy Sharp', age=48, created_on=datetime.datetime(2024, 8, 1, 19, 39, 7), modified_on=None)]
```

### Example update record
```python
async with AsyncSession(async_engine) as session:
# Upsert the record
hero = Hero(name="Deadpond", secret_name="Dive Wilson", age=25)
await Hero.upsert(hero, session)

# Update the record
hero.age = 30
# Upsert the updated record
await Hero.upsert(hero, session)

# Fetch the updated record
result = (
(await session.exec(select(Hero).where(Hero.name == "Deadpond")))
.scalars()
.all()
)

print(result)
```
Output:
```text
[Hero(id=1, name='Deadpond', secret_name='Dive Wilson', age=30, created_on=datetime.datetime(2024, 8, 1, 19, 39, 7), modified_on=datetime.datetime(2024, 8, 1, 19, 39, 8))]
```

### Features
- Upsert and batch_upsert functions.
- For auditing, automatically adds and manages `created_on` and `modified_on` columns to your table (timezones are supported).
- Validates your data before upserting using Pydantic validate_model method (not supported in SQLModel)
- Asyncio
- Compatible with Alembic
- Specify the (PK) columns to use for upserting using `_upsert_index_elements` attribute
- Ignore specific columns from updating using `_upsert_exclude_fields` attribute

### Contributing
- Fork the repository
- Create a new branch
- Make your changes
- Raise a PR

### License
This project is licensed under the terms of the [MIT License](LICENSE)