Metadata-Version: 2.1
Name: saffier
Version: 0.2.1
Summary: The only python ORM you will ever need.
Project-URL: Homepage, https://github.com/tarsil/saffier
Project-URL: Documentation, https://saffier.tarsild.io/
Project-URL: Changelog, https://saffier.tarsild.io/release-notes/
Project-URL: Funding, https://github.com/sponsors/tarsil
Project-URL: Source, https://github.com/tarsil/saffier
Author-email: Tiago Silva <tiago.arasilva@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: api,asgi,fastapi,framework,http,machine learning,ml,openapi,pydantic,rest,starlette,websocket
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: AnyIO
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Internet
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.8
Requires-Dist: databases>=0.7.0
Requires-Dist: orjson<4.0.0,>=3.8.5
Requires-Dist: pydantic<2.0.0,>=1.10.4
Requires-Dist: sqlalchemy==1.4.46
Provides-Extra: all
Requires-Dist: databases[mysql,postgresql,sqlite]; extra == 'all'
Requires-Dist: orjson<4.0.0,>=3.8.5; extra == 'all'
Requires-Dist: pydantic<2.0.0,>=1.10.4; extra == 'all'
Provides-Extra: dev
Requires-Dist: autoflake>=1.4.0; extra == 'dev'
Requires-Dist: flake8>=5.0.4; extra == 'dev'
Requires-Dist: loguru<0.7.0,>=0.6.0; extra == 'dev'
Requires-Dist: pre-commit<4.0.0,>=2.17.0; extra == 'dev'
Requires-Dist: uvicorn[standard]>=0.19.0; extra == 'dev'
Requires-Dist: watchfiles<0.20.0,>=0.16.1; extra == 'dev'
Provides-Extra: doc
Requires-Dist: mdx-include<2.0.0,>=1.4.1; extra == 'doc'
Requires-Dist: mkautodoc<0.3.0,>=0.2.0; extra == 'doc'
Requires-Dist: mkdocs-markdownextradata-plugin<0.3.0,>=0.1.7; extra == 'doc'
Requires-Dist: mkdocs-material==9.0.12; extra == 'doc'
Requires-Dist: mkdocs<2.0.0,>=1.4.2; extra == 'doc'
Requires-Dist: mkdocstrings<0.21.0,>=0.19.0; extra == 'doc'
Requires-Dist: pyyaml<7.0.0,>=5.3.1; extra == 'doc'
Provides-Extra: mysql
Requires-Dist: databases[mysql]>=0.7.0; extra == 'mysql'
Provides-Extra: postgres
Requires-Dist: databases[postgresql]>=0.7.0; extra == 'postgres'
Provides-Extra: sqlite
Requires-Dist: databases[sqlite]>=0.7.0; extra == 'sqlite'
Provides-Extra: test
Requires-Dist: anyio<4,>=3.0.0; extra == 'test'
Requires-Dist: asyncpg<1,>=0.27.0; extra == 'test'
Requires-Dist: black==23.1.0; extra == 'test'
Requires-Dist: flake8>=5.0.4; extra == 'test'
Requires-Dist: freezegun<2.0.0,>=1.2.2; extra == 'test'
Requires-Dist: isort<6.0.0,>=5.0.6; extra == 'test'
Requires-Dist: mock==5.0.1; extra == 'test'
Requires-Dist: mypy==1.0.0; extra == 'test'
Requires-Dist: pydantic==1.10.5; extra == 'test'
Requires-Dist: pytest-asyncio>=0.19.0; extra == 'test'
Requires-Dist: pytest-cov<5.0.0,>=2.12.0; extra == 'test'
Requires-Dist: pytest<8.0.0,>=7.1.3; extra == 'test'
Requires-Dist: types-orjson==3.6.2; extra == 'test'
Description-Content-Type: text/markdown

# Saffier

<p align="center">
  <a href="https://saffier.tarsild.io"><img src="https://res.cloudinary.com/dymmond/image/upload/v1675104815/Saffier/logo/logo_dowatx.png" alt='Saffier'></a>
</p>

<p align="center">
    <em>🚀 The only Async ORM you need. 🚀</em>
</p>

<p align="center">
<a href="https://github.com/tarsil/saffier/workflows/Test%20Suite/badge.svg?event=push&branch=main" target="_blank">
    <img src="https://github.com/tarsil/saffier/workflows/Test%20Suite/badge.svg?event=push&branch=main" alt="Test Suite">
</a>

<a href="https://pypi.org/project/saffier" target="_blank">
    <img src="https://img.shields.io/pypi/v/saffier?color=%2334D058&label=pypi%20package" alt="Package version">
</a>

<a href="https://pypi.org/project/saffier" target="_blank">
    <img src="https://img.shields.io/pypi/pyversions/saffier.svg?color=%2334D058" alt="Supported Python versions">
