{"id":13501304,"url":"https://github.com/yezz123/ormdantic","last_synced_at":"2025-04-09T19:17:21.221Z","repository":{"id":50543690,"uuid":"517756908","full_name":"yezz123/ormdantic","owner":"yezz123","description":"Asynchronous ORM that uses pydantic models to represent database tables ✨","archived":false,"fork":false,"pushed_at":"2025-04-07T21:26:58.000Z","size":537,"stargazers_count":143,"open_issues_count":2,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-09T19:17:12.780Z","etag":null,"topics":["async","asyncio","database","hacktoberfest","orm","postgres","pydantic","pypika","python","sql","sqlalchemy","sqlite","typing"],"latest_commit_sha":null,"homepage":"https://ormdantic.yezz.me/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yezz123.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"yezz123","polar":"yezz123"}},"created_at":"2022-07-25T17:20:07.000Z","updated_at":"2025-03-29T12:09:45.000Z","dependencies_parsed_at":"2023-02-10T08:16:02.399Z","dependency_job_id":"3472e034-d12e-4fe2-b0f1-80bc923bafd3","html_url":"https://github.com/yezz123/ormdantic","commit_stats":{"total_commits":195,"total_committers":4,"mean_commits":48.75,"dds":0.2153846153846154,"last_synced_commit":"5927ee299c6d27e69406b5af2948467266a36370"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yezz123%2Formdantic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yezz123%2Formdantic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yezz123%2Formdantic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yezz123%2Formdantic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yezz123","download_url":"https://codeload.github.com/yezz123/ormdantic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248094988,"owners_count":21046770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","asyncio","database","hacktoberfest","orm","postgres","pydantic","pypika","python","sql","sqlalchemy","sqlite","typing"],"created_at":"2024-07-31T22:01:32.396Z","updated_at":"2025-04-09T19:17:21.166Z","avatar_url":"https://github.com/yezz123.png","language":"Python","readme":"![Logo](https://raw.githubusercontent.com/yezz123/ormdantic/main/.github/logo.png)\n\n\u003cp align=\"center\"\u003e\n    \u003cem\u003eAsynchronous ORM that uses pydantic models to represent database tables ✨\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/yezz123/ormdantic/actions/workflows/ci.yml\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://github.com/yezz123/ormdantic/actions/workflows/ci.yml/badge.svg\" alt=\"Test\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/yezz123/ormdantic\"\u003e\n    \u003cimg src=\"https://codecov.io/gh/yezz123/ormdantic/branch/main/graph/badge.svg\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/ormdantic\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/v/ormdantic?color=%2334D058\u0026label=pypi%20package\" alt=\"Package version\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pypi.org/project/ormdantic\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/pyversions/ormdantic.svg?color=%2334D058\" alt=\"Supported Python versions\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\nOrmdantic is a library for interacting with Asynchronous \u003cabbr title='Also called \"Relational databases\"'\u003eSQL databases\u003c/abbr\u003e from Python code, with Python objects. It is designed to be intuitive, easy to use, compatible, and robust.\n\n**Ormdantic** is based on [Pypika](https://github.com/kayak/pypika), and powered by \u003ca href=\"https://pydantic-docs.helpmanual.io/\" class=\"external-link\" target=\"_blank\"\u003ePydantic\u003c/a\u003e and \u003ca href=\"https://sqlalchemy.org/\" class=\"external-link\" target=\"_blank\"\u003eSQLAlchemy\u003c/a\u003e, and Highly inspired by \u003ca href=\"https://github.com/tiangolo/Sqlmodel\" class=\"external-link\" target=\"_blank\"\u003eSqlmodel\u003c/a\u003e, Created by [@tiangolo](https://github.com/tiangolo).\n\n\u003e What is [Pypika](https://github.com/kayak/pypika)?\n\u003e\n\u003e 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.\n\nThe key features are:\n\n* **Easy to use**: It has sensible defaults and does a lot of work underneath to simplify the code you write.\n* **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.\n* **Extensible**: You have all the power of SQLAlchemy and Pypika underneath.\n* **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.\n\n## Requirements\n\nA recent and currently supported version of Python (right now, \u003ca href=\"https://www.python.org/downloads/\" class=\"external-link\" target=\"_blank\"\u003ePython supports versions 3.10 and above\u003c/a\u003e).\n\nAs **Ormdantic** is based on **Pydantic** and **SQLAlchemy** and **Pypika**, it requires them. They will be automatically installed when you install Ormdantic.\n\n## Installation\n\nYou can add Ormdantic in a few easy steps. First of all, install the dependency:\n\n```shell\n$ pip install ormdantic\n\n---\u003e 100%\n\nSuccessfully installed Ormdantic\n```\n\n* Install The specific Asynchronous ORM library for your database.\n\n```shell\n# PostgreSQL\n$ pip install ormdantic[postgres]\n\n# SQLite\n$ pip install ormdantic[sqlite]\n```\n\n## Example\n\nTo 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.\n\nCheck out the [documentation](https://sqlmodel.tiangolo.com/).\n\nBut let's see how to use Ormdantic.\n\n### Create SQLAlchemy engine\n\nOrmdantic uses SQLAlchemy under hood to run different queries, which is why we need to initialize by creating an asynchronous engine.\n\n\u003e **Note**: You will use the `connection` parameter to pass the connection to the engine directly.\n\n```python\nfrom ormdantic import Ormdantic\n\nconnection = \"sqlite+aiosqlite:///db.sqlite3\"\n\ndatabase = Ormdantic(connection)\n```\n\n**Note**: You can use any asynchronous engine, check out the [documentation](https://docs.sqlalchemy.org/en/14/core/engines.html) for more information.\n\n### Create a table\n\nTo 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.\n\n#### Table Restrictions\n\n* Tables must have a single column primary key.\n* The primary key column must be the first column.\n* Relationships must `union-type` the foreign model and that models primary key.\n\n```python\nfrom uuid import uuid4\nfrom pydantic import BaseModel, Field\n\n@database.table(pk=\"id\", indexed=[\"name\"])\nclass Flavor(BaseModel):\n     \"\"\"A coffee flavor.\"\"\"\n\n     id: UUID = Field(default_factory=uuid4)\n     name: str = Field(max_length=63)\n```\n\n### Queries\n\nNow after we create the table, we can initialize the database with the table and then run different queries.\n\n#### Init()\n\n* Register models as ORM models and initialize the database.\n\nWe use `database.init` will Populate relations information and create the tables.\n\n```python\nasync def demo() -\u003e None:\n    async def _init() -\u003e None:\n        async with db._engine.begin() as conn:\n            await db.init()\n            await conn.run_sync(db._metadata.drop_all)\n            await conn.run_sync(db._metadata.create_all)\n    await _init()\n```\n\n#### Insert\n\nNow let's imagine we have another table called `Coffee` that has a foreign key to `Flavor`.\n\n```python\n@database.table(pk=\"id\")\nclass Coffee(BaseModel):\n     \"\"\"Drink it in the morning.\"\"\"\n\n     id: UUID = Field(default_factory=uuid4)\n     sweetener: str | None = Field(max_length=63)\n     sweetener_count: int | None = None\n     flavor: Flavor | UUID\n```\n\nAfter we create the table, we can insert data into the table, using the `database.insert` method, is away we insert a Model Instance.\n\n```python\n# Create a Flavor called \"Vanilla\"\nvanilla = Flavor(name=\"Vanilla\")\n\n# Insert the Flavor into the database\nawait database[Flavor].insert(vanilla)\n\n# Create a Coffee with the Vanilla Flavor\ncoffee = Coffee(sweetener=\"Sugar\", sweetener_count=1, flavor=vanilla)\n\n# Insert the Coffee into the database\nawait database[Coffee].insert(coffee)\n```\n\n#### Searching Queries\n\nAs we know, in SQL, we can search for data using different methods, ex. `WHERE`, `LIKE`, `IN`, `BETWEEN`, etc.\n\nIn Ormdantic, we can search for data using the `database.find_one` or `database.find_many` methods.\n\n* `Find_one`  used to find a Model instance by Primary Key, its could also find with `depth` parameter.\n\n```python\n     # Find one\n     vanilla = await database[Flavor].find_one(flavor.id)\n     print(vanilla.name)\n\n     # Find one with depth.\n     find_coffee = await database[Coffee].find_one(coffee.id, depth=1)\n     print(find_coffee.flavor.name)\n```\n\n* `Find_many` used to find Model instances by some condition ex. `where`, `order_by`, `order`, `limit`, `offset`, `depth`.\n\n```python\n     # Find many\n     await database[Flavor].find_many()\n\n     # Get paginated results.\n     await database[Flavor].find_many(\n          where={\"name\": \"vanilla\"}, order_by=[\"id\", \"name\"], limit=2, offset=2\n     )\n```\n\n#### Update / Upsert Queries\n\n##### Update\n\nThe 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.\n\n```python\n     # Update a Flavor\n     flavor.name = \"caramel\"\n     await database[Flavor].update(flavor)\n```\n\n##### Upsert\n\nThe `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.\n\n```python\n     # Upsert a Flavor\n     flavor.name = \"mocha\"\n     await database[Flavor].upsert(flavor)\n```\n\n### Delete\n\nThe `DELETE` statement is used to delete existing records in a table.\n\n```python\n     # Delete a Flavor\n     await database[Flavor].delete(flavor.id)\n```\n\n### Count\n\nTo count the number of rows of a table or in a result set you can use the `count` function.\n\n```python\n     # Count\n     count = await database[Flavor].count()\n     print(count)\n```\n\n* It's support also `Where` and `Depth`\n\n```python\n     count_advanced = await database[Coffee].count(\n          where={\"sweetener\": 2}, depth=1\n     )\n     print(count_advanced)\n```\n\n## Generator Feature\n\nWe introduce a new feature called `Generator`, which is a way to generate a Model instance with random data.\n\nSo, Given a Pydantic model type can generate instances of that model with randomly generated values.\n\nusing `ormdantic.generator.Generator` to generate a Model instance.\n\n```python\nfrom enum import auto, Enum\nfrom uuid import UUID\n\nfrom ormdantic.generator import Generator\nfrom pydantic import BaseModel\n\n\nclass Flavor(Enum):\n    MOCHA = auto()\n    VANILLA = auto()\n\n\nclass Brand(BaseModel):\n    brand_name: str\n\n\nclass Coffee(BaseModel):\n    id: UUID\n    description: str\n    cream: bool\n    sweetener: int\n    flavor: Flavor\n    brand: Brand\n\n\nprint(Generator(Coffee))\n```\n\nso the results will be:\n\n```shell\nid=UUID('93b517c2-083b-457d-a0e5-6e1bd2a927e4')\ndescription='ctWOb' cream=True sweetener=234\nflavor=\u003cFlavor.VANILLA: 2\u003e brand=Brand(brand_name='LMrIf')\n```\n\nWe can integrate this with our database while testing our application (Live Tests).\n\n## Development 🚧\n\n### Setup environment 📦\n\nYou should create a virtual environment and activate it:\n\n```bash\npython -m venv venv/\n```\n\n```bash\nsource venv/bin/activate\n```\n\nAnd then install the development dependencies:\n\n```bash\n# Install dependencies\npip install -r requirements/all.txt\n```\n\n### Run tests 🌝\n\nYou can run all the tests with:\n\n```bash\nbash scripts/test.sh\n```\n\n### Format the code 🍂\n\nExecute the following command to apply `pre-commit` formatting:\n\n```bash\nbash scripts/format.sh\n```\n\nExecute the following command to apply `mypy` type checking:\n\n```bash\nbash scripts/lint.sh\n```\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n","funding_links":["https://github.com/sponsors/yezz123","https://polar.sh/yezz123"],"categories":["Python","Uncategorized"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyezz123%2Formdantic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyezz123%2Formdantic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyezz123%2Formdantic/lists"}