AFAS-pydantic-orm


NameAFAS-pydantic-orm JSON
Version 1.0.0 PyPI version JSON
download
home_pagehttps://github.com/Alexanderkievit/AFAS_Pydantic_ORM
SummaryCRUD operations on nested SQLAlchemy ORM-models using Pydantic
upload_time2024-09-17 08:07:01
maintainerNone
docs_urlNone
authorAKievit
requires_python>=3.8
licenseNone
keywords python pydantic sqlalchemy orm nested nesting crud
VCS
bugtrack_url
requirements annotated-types appdirs attrs backports.tarfile black bleach build certifi cffi chardet charset-normalizer check-manifest click colorama coverage cryptography distlib docutils exceptiongroup filelock flake8 greenlet idna importlib_metadata iniconfig isort jaraco.classes jaraco.context jaraco.functools jeepney keyring Mako Markdown markdown-it-py MarkupSafe mccabe mdurl more-itertools mypy mypy-extensions nh3 packaging pathspec pdoc3 pep517 pkginfo platformdirs pluggy py pycodestyle pycparser pydantic pydantic_core pyflakes Pygments pyparsing pyproject_hooks pytest pywin32-ctypes readme_renderer regex requests requests-toolbelt rfc3986 rich SecretStorage six SQLAlchemy toml tomli tqdm twine typed-ast typing_extensions urllib3 virtualenv webencodings zipp
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # sqlalchemy-pydantic-orm
This library makes it a lot easier to do nested database operation with 
SQLAlchemy. With this library it is for example possible to validate, convert, 
and upload a 100-level deep nested JSON (dict) to its corresponding tables in a 
given database, within 3 lines of code. 

