• Stars
    star
    119
  • Rank 297,930 (Top 6 %)
  • Language
    Python
  • License
    MIT License
  • Created over 2 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Asynchronous ORM that uses pydantic models to represent database tables โœจ

Logo

Asynchronous ORM that uses pydantic models to represent database tables โœจ

Test Package version Supported Python versions

Ormdantic is a library for interacting with Asynchronous SQL databases from Python code, with Python objects. It is designed to be intuitive, easy to use, compatible, and robust.

Ormdantic is based on Pypika, and powered by Pydantic and SQLAlchemy, and Highly inspired by Sqlmodel, Created by @tiangolo.

What is Pypika?

PyPika is a Python API for building SQL queries. The motivation behind PyPika is to provide a simple interface for building SQL queries without limiting the flexibility of handwritten SQL. Designed with data analysis in mind, PyPika leverages the builder design pattern to construct queries to avoid messy string formatting and concatenation. It is also easily extended to take full advantage of specific features of SQL database vendors.

The key features are:

  • Easy to use: It has sensible defaults and does a lot of work underneath to simplify the code you write.
  • Compatible: It combines SQLAlchemy, Pydantic and Pypika tries to simplify the code you write as much as possible, allowing you to reduce the code duplication to a minimum, but while getting the best developer experience possible.
  • Extensible: You have all the power of SQLAlchemy and Pypika underneath.
  • Short Queries: You can write queries in a single line of code, and it will be converted to the appropriate syntax for the database you are using.

Requirements

A recent and currently supported version of Python (right now, Python supports versions 3.10 and above).

As Ormdantic is based on Pydantic and SQLAlchemy and Pypika, it requires them. They will be automatically installed when you install Ormdantic.

Installation

You can add Ormdantic in a few easy steps. First of all, install the dependency:

$ pip install ormdantic

---> 100%

Successfully installed Ormdantic
  • Install The specific Asynchronous ORM library for your database.
# PostgreSQL
$ pip install ormdantic[postgres]

# SQLite
$ pip install ormdantic[sqlite]

Example

To understand SQL, Sebastian the Creator of FastAPI and SQLModel created an amazing documentation that could help you understand the basics of SQL, ex. CREATE TABLE, INSERT, SELECT, UPDATE, DELETE, etc.

Check out the documentation.

But let's see how to use Ormdantic.

Create SQLAlchemy engine

Ormdantic uses SQLAlchemy under hood to run different queries, which is why we need to initialize by creating an asynchronous engine.

Note: You will use the connection parameter to pass the connection to the engine directly.

from ormdantic import Ormdantic

connection = "sqlite+aiosqlite:///db.sqlite3"

database = Ormdantic(connection)

Note: You can use any asynchronous engine, check out the documentation for more information.

Create a table

To create tables decorate a pydantic model with the database.table decorator, passing the database information ex. Primary key, foreign keys, Indexes, back_references, unique_constraints etc. to the decorator call.

Table Restrictions

  • Tables must have a single column primary key.
  • The primary key column must be the first column.
  • Relationships must union-type the foreign model and that models primary key.
from uuid import uuid4
from pydantic import BaseModel, Field

@database.table(pk="id", indexed=["name"])
class Flavor(BaseModel):
     """A coffee flavor."""

     id: UUID = Field(default_factory=uuid4)
     name: str = Field(max_length=63)

Queries

Now after we create the table, we can initialize the database with the table and then run different queries.

Init()

  • Register models as ORM models and initialize the database.

We use database.init will Populate relations information and create the tables.

async def demo() -> None:
    async def _init() -> None:
        async with db._engine.begin() as conn:
            await db.init()
            await conn.run_sync(db._metadata.drop_all)
            await conn.run_sync(db._metadata.create_all)
    await _init()

Insert

Now let's imagine we have another table called Coffee that has a foreign key to Flavor.

@database.table(pk="id")
class Coffee(BaseModel):
     """Drink it in the morning."""

     id: UUID = Field(default_factory=uuid4)
     sweetener: str | None = Field(max_length=63)
     sweetener_count: int | None = None
     flavor: Flavor | UUID

After we create the table, we can insert data into the table, using the database.insert method, is away we insert a Model Instance.

# Create a Flavor called "Vanilla"
vanilla = Flavor(name="Vanilla")

# Insert the Flavor into the database
await database[Flavor].insert(vanilla)

# Create a Coffee with the Vanilla Flavor
coffee = Coffee(sweetener="Sugar", sweetener_count=1, flavor=vanilla)

# Insert the Coffee into the database
await database[Coffee].insert(coffee)

Searching Queries

As we know, in SQL, we can search for data using different methods, ex. WHERE, LIKE, IN, BETWEEN, etc.

In Ormdantic, we can search for data using the database.find_one or database.find_many methods.

  • Find_one used to find a Model instance by Primary Key, its could also find with depth parameter.
     # Find one
     vanilla = await database[Flavor].find_one(flavor.id)
     print(vanilla.name)

     # Find one with depth.
     find_coffee = await database[Coffee].find_one(coffee.id, depth=1)
     print(find_coffee.flavor.name)
  • Find_many used to find Model instances by some condition ex. where, order_by, order, limit, offset, depth.
     # Find many
     await database[Flavor].find_many()

     # Get paginated results.
     await database[Flavor].find_many(
          where={"name": "vanilla"}, order_by=["id", "name"], limit=2, offset=2
     )

Update / Upsert Queries

Update

The modification of data that is already in the database is referred to as updating. You can update individual rows, all the rows in a table, or a subset of all rows. Each column can be updated separately; the other columns are not affected.

     # Update a Flavor
     flavor.name = "caramel"
     await database[Flavor].update(flavor)