</a>
</p>

---

**Documentation**: [https://saffier.tarsild.io](https://saffier.tarsild.io) 📚

**Source Code**: [https://github.com/tarsil/saffier](https://github.com/tarsil/saffier)

---

## Motivation

Almost every project, in one way or another uses one (or many) databases. An ORM is simply an mapping
of the top of an existing database. ORM extends for Object Relational Mapping and bridges object-oriented
programs and relational databases.

Two of the most well known ORMs are from Django and SQLAlchemy. Both have their own strenghts and
weaknesses and specific use cases.

This ORM is built on the top of SQLAlchemy core and aims to simplify the way the setup and queries
are done into a more common and familiar interface.

## Why this ORM

When investigating for a project different types of ORMs and compared them to each other, for a lot
of use cases, SQL Alchemy always took the win but had an issue, the async support (which now there
are a few solutions). While doing the research I came across [Encode ORM](https://www.encode.io/orm/).

The team is the same behind of Databases, Django Rest Framework, Starlette,
httpx and a lot more tools used by millions.

There was one issue thou, although ORM was doing a great familiar interface with SQL Alchemy and
providing the async solution needed, it was, by the time of this writting, incomplete and they
even stated that in the documentation and that is how **Saffier** was born.

Saffier uses some of the same concepts of ORM from Encode but rewritten in **Pydantic** but not all.

## Saffier

Saffier is some sort of a fork from [Encode ORM](https://www.encode.io/orm/) but rewritten at its
core and with a complete set of tools with a familiar interface to work with. 
If you are familiar with Django, then you came for a treat 😄.

This was necessary because the team behind [Encode ORM](https://www.encode.io/orm/) is extremely
busy and could not dedicate as much time as they would like to the project so I decided to continue
and improve.

Saffier leverages the power of **Pydantic** for its fields while offering a friendly, familiar and
easy to use interface.

This ORM was designed to be flexible and compatible with pretty much every ASGI framework, like
[Esmerald](https://esmerald.dymmond.com), Starlette, FastAPI, Sanic, Quart... With simple pluggable
design thanks to its origins.

## Features

While adopting a familiar interface, it offers some cool and powerful features on the top of
SQL Alchemy core.

### Key features

* **Model inheritance** - For those cases where you don't want to repeat yourself while maintaining
intregity of the models.
* **Abstract classes** - That's right! Sometimes you simply want a model that holds common fields
that doesn't need to created as a table in the database.
* **Meta classes** - If you are familiar with Django, this is not new to you and Saffier offers this
in the same fashion.
* **Managers** - Versatility at its core, you can have separate managers for your models to optimise
specific queries and querysets at ease.
* **Filters** - Filter by any field you want and need.
* **Model operators** - Classic operations such as `update`, `get`, `get_or_none`, `bulk_create`,
`bulk_update` and a lot more.
* **Relationships made it easy** - Support for `OneToOne` and `ForeignKey` in the same Django style.
* **Constraints** - Unique constraints through meta fields.

And a lot more you can do here.

## Migrations

Since **Saffier**, like [Encode ORM](https://www.encode.io/orm/), is built on the top of 
[SQLAlchemy core](https://docs.sqlalchemy.org/en/20/core/), you cam use the widely known
[Alembic](https://alembic.sqlalchemy.org/en/latest/) to manage the migrations for you.

## Installation

To install Saffier, simply run:

```shell
$ pip install saffier
```

You can pickup your favourite database driver by yourself or you can run:

**Postgres**

```shell
$ pip install saffier[postgres]
```

**MySQL/MariaDB**

```shell
$ pip install saffier[mysql]
```

**SQLite**

```shell
$ pip install saffier[sqlite]
```

## Quick Start

The following is an example how to start with **Saffier** and more details and examples can be
found throughout the documentation.

**Use** `ipython` **to run the following from the console, since it supports** `await`.

```python
import saffier
from saffier import Database, Registry

database = Database("sqlite:///db.sqlite")
models = Registry(database=database)


class User(saffier.Model):
    """
    The User model to be created in the database as a table
    If no name is provided the in Meta class, it will generate
    a "users" table for you.
    """

    id = saffier.IntegerField(primary_key=True)
    is_active = saffier.BooleanField(default=False)

    class Meta:
        registry = models


# Create the db and tables
# Don't use this in production! Use Alembic or any tool to manage
# The migrations for you
await models.create_all()

await User.query.create(is_active=False)

user = await User.query.get(id=1)
print(user)
# User(id=1)

```

As stated in the example, if no `tablename` is provided in the `Meta` class, Saffier automatically
generates the name of the table for you by pluralising the class name.

**Exciting!** 

In the documentation we go deeper in explanations and examples, this was just to warm up. 😁
