# sqlalchemy-nest
[![PyPI - Version](https://img.shields.io/pypi/v/sqlalchemy-nest)](https://pypi.org/project/sqlalchemy-nest/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sqlalchemy-nest)](https://pypi.org/project/sqlalchemy-nest/)
[![Downloads](https://static.pepy.tech/badge/sqlalchemy-nest)](https://pepy.tech/project/sqlalchemy-nest)
[![CI](https://github.com/satorudev976/sqlalchemy-nest/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/satorudev976/sqlalchemy-nest/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/satorudev976/sqlalchemy-nest/graph/badge.svg?token=67ESOOAA5E)](https://codecov.io/gh/satorudev976/sqlalchemy-nest)
[![Maintainability](https://api.codeclimate.com/v1/badges/7c8a77a2447deec781ce/maintainability)](https://codeclimate.com/github/satorudev976/sqlalchemy-nest/maintainability)
sqlalchemy-nest is easy create nested models for sqlalchemy
### Why?? 🧐🧐
The default constructor of ```declarative_base()``` in sqlalchemy is as follows
```python
def _declarative_constructor(self: Any, **kwargs: Any) -> None:
cls_ = type(self)
for k in kwargs:
if not hasattr(cls_, k):
raise TypeError(
"%r is an invalid keyword argument for %s" % (k, cls_.__name__)
)
setattr(self, k, kwargs[k])
```
So can’t create nested model by unpacking schema like below. OMG !!!
```python
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import declarative_base, relationship
Base = declarative_base()
class Root(Base):
__tablename__ = "root"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100))
branches = relationship("Branch", back_populates="root", uselist=True, lazy="joined")
class Branch(Base):
__tablename__ = "branch"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100))
root_id = Column(Integer, ForeignKey("root.id"))
root = relationship("Root")
root = {
'name': 'root',
'branches': [
{
'name': 'branch',
},
]
}
created_root = Root(**root)
>>> AttributeError: 'dict' object has no attribute '_sa_instance_state'
```
### Installation
```
pip install sqlalchemy-nest
```
### Create Nested Model
1. Set declarative_base constructor
use ```declarative_nested_model_constructor``` for declarative_base constructor
```python
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy_nest import declarative_nested_model_constructor
Base = declarative_base(constructor=declarative_nested_model_constructor)
class Root(Base):
__tablename__ = "root"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100))
branches = relationship("Branch", back_populates="root", uselist=True, lazy="joined")
class Branch(Base):
__tablename__ = "branch"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100))
root_id = Column(Integer, ForeignKey("root.id"))
root = relationship("Root")
```
1. Initialization from **kwargs
sets attributes on the constructed instance using the names and values in kwargs.
```python
root = {
'name': 'root',
'branches': [
{
'name': 'branch',
},
]
}
>>> session.add(Root(**root))
>>> session.commit()
>>> added_root: Root = session.query(Root).filter(Root.id == 1).first()
Root(id=1, name='root', branches=[
Branch(id=1, name='branch', root_id=1)]
)
```
### Merge Nested Model
1. Set declarative_base constructor and cls
use ```declarative_nested_model_constructor``` and ```BaseModel``` for declarative_base
```python
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy_nest import declarative_nested_model_constructor
from sqlalchemy_nest.orm import BaseModel
Base = declarative_base(cls=BaseModel, constructor=declarative_nested_model_constructor)
class Root(Base):
__tablename__ = "root"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100))
branches = relationship("Branch", back_populates="root", uselist=True, lazy="joined")
class Branch(Base):
__tablename__ = "branch"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100))
root_id = Column(Integer, ForeignKey("root.id"))
root = relationship("Root")
```
1. Update from **kwargs
```python
root = {
'name': 'root',
'branches': [
{
'name': 'branch',
},
]
}
>>> session.add(Root(**root))
>>> session.commit()
>>> added_root: Root = session.query(Root).filter(Root.id == 1).first()
Root(id=1, name='root', branches=[
Branch(id=1, name='branch', root_id=1)]
)
update_root = {
'id': 1,
'name': 'updated_root',
'branches': [
{
'id': 1,
'name': 'updated_branch',
},
{
'name': 'created_branch',
},
]
}
>>> added_root.merge(**update_root)
>>> session.commit()
>>> updated_root: Root = session.query(Root).filter(Root.id == 1).first()
Root(id=1, name='updated_root', branches=[
Branch(id=1, name='updated_branch', root_id=1),
Branch(id=2, name='created_branch', root_id=1)]
)
```
### Development
Please refer to the [CONTRIBUTING](https://github.com/satorudev976/sqlalchemy-nest/blob/main/CONTRIBUTING.md)
### Example
[Sample Code](https://github.com/satorudev976/sqlalchemy-nest/tree/main/examples) using FastAPI and SQLAlchemy
Raw data
{
"_id": null,
"home_page": "https://github.com/satorudev976/sqlalchemy-nest.git",
"name": "sqlalchemy-nest",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9,<4.0",
"maintainer_email": "",
"keywords": "sqlalchemy,nest,sqlalchemy-nest",
"author": "satoru",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/fa/19/a2802204d166a110ae1dfb8178277e22b88a9914fa27288916b2cdd82ecb/sqlalchemy_nest-1.2.2.tar.gz",
"platform": null,
"description": "# sqlalchemy-nest\n\n[![PyPI - Version](https://img.shields.io/pypi/v/sqlalchemy-nest)](https://pypi.org/project/sqlalchemy-nest/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sqlalchemy-nest)](https://pypi.org/project/sqlalchemy-nest/)\n[![Downloads](https://static.pepy.tech/badge/sqlalchemy-nest)](https://pepy.tech/project/sqlalchemy-nest)\n[![CI](https://github.com/satorudev976/sqlalchemy-nest/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/satorudev976/sqlalchemy-nest/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/satorudev976/sqlalchemy-nest/graph/badge.svg?token=67ESOOAA5E)](https://codecov.io/gh/satorudev976/sqlalchemy-nest)\n[![Maintainability](https://api.codeclimate.com/v1/badges/7c8a77a2447deec781ce/maintainability)](https://codeclimate.com/github/satorudev976/sqlalchemy-nest/maintainability)\n\nsqlalchemy-nest is easy create nested models for sqlalchemy\n\n### Why?? \ud83e\uddd0\ud83e\uddd0\n\nThe default constructor of ```declarative_base()``` in sqlalchemy is as follows\n\n```python\ndef _declarative_constructor(self: Any, **kwargs: Any) -> None:\n cls_ = type(self)\n for k in kwargs:\n if not hasattr(cls_, k):\n raise TypeError(\n \"%r is an invalid keyword argument for %s\" % (k, cls_.__name__)\n )\n setattr(self, k, kwargs[k])\n\n```\n\nSo can\u2019t create nested model by unpacking schema like below. OMG !!!\n\n```python\nfrom sqlalchemy import Column, ForeignKey, Integer, String\nfrom sqlalchemy.orm import declarative_base, relationship\n\nBase = declarative_base()\n\nclass Root(Base):\n __tablename__ = \"root\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String(100))\n\n branches = relationship(\"Branch\", back_populates=\"root\", uselist=True, lazy=\"joined\")\n\nclass Branch(Base):\n __tablename__ = \"branch\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String(100))\n root_id = Column(Integer, ForeignKey(\"root.id\"))\n\n root = relationship(\"Root\")\n\nroot = {\n 'name': 'root',\n 'branches': [\n {\n 'name': 'branch',\n },\n ]\n}\n\ncreated_root = Root(**root)\n>>> AttributeError: 'dict' object has no attribute '_sa_instance_state'\n```\n\n\n### Installation\n\n```\npip install sqlalchemy-nest\n```\n\n### Create Nested Model\n\n1. Set declarative_base constructor\n\n use ```declarative_nested_model_constructor``` for declarative_base constructor\n\n ```python\n from sqlalchemy import Column, ForeignKey, Integer, String\n from sqlalchemy.orm import declarative_base, relationship\n from sqlalchemy_nest import declarative_nested_model_constructor\n\n Base = declarative_base(constructor=declarative_nested_model_constructor)\n\n class Root(Base):\n __tablename__ = \"root\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String(100))\n\n branches = relationship(\"Branch\", back_populates=\"root\", uselist=True, lazy=\"joined\")\n\n class Branch(Base):\n __tablename__ = \"branch\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String(100))\n root_id = Column(Integer, ForeignKey(\"root.id\"))\n\n root = relationship(\"Root\")\n ```\n\n1. Initialization from **kwargs\n\n sets attributes on the constructed instance using the names and values in kwargs.\n\n ```python\n root = {\n 'name': 'root',\n 'branches': [\n {\n 'name': 'branch',\n },\n ]\n }\n >>> session.add(Root(**root))\n >>> session.commit()\n >>> added_root: Root = session.query(Root).filter(Root.id == 1).first()\n Root(id=1, name='root', branches=[\n Branch(id=1, name='branch', root_id=1)]\n )\n ```\n\n### Merge Nested Model\n\n1. Set declarative_base constructor and cls\n\n use ```declarative_nested_model_constructor``` and ```BaseModel``` for declarative_base\n\n ```python\n from sqlalchemy import Column, ForeignKey, Integer, String\n from sqlalchemy.orm import declarative_base, relationship\n from sqlalchemy_nest import declarative_nested_model_constructor\n from sqlalchemy_nest.orm import BaseModel\n\n Base = declarative_base(cls=BaseModel, constructor=declarative_nested_model_constructor)\n\n class Root(Base):\n __tablename__ = \"root\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String(100))\n\n branches = relationship(\"Branch\", back_populates=\"root\", uselist=True, lazy=\"joined\")\n\n class Branch(Base):\n __tablename__ = \"branch\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String(100))\n root_id = Column(Integer, ForeignKey(\"root.id\"))\n\n root = relationship(\"Root\")\n\n ```\n\n1. Update from **kwargs\n\n\n ```python\n root = {\n 'name': 'root',\n 'branches': [\n {\n 'name': 'branch',\n },\n ]\n }\n >>> session.add(Root(**root))\n >>> session.commit()\n >>> added_root: Root = session.query(Root).filter(Root.id == 1).first()\n Root(id=1, name='root', branches=[\n Branch(id=1, name='branch', root_id=1)]\n )\n\n update_root = {\n 'id': 1,\n 'name': 'updated_root',\n 'branches': [\n {\n 'id': 1,\n 'name': 'updated_branch',\n },\n {\n 'name': 'created_branch',\n },\n ]\n }\n >>> added_root.merge(**update_root)\n >>> session.commit()\n >>> updated_root: Root = session.query(Root).filter(Root.id == 1).first()\n Root(id=1, name='updated_root', branches=[\n Branch(id=1, name='updated_branch', root_id=1),\n Branch(id=2, name='created_branch', root_id=1)]\n )\n ```\n\n### Development\n\nPlease refer to the [CONTRIBUTING](https://github.com/satorudev976/sqlalchemy-nest/blob/main/CONTRIBUTING.md)\n\n### Example\n\n[Sample Code](https://github.com/satorudev976/sqlalchemy-nest/tree/main/examples) using FastAPI and SQLAlchemy\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "easy create nested models for sqlalchemy",
"version": "1.2.2",
"project_urls": {
"Homepage": "https://github.com/satorudev976/sqlalchemy-nest.git",
"Repository": "https://github.com/satorudev976/sqlalchemy-nest.git"
},
"split_keywords": [
"sqlalchemy",
"nest",
"sqlalchemy-nest"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "4deb95235594c5c4c37d0422f4ada9f01a137ca1c37446d0564757847e29bea3",
"md5": "150325d933991092a7b17c349174cf23",
"sha256": "5dcab057575605e47c5821b745ee14ab9030e671c3aa1ec6341b9d3f49ac8aa7"
},
"downloads": -1,
"filename": "sqlalchemy_nest-1.2.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "150325d933991092a7b17c349174cf23",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9,<4.0",
"size": 5214,
"upload_time": "2024-02-18T09:16:58",
"upload_time_iso_8601": "2024-02-18T09:16:58.954663Z",
"url": "https://files.pythonhosted.org/packages/4d/eb/95235594c5c4c37d0422f4ada9f01a137ca1c37446d0564757847e29bea3/sqlalchemy_nest-1.2.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "fa19a2802204d166a110ae1dfb8178277e22b88a9914fa27288916b2cdd82ecb",
"md5": "536ebf509093dfdeebbaf99e2ad81d36",
"sha256": "764c92a6a43d1856eb28f6e70d43931f7d959cffd6a459582b537ef4cd59e2f7"
},
"downloads": -1,
"filename": "sqlalchemy_nest-1.2.2.tar.gz",
"has_sig": false,
"md5_digest": "536ebf509093dfdeebbaf99e2ad81d36",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9,<4.0",
"size": 4625,
"upload_time": "2024-02-18T09:17:00",
"upload_time_iso_8601": "2024-02-18T09:17:00.292705Z",
"url": "https://files.pythonhosted.org/packages/fa/19/a2802204d166a110ae1dfb8178277e22b88a9914fa27288916b2cdd82ecb/sqlalchemy_nest-1.2.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-02-18 09:17:00",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "satorudev976",
"github_project": "sqlalchemy-nest",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "sqlalchemy-nest"
}