[Pydantic](https://pydantic-docs.helpmanual.io/) is used for creating the 
dataclass and validating it. Pydantic already has a function called
[`.from_orm()`](https://pydantic-docs.helpmanual.io/usage/models/#orm-mode-aka-arbitrary-class-instances) 
that can do a nested get operation, but it only supports ORM -> Pydantic and 
not Pydantic -> ORM. That's exactly where this library fills in, with 2 
specific functions `.orm_create()` and `.orm_update()`, and one general 
function `.to_orm()` that combines the functionality of the first 2, calling 
one or the other, depending on if there is an id provided.


# Requirements
- Python 3.8+
- SQLAlchemy 1.4+
- Pydantic 1.8+

# Installation
```shell
$ pip install AFAS-pydantic-orm
```
To tinker with the code yourself, install the full dependencies with:
```shell
$ pip install AFAS-pydantic-orm[dev]
```

# Useful references
- https://pydantic-docs.helpmanual.io/usage/models/
- https://fastapi.tiangolo.com/tutorial/sql-databases/


# Examples
Below 1 example is provided (more coming).

[comment]: <> (The first one is a more manual setup, the second does all the work for you.)
For a bigger and more detailed example you can look at the /examples/ folder.

## Example 1 - Using manual created schemas
Create your own Pydantic schemas and link them to the SQLAlchemy ORM-models.

### Create your SQLAlchemy ORM-models (one-to-one or one-to-many)
```python
class Parent(Base):
    id = Column(Integer, primary_key=True, index=True, nullable=False)
    name = Column(String, nullable=False)
    car = relationship("Car", cascade="all, delete", uselist=False, back_populates="owner")
    children = relationship("Child", cascade="all, delete")
    
class Car(Base):
    id = Column(Integer, primary_key=True, index=True, nullable=False)
    color = Column(String, nullable=False)
    owner_id = Column(Integer, ForeignKey("parents.id"), nullable=False)
    owner = relationship("Parent", back_populates="car")

class Child(Base):
    id = Column(Integer, primary_key=True, index=True, nullable=False)
    name = Column(String, nullable=False)
    parent_id = Column(Integer, ForeignKey("parents.id"), nullable=False)
```

### Create your Pydantic base and CRUD schemas using these ORM models, and the imported ORMBaseSchema

#### Base schemas
```python
from sqlalchemy_pydantic_orm import ORMBaseSchema
from .models import Parent, Car, Child

class ParentBase(ORMBaseSchema):
    name: str
    _orm_model = PrivateAttr(Parent)

class CarBase(ORMBaseSchema):
    color: str
    _orm_model = PrivateAttr(models.Car)

class ChildBase(ORMBaseSchema):
    name: str
    _orm_model = PrivateAttr(models.Child)
```

#### GET schemas
```python
class Parent(ParentBase):
    id: int
    children: List[Child]
    car: Car

class Car(CarBase):
    id: int

class Child(ChildBase):
    id: int
```

#### CREATE/UPDATE schemas
```python
class ParentCreate(ParentBase):
    id: Optional[int]
    children: List[ChildCreate]
    car: CarCreate

class CarCreate(CarBase):
    id: Optional[int]

class ChildCreate(ChildBase):
    id: Optional[int]
```

### Use your schemas to do nested CRU~~D~~ operations.
```python
with ConnectionDatabase() as db:
    create_schema = schemas.ParentCreate.parse_obj(create_dict)
    parent_db = create_schema.orm_create()
    db.add(parent_db)
    db.commit()

    db_create_schema = schemas.Parent.from_orm(parent_db)
    print(db_create_schema.dict())

    update_schema = schemas.ParentUpdate.parse_obj(update_dict)
    update_schema.to_orm(db)
    db.commit()

    db_update_schema = schemas.Parent.orm_update(parent_db)
    print(db_update_schema.dict())
```
Note: with `.orm_create()` you have to call `db.add()`
before calling `db.commit()`. 
With orm_update you give the db session as parameter,
and you only have to call `db.commit()`.


## ~~Example 2 - Using generated schemas~~
TODO: Integrate with https://github.com/tiangolo/pydantic-sqlalchemy

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/Alexanderkievit/AFAS_Pydantic_ORM",
    "name": "AFAS-pydantic-orm",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "python, pydantic, sqlalchemy, ORM, nested, nesting, CRUD",
    "author": "AKievit",
    "author_email": "<alexander.kievit@afas.nl>",
    "download_url": "https://files.pythonhosted.org/packages/3f/f1/f4b2fb0f4df489aad50d0a65a600f51034986367035ed2f3edbe32cdbaf3/afas_pydantic_orm-1.0.0.tar.gz",
    "platform": null,
    "description": "# sqlalchemy-pydantic-orm\r\nThis library makes it a lot easier to do nested database operation with \r\nSQLAlchemy. With this library it is for example possible to validate, convert, \r\nand upload a 100-level deep nested JSON (dict) to its corresponding tables in a \r\ngiven database, within 3 lines of code. \r\n\r\n[Pydantic](https://pydantic-docs.helpmanual.io/) is used for creating the \r\ndataclass and validating it. Pydantic already has a function called\r\n[`.from_orm()`](https://pydantic-docs.helpmanual.io/usage/models/#orm-mode-aka-arbitrary-class-instances) \r\nthat can do a nested get operation, but it only supports ORM -> Pydantic and \r\nnot Pydantic -> ORM. That's exactly where this library fills in, with 2 \r\nspecific functions `.orm_create()` and `.orm_update()`, and one general \r\nfunction `.to_orm()` that combines the functionality of the first 2, calling \r\none or the other, depending on if there is an id provided.\r\n\r\n\r\n# Requirements\r\n- Python 3.8+\r\n- SQLAlchemy 1.4+\r\n- Pydantic 1.8+\r\n\r\n# Installation\r\n```shell\r\n$ pip install AFAS-pydantic-orm\r\n```\r\nTo tinker with the code yourself, install the full dependencies with:\r\n```shell\r\n$ pip install AFAS-pydantic-orm[dev]\r\n```\r\n\r\n# Useful references\r\n- https://pydantic-docs.helpmanual.io/usage/models/\r\n- https://fastapi.tiangolo.com/tutorial/sql-databases/\r\n\r\n\r\n# Examples\r\nBelow 1 example is provided (more coming).\r\n\r\n[comment]: <> (The first one is a more manual setup, the second does all the work for you.)\r\nFor a bigger and more detailed example you can look at the /examples/ folder.\r\n\r\n## Example 1 - Using manual created schemas\r\nCreate your own Pydantic schemas and link them to the SQLAlchemy ORM-models.\r\n\r\n### Create your SQLAlchemy ORM-models (one-to-one or one-to-many)\r\n```python\r\nclass Parent(Base):\r\n    id = Column(Integer, primary_key=True, index=True, nullable=False)\r\n    name = Column(String, nullable=False)\r\n    car = relationship(\"Car\", cascade=\"all, delete\", uselist=False, back_populates=\"owner\")\r\n    children = relationship(\"Child\", cascade=\"all, delete\")\r\n    \r\nclass Car(Base):\r\n    id = Column(Integer, primary_key=True, index=True, nullable=False)\r\n    color = Column(String, nullable=False)\r\n    owner_id = Column(Integer, ForeignKey(\"parents.id\"), nullable=False)\r\n    owner = relationship(\"Parent\", back_populates=\"car\")\r\n\r\nclass Child(Base):\r\n    id = Column(Integer, primary_key=True, index=True, nullable=False)\r\n    name = Column(String, nullable=False)\r\n    parent_id = Column(Integer, ForeignKey(\"parents.id\"), nullable=False)\r\n```\r\n\r\n### Create your Pydantic base and CRUD schemas using these ORM models, and the imported ORMBaseSchema\r\n\r\n#### Base schemas\r\n```python\r\nfrom sqlalchemy_pydantic_orm import ORMBaseSchema\r\nfrom .models import Parent, Car, Child\r\n\r\nclass ParentBase(ORMBaseSchema):\r\n    name: str\r\n    _orm_model = PrivateAttr(Parent)\r\n\r\nclass CarBase(ORMBaseSchema):\r\n    color: str\r\n    _orm_model = PrivateAttr(models.Car)\r\n\r\nclass ChildBase(ORMBaseSchema):\r\n    name: str\r\n    _orm_model = PrivateAttr(models.Child)\r\n```\r\n\r\n#### GET schemas\r\n```python\r\nclass Parent(ParentBase):\r\n    id: int\r\n    children: List[Child]\r\n    car: Car\r\n\r\nclass Car(CarBase):\r\n    id: int\r\n\r\nclass Child(ChildBase):\r\n    id: int\r\n```\r\n\r\n#### CREATE/UPDATE schemas\r\n```python\r\nclass ParentCreate(ParentBase):\r\n    id: Optional[int]\r\n    children: List[ChildCreate]\r\n    car: CarCreate\r\n\r\nclass CarCreate(CarBase):\r\n    id: Optional[int]\r\n\r\nclass ChildCreate(ChildBase):\r\n    id: Optional[int]\r\n```\r\n\r\n### Use your schemas to do nested CRU~~D~~ operations.\r\n```python\r\nwith ConnectionDatabase() as db:\r\n    create_schema = schemas.ParentCreate.parse_obj(create_dict)\r\n    parent_db = create_schema.orm_create()\r\n    db.add(parent_db)\r\n    db.commit()\r\n\r\n    db_create_schema = schemas.Parent.from_orm(parent_db)\r\n    print(db_create_schema.dict())\r\n\r\n    update_schema = schemas.ParentUpdate.parse_obj(update_dict)\r\n    update_schema.to_orm(db)\r\n    db.commit()\r\n\r\n    db_update_schema = schemas.Parent.orm_update(parent_db)\r\n    print(db_update_schema.dict())\r\n```\r\nNote: with `.orm_create()` you have to call `db.add()`\r\nbefore calling `db.commit()`. \r\nWith orm_update you give the db session as parameter,\r\nand you only have to call `db.commit()`.\r\n\r\n\r\n## ~~Example 2 - Using generated schemas~~\r\nTODO: Integrate with https://github.com/tiangolo/pydantic-sqlalchemy\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "CRUD operations on nested SQLAlchemy ORM-models using Pydantic",
    "version": "1.0.0",
    "project_urls": {
        "Documentation": "https://github.com/Alexanderkievit/AFAS_Pydantic_ORM",
        "Homepage": "https://github.com/Alexanderkievit/AFAS_Pydantic_ORM",
        "Source": "https://github.com/Alexanderkievit/AFAS_Pydantic_ORM",
        "Tracker": "https://github.com/Alexanderkievit/AFAS_Pydantic_ORM/issues"
    },
    "split_keywords": [
        "python",
        " pydantic",
        " sqlalchemy",
        " orm",
        " nested",
        " nesting",
        " crud"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3ff1f4b2fb0f4df489aad50d0a65a600f51034986367035ed2f3edbe32cdbaf3",
                "md5": "fb20fc0088021479d9c05f65b6bdbf01",
                "sha256": "879f484c30fe839185192cd8e6ae4450c04891e32e832f6051a80c22eede8e50"
            },
            "downloads": -1,
            "filename": "afas_pydantic_orm-1.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "fb20fc0088021479d9c05f65b6bdbf01",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 9736,
            "upload_time": "2024-09-17T08:07:01",
            "upload_time_iso_8601": "2024-09-17T08:07:01.057630Z",
            "url": "https://files.pythonhosted.org/packages/3f/f1/f4b2fb0f4df489aad50d0a65a600f51034986367035ed2f3edbe32cdbaf3/afas_pydantic_orm-1.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-17 08:07:01",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Alexanderkievit",
    "github_project": "AFAS_Pydantic_ORM",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "annotated-types",
            "specs": [
                [
                    "==",
                    "0.7.0"
                ]
            ]
        },
        {
            "name": "appdirs",
            "specs": [
                [
                    "==",
                    "1.4.4"
                ]
            ]
        },
        {
            "name": "attrs",
            "specs": [
                [
                    "==",
                    "24.2.0"
                ]
            ]
        },
        {
            "name": "backports.tarfile",
            "specs": [
                [
                    "==",
                    "1.2.0"
                ]
            ]
        },
        {
            "name": "black",
            "specs": [
                [
                    "==",
                    "24.8.0"
                ]
            ]
        },
        {
            "name": "bleach",
            "specs": [
                [
                    "==",
                    "6.1.0"
                ]
            ]
        },
        {
            "name": "build",
            "specs": [
                [
                    "==",
                    "1.2.2"
                ]
            ]
        },
        {
            "name": "certifi",
            "specs": [
                [
                    "==",
                    "2024.8.30"
                ]
            ]
        },
        {
            "name": "cffi",
            "specs": [
                [
                    "==",
                    "1.17.1"
                ]
            ]
        },
        {
            "name": "chardet",
            "specs": [
                [
                    "==",
                    "5.2.0"
                ]
            ]
        },
        {
            "name": "charset-normalizer",
            "specs": [
                [
                    "==",
                    "3.3.2"
                ]
            ]
        },
        {
            "name": "check-manifest",
            "specs": [
                [
                    "==",
                    "0.49"
                ]
            ]
        },
        {
            "name": "click",
            "specs": [
                [
                    "==",
                    "8.1.7"
                ]
            ]
        },
        {
            "name": "colorama",
            "specs": [
                [
                    "==",
                    "0.4.6"
                ]
            ]
        },
        {
            "name": "coverage",
            "specs": [
                [
                    "==",
                    "7.6.1"
                ]
            ]
        },
        {
            "name": "cryptography",
            "specs": [
                [
                    "==",
                    "43.0.1"
                ]
            ]
        },
        {
            "name": "distlib",
            "specs": [
                [
                    "==",
                    "0.3.8"
                ]
            ]
        },
        {
            "name": "docutils",
            "specs": [
                [
                    "==",
                    "0.21.2"
                ]
            ]
        },
        {
            "name": "exceptiongroup",
            "specs": [
                [
                    "==",
                    "1.2.2"
                ]
            ]
        },
        {
            "name": "filelock",
            "specs": [
                [
                    "==",
                    "3.16.0"
                ]
            ]
        },
        {
            "name": "flake8",
            "specs": [
                [
                    "==",
                    "7.1.1"
                ]
            ]
        },
        {
            "name": "greenlet",
            "specs": [
                [
                    "==",
                    "3.1.0"
                ]
            ]
        },
        {
            "name": "idna",
            "specs": [
                [
                    "==",
                    "3.10"
                ]
            ]
        },
        {
            "name": "importlib_metadata",
            "specs": [
                [
                    "==",
                    "8.5.0"
                ]
            ]
        },
        {
            "name": "iniconfig",
            "specs": [
                [
                    "==",
                    "2.0.0"
                ]
            ]
        },
        {
            "name": "isort",
            "specs": [
                [
                    "==",
                    "5.13.2"
                ]
            ]
        },
        {
            "name": "jaraco.classes",
            "specs": [
                [
                    "==",
                    "3.4.0"
                ]
            ]
        },
        {
            "name": "jaraco.context",
            "specs": [
                [
                    "==",
                    "6.0.1"
                ]
            ]
        },
        {
            "name": "jaraco.functools",
            "specs": [
                [
                    "==",
                    "4.0.2"
                ]
            ]
        },
        {
            "name": "jeepney",
            "specs": [
                [
                    "==",
                    "0.8.0"
                ]
            ]
        },
        {
            "name": "keyring",
            "specs": [
                [
                    "==",
                    "25.3.0"
                ]
            ]
        },
        {
            "name": "Mako",
            "specs": [
                [
                    "==",
                    "1.3.5"
                ]
            ]
        },
        {
            "name": "Markdown",
            "specs": [
                [
                    "==",
                    "3.7"
                ]
            ]
        },
        {
            "name": "markdown-it-py",
            "specs": [
                [
                    "==",
                    "3.0.0"
                ]
            ]
        },
        {
            "name": "MarkupSafe",
            "specs": [
                [
                    "==",
                    "2.1.5"
                ]
            ]
        },
        {
            "name": "mccabe",
            "specs": [
                [
                    "==",
                    "0.7.0"
                ]
            ]
        },
        {
            "name": "mdurl",
            "specs": [
                [
                    "==",
                    "0.1.2"
                ]
            ]
        },
        {
            "name": "more-itertools",
            "specs": [
                [
                    "==",
                    "10.5.0"
                ]
            ]
        },
        {
            "name": "mypy",
            "specs": [
                [
                    "==",
                    "1.11.2"
                ]
            ]
        },
        {
            "name": "mypy-extensions",
            "specs": [
                [
                    "==",
                    "1.0.0"
                ]
            ]
        },
        {
            "name": "nh3",
            "specs": [
                [
                    "==",
                    "0.2.18"
                ]
            ]
        },
        {
            "name": "packaging",
            "specs": [
                [
                    "==",
                    "24.1"
                ]
            ]
        },
        {
            "name": "pathspec",
            "specs": [
                [
                    "==",
                    "0.12.1"
                ]
            ]
        },
        {
            "name": "pdoc3",
            "specs": [
                [
                    "==",
                    "0.11.1"
                ]
            ]
        },
        {
            "name": "pep517",
            "specs": [
                [
                    "==",
                    "0.13.1"
                ]
            ]
        },
        {
            "name": "pkginfo",
            "specs": [
                [
                    "==",
                    "1.10.0"
                ]
            ]
        },
        {
            "name": "platformdirs",
            "specs": [
                [
                    "==",
                    "4.3.3"
                ]
            ]
        },
        {
            "name": "pluggy",
            "specs": [
                [
                    "==",
                    "1.5.0"
                ]
            ]
        },
        {
            "name": "py",
            "specs": [
                [
                    "==",
                    "1.11.0"
                ]
            ]
        },
        {
            "name": "pycodestyle",
            "specs": [
                [
                    "==",
                    "2.12.1"
                ]
            ]
        },
        {
            "name": "pycparser",
            "specs": [
                [
                    "==",
                    "2.22"
                ]
            ]
        },
        {
            "name": "pydantic",
            "specs": [
                [
                    "==",
                    "2.9.1"
                ]
            ]
        },
        {
            "name": "pydantic_core",
            "specs": [
                [
                    "==",
                    "2.23.3"
                ]
            ]
        },
        {
            "name": "pyflakes",
            "specs": [
                [
                    "==",
                    "3.2.0"
                ]
            ]
        },
        {
            "name": "Pygments",
            "specs": [
                [
                    "==",
                    "2.18.0"
                ]
            ]
        },
        {
            "name": "pyparsing",
            "specs": [
                [
                    "==",
                    "3.1.4"
                ]
            ]
        },
        {
            "name": "pyproject_hooks",
            "specs": [
                [
                    "==",
                    "1.1.0"
                ]
            ]
        },
        {
            "name": "pytest",
            "specs": [
                [
                    "==",
                    "8.3.3"
                ]
            ]
        },
        {
            "name": "pywin32-ctypes",
            "specs": [
                [
                    "==",
                    "0.2.3"
                ]
            ]
        },
        {
            "name": "readme_renderer",
            "specs": [
                [
                    "==",
                    "44.0"
                ]
            ]
        },
        {
            "name": "regex",
            "specs": [
                [
                    "==",
                    "2024.9.11"
                ]
            ]
        },
        {
            "name": "requests",
            "specs": [
                [
                    "==",
                    "2.32.3"
                ]
            ]
        },
        {
            "name": "requests-toolbelt",
            "specs": [
                [
                    "==",
                    "1.0.0"
                ]
            ]
        },
        {
            "name": "rfc3986",
            "specs": [
                [
                    "==",
                    "2.0.0"
                ]
            ]
        },
        {
            "name": "rich",
            "specs": [
                [
                    "==",
                    "13.8.1"
                ]
            ]
        },
        {
            "name": "SecretStorage",
            "specs": [
                [
                    "==",
                    "3.3.3"
                ]
            ]
        },
        {
            "name": "six",
            "specs": [
                [
                    "==",
                    "1.16.0"
                ]
            ]
        },
        {
            "name": "SQLAlchemy",
            "specs": [
                [
                    "==",
                    "2.0.34"
                ]
            ]
        },
        {
            "name": "toml",
            "specs": [
                [
                    "==",
                    "0.10.2"
                ]
            ]
        },
        {
            "name": "tomli",
            "specs": [
                [
                    "==",
                    "2.0.1"
                ]
            ]
        },
        {
            "name": "tqdm",
            "specs": [
                [
                    "==",
                    "4.66.5"
                ]
            ]
        },
        {
            "name": "twine",
            "specs": [
                [
                    "==",
                    "5.1.1"
                ]
            ]
        },
        {
            "name": "typed-ast",
            "specs": [
                [
                    "==",
                    "1.5.5"
                ]
            ]
        },
        {
            "name": "typing_extensions",
            "specs": [
                [
                    "==",
                    "4.12.2"
                ]
            ]
        },
        {
            "name": "urllib3",
            "specs": [
                [
                    "==",
                    "2.2.3"
                ]
            ]
        },
        {
            "name": "virtualenv",
            "specs": [
                [
                    "==",
                    "20.26.4"
                ]
            ]
        },
        {
            "name": "webencodings",
            "specs": [
                [
                    "==",
                    "0.5.1"
                ]
            ]
        },
        {
            "name": "zipp",
            "specs": [
                [
                    "==",
                    "3.20.2"
                ]
            ]
        }
    ],
    "lcname": "afas-pydantic-orm"
}
        
Elapsed time: 0.76773s