Upsert

The Upsert method is similar to the Synchronize method with one exception; the Upsert method does not delete any records. The Upsert method will result in insert or update operations. If the record exists, it will be updated. If the record does not exist, it will be inserted.

     # Upsert a Flavor
     flavor.name = "mocha"
     await database[Flavor].upsert(flavor)

Delete

The DELETE statement is used to delete existing records in a table.

     # Delete a Flavor
     await database[Flavor].delete(flavor.id)

Count

To count the number of rows of a table or in a result set you can use the count function.

     # Count
     count = await database[Flavor].count()
     print(count)
  • It's support also Where and Depth
     count_advanced = await database[Coffee].count(
          where={"sweetener": 2}, depth=1
     )
     print(count_advanced)

Generator Feature

We introduce a new feature called Generator, which is a way to generate a Model instance with random data.

So, Given a Pydantic model type can generate instances of that model with randomly generated values.

using ormdantic.generator.Generator to generate a Model instance.

from enum import auto, Enum
from uuid import UUID

from ormdantic.generator import Generator
from pydantic import BaseModel


class Flavor(Enum):
    MOCHA = auto()
    VANILLA = auto()


class Brand(BaseModel):
    brand_name: str


class Coffee(BaseModel):
    id: UUID
    description: str
    cream: bool
    sweetener: int
    flavor: Flavor
    brand: Brand


print(Generator(Coffee))

so the results will be:

id=UUID('93b517c2-083b-457d-a0e5-6e1bd2a927e4')
description='ctWOb' cream=True sweetener=234
flavor=<Flavor.VANILLA: 2> brand=Brand(brand_name='LMrIf')

We can integrate this with our database while testing our application (Live Tests).

Development ๐Ÿšง

Setup environment ๐Ÿ“ฆ

You should create a virtual environment and activate it:

python -m venv venv/
source venv/bin/activate

And then install the development dependencies:

# Install dependencies
pip install -e .[sqlite,postgresql,test,lint,docs]

Run tests ๐ŸŒ

You can run all the tests with:

bash scripts/test.sh

Note: You can also generate a coverage report with:

bash scripts/test_html.sh

Format the code ๐Ÿ‚

Execute the following command to apply pre-commit formatting:

bash scripts/format.sh

Execute the following command to apply mypy type checking:

bash scripts/lint.sh

License

This project is licensed under the terms of the MIT license.

More Repositories

1

authx

Ready-to-use and customizable Authentications and Oauth2 management for FastAPI โœจ
Python
554
star
2

DogeAPI

API with high performance to create a simple blog and Auth using OAuth2 โ›
Python
120
star
3

fastapi-lazy

Lazy package to start your project using FastAPIโœจ
Python
99
star
4

SoIG

OSINT Tool gets a range of information from an Instagram account ๐Ÿ› 
Python
89
star
5

fastapi-class

provides a class-based View decorator to help reduce the amount of boilerplate necessary when developing related routes. โœจ๐Ÿš€
Python
75
star
6

FRDP

Boilerplate code for quick docker implementation of REST API with JWT Authentication using FastAPI, PostgreSQL and PgAdmin โญ
Python
60
star
7

pyngo

Pydantic model support for Django & Django-Rest-Framework โœจ
Python
58
star
8

My-Business

Business management tool featuring accounts, invoices, partners, projects, and server ๐Ÿฆ„
Python
44
star
9

Athena

Test your Security Skills, and Clean Code Development as a Pythonist, Hacker & Warrior ๐Ÿฅท๐Ÿป
Python
43
star
10

fastapi-tag

Documented & Enhance the Metadata of your API โœจ
Python
37
star
11

JeffQL

fastAPI Simple authentication & Login API using GraphQL and JWT ๐Ÿš€
Python
28
star
12

yezz.me

My personal website ๐Ÿš€
JavaScript
25
star
13

asgi-aws

Build API with ASGI in AWS Lambda with API Gateway HTTP API or REST API, or with Function URL โœจ
Python
24
star
14

yezz123.github.io

My personal website ๐Ÿš€
JavaScript
23
star
15

CoveAPI

OpenAPI-based test coverage analysis tool that helps teams improve integration test coverage in CI/CD pipelines
Rust
18
star
16

VSCode-Sidebar-Themes

VSCode sidebar themes Website where you can copy & share your Sidebar Themes.
TypeScript
18
star
17

Blog

A Next.js, Tailwind CSS blogging Template. Used to Create my Blog ๐Ÿš€
JavaScript
17
star
18

Parser

Pure Python standard library JSON ๐Ÿฆ„
Python
12
star
19

pagidantic

Pagination using Pydantic. Easy to use, lightweight, and easy to integrate with existing projects ๐Ÿ’ก
Python
10
star
20

yezz123

My official Github Configuration ๐Ÿš€
10
star
21

yasgi

yasgi is a tiny ASGI framework โœจ
Python
7
star
22

stripe-template

Template for integrating stripe into your FastAPI application ๐Ÿ’ธ
Python
5
star
23

pg-dummy

A simple Script Help to insert Some Dummy Data to Populate Database For Testing
Python
3
star
24

RustyServer

A simple and minimalistic HTTP server for Rust
Rust
3
star
25

pycontent-type

A Python library to access all Supported Content-Types/Media-Types
Python
2
star
26

pypi-python-template

Quickly creating a new Modern Python project and publishing it to PyPI โœจ
Python
1
star
27

spotify-preview

a simple bootstrapped application to show my own playlists
TypeScript
1
star
28

pyregex

Python bindings for the Rust regex Crate โœจ
1
star
29

AWSFlowUtils

Improve your data workflow with enhanced simplicity and robustness in handling common data tasks โœจ
Python
1
star