Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fernaper/kascade-orm
Python ORM for SQL Databases based on Pydantic
https://github.com/fernaper/kascade-orm
Last synced: 1 day ago
JSON representation
Python ORM for SQL Databases based on Pydantic
- Host: GitHub
- URL: https://github.com/fernaper/kascade-orm
- Owner: fernaper
- Created: 2023-07-21T15:54:24.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2023-07-23T15:52:12.000Z (over 1 year ago)
- Last Synced: 2023-07-23T16:24:30.345Z (over 1 year ago)
- Language: Python
- Size: 15.6 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
Kascade ORM
Python ORM for SQL Databases based on Pydantic
## What is Kascade ORM?
Kascade ORM is the next-geneneration ORM built on top of Pydantic in order to have the best integration with frameworks like FastAPI.
---
## Roadmap
Our plan is to give the users maximum access to SQL Databases inside Python code without making them hard to understand.
To do so, we plan to structure everything in `Objects`.### Notes about Callable:
This util callables could be created directly in SQL or via Python.
If is it possible it is always created on SQL.- cuid: Depends
- uuid: Depends
- random: Depends
- autoincrement: SQL Always
- utcnow: SQL Always
- now: SQL Always
- custom: Python Always### Things to store per column:
- Type: `Type Hint`
- Name: `str`
- Unique: `bool`
- Optional: `bool`
- IsId: `bool`
- Default: `Any` or `Callable`
- OnUpdate: `None` or `Callable`### Things to store per Relation:
- Table1: `Table`
- Table2: `Table`
- Table1Columns: `List[Column]`
- Table2Columns: `List[Column]`### Things to store per Table:
- Columns: `List[Column]`
- CompoundUniques: `List[List[Column]]`
- Indexes: `List[Column]`
- Relations: `List[Relation]`---
## Extra features planned to be added
1. If we have two tables we plan to substract them in order to detect differences between them. This whay we can easily manage `apply`s to update the tables.
2. Users should be capable to create fast Type Hints from their tables in order to allow returning for example an `User` without the `password` in FastAPI without needing to create a custom Schema that is just a duplication of the `User` schema without this field.
3. Important: Allow to generate the Python file with the current Database schema.## This is an example of how we plan to create tables
```python
from pydantic import EmailStr
from kascade import ForeignKey, Table, Column, column_defaultsclass Item(Table):
# Note that if it is called ID and is an int,
# this configuration is equivalente to the
# `User` table configuration
id: int
name: str
user_id: intclass User(Table):
name: str
email: EmailStr
password: str
id: Column = Column(
type=int,
unique=True,
default=column_defaults.autoincrement,
)
avatar: Optional[bytes] = None
items: ForeignKey = ForeignKey(
table=Item,
column='user_id',
)
```This is just an idea, we could change it. Also, we are still thinking on the best way to manage relationships.
On this example the generated Python code (internally) will look similar to:
```python
from pydantic import EmailStr
from kascade import ForeignKey, Table, Column, column_defaultsclass Item(Table):
id: int
name: str
user_id: int@property
def user(self):
# Code to get user dynamically (or explicitly)
passclass User(Table):
name: str
email: EmailStr
password: str
id: int
avatar: Optional[bytes] = None
@property
def items(self):
# Code to get items dynamically (or explicitly)
pass```
This class will have also other methods based on Table ones.
---
Also, this is an example on how an end user will use this tables:
```python
import asynciofrom kascade import Kascade
async def main():
async with Kascade() as db:
user = await db.User.create({
name='Fernando Pérez',
email='[email protected]',
password='super-secure-kascade-password',
})item = await db.Item.create({
name='Laptop',
# Only one of the following is needed
user_id=user.id,
user=user,
})all_users_with_avatar = await db.User.find_many({
'where': {
'avatar': {
'not': None,
}
}
})# In this case we know that for some reasson we need
# to query all items for each user,
# Therefore, in order to improve performance and
# avoid unexpected loads when querying items from
# each user with the code: all_kascade_emails[0].items
all_kascade_emails = await db.User.find_many({
'where': {
'email': {
'ends_with': '@kascade.com',
}
}
'include': {
'items': True,
}
})# Example on how to define Type Hints
def item_example() -> Kascade.Item:
pass# Example on how to skip some parameters
def user_example() -> kascade.User.exclude('password'):
passif __name__ == '__main__':
asyncio.run(main())```
In order to suppor include or exclude we are going to solve it in a way similar to:
```python
from typing import get_type_hints
from pydantic import BaseModel, create_model# `create_model` <--- this is the solution
# https://chat.openai.com/share/cfa989a8-7ac4-4508-abd8-f39ca5a17602class Table(BaseModel):
a: int = 1
b: int = 2@classmethod
def exclude(cls, *field_names):
annotations = get_type_hints(cls)
field_names = set(field_names)
for field_name in field_names:
annotations.pop(field_name, None)
fields_dict = cls.model_fields
new_fields = {
name: field for name, field in fields_dict.items() if name not in field_names
}
namespace = {'__annotations__': annotations}
new_class = type(cls.__name__, (BaseModel,), namespace)
new_class.__fields__ = new_fields
return new_class
```