diff --git a/docs/advanced/joins.md b/docs/advanced/joins.md index 5aa9399..2215ed5 100644 --- a/docs/advanced/joins.md +++ b/docs/advanced/joins.md @@ -308,29 +308,24 @@ The result will be: ##### Example -To demonstrate a one-to-many relationship, let's assume `User` and `Post` tables: +To demonstrate a one-to-many relationship, let's assume `Author` and `Article` tables: ```python -class User(Base): - __tablename__ = "user" - id = Column(Integer, primary key=True) - name = Column(String) - -class Post(Base): - __tablename__ = "post" - id = Column(Integer, primary key=True) - user_id = Column(Integer, ForeignKey("user.id")) - content = Column(String) +--8<-- +fastcrud/examples/author/model.py:model +fastcrud/examples/article/model.py:model +--8<-- ``` Fetch a user and all their posts: ```python -user_posts = await user_crud.get_joined( +author_crud = FastCRUD(Author) +author_articles = await author_crud.get_joined( db=db, - join_model=Post, - join_on=User.id == Post.user_id, - join_prefix="post_", + join_model=Article, + join_on=Author.id == Article.author_id, + join_prefix="article_", join_type="left", nest_joins=True, id=1, @@ -342,17 +337,19 @@ The result will be: ```json { "id": 1, - "name": "Example User", - "posts": [ + "name": "Example Author", + "articles": [ { "id": 101, - "user_id": 1, - "content": "First post content" + "author_id": 1, + "title": "First Article!", + "content": "First article content" }, { "id": 102, - "user_id": 1, - "content": "Second post content" + "author_id": 1, + "title": "Second Article?", + "content": "Second article content" } ] } diff --git a/fastcrud/crud/fast_crud.py b/fastcrud/crud/fast_crud.py index a9333a6..83f1fd9 100644 --- a/fastcrud/crud/fast_crud.py +++ b/fastcrud/crud/fast_crud.py @@ -252,6 +252,62 @@ class FastCRUD( --8<-- ``` + --- + + ??? example "`profile/model.py`" + + ```python + --8<-- + fastcrud/examples/profile/model.py:imports + fastcrud/examples/profile/model.py:model + --8<-- + ``` + + ??? example "`profile/schemas.py`" + + ```python + --8<-- + fastcrud/examples/profile/schemas.py:imports + fastcrud/examples/profile/schemas.py:readschema + --8<-- + ``` + + ??? example "`author/model.py`" + + ```python + --8<-- + fastcrud/examples/author/model.py:imports + fastcrud/examples/author/model.py:model + --8<-- + ``` + + ??? example "`author/schemas.py`" + + ```python + --8<-- + fastcrud/examples/author/schemas.py:imports + fastcrud/examples/author/schemas.py:readschema + --8<-- + ``` + + ??? example "`article/model.py`" + + ```python + --8<-- + fastcrud/examples/article/model.py:imports + fastcrud/examples/article/model.py:model + --8<-- + ``` + + ??? example "`article/schemas.py`" + + ```python + --8<-- + fastcrud/examples/article/schemas.py:imports + fastcrud/examples/article/schemas.py:readschema + --8<-- + ``` + Example 1: Basic Usage ---------------------- @@ -1422,28 +1478,32 @@ async def get_joined( ``` Example using one-to-one relationship: + ```python - result = await crud_user.get_joined( + author_crud = FastCRUD(Author) + result = await author_crud.get_joined( db=session, - schema_to_select=UserSchema, + schema_to_select=ReadAuthorSchema, join_model=Profile, - join_on=User.profile_id == Profile.id, - join_schema_to_select=ProfileSchema, + join_on=Author.profile_id == Profile.id, + join_schema_to_select=ReadProfileSchema, + nest_joins=True, relationship_type='one-to-one', # note that this is the default behavior ) # Expect 'result' to have 'profile' as a nested dictionary ``` Example using one-to-many relationship: + ```python - result = await crud_user.get_joined( + result = await author_crud.get_joined( db=session, - schema_to_select=UserSchema, - join_model=Post, - join_on=User.id == Post.user_id, - join_schema_to_select=PostSchema, - relationship_type='one-to-many', + schema_to_select=ReadAuthorSchema, + join_model=Article, + join_on=Author.id == Article.author_id, + join_schema_to_select=ReadArticleSchema, nest_joins=True, + relationship_type='one-to-many', ) # Expect 'result' to have 'posts' as a nested list of dictionaries ``` @@ -1768,36 +1828,40 @@ async def get_multi_joined( ) ``` - Example using one-to-one relationship: - ```python - users = await crud_user.get_multi_joined( - db=session, - schema_to_select=UserSchema, - join_model=Profile, - join_on=User.profile_id == Profile.id, - join_schema_to_select=ProfileSchema, - offset=0, - limit=10, - relationship_type='one-to-one', # note that this is the default behavior - ) - # Expect 'profile' to be nested as a dictionary under each user - ``` + Example using one-to-one relationship: - Example using one-to-many relationship: - ```python - users = await crud_user.get_multi_joined( - db=session, - schema_to_select=UserSchema, - join_model=Post, - join_on=User.id == Post.user_id, - join_schema_to_select=PostSchema, - nest_joins=True, - offset=0, - limit=10, - relationship_type='one-to-many', - ) - # Expect 'posts' to be nested as a list of dictionaries under each user - ``` + ```python + author_crud = FastCRUD(Author) + results = await author_crud.get_multi_joined( + db=session, + schema_to_select=ReadAuthorSchema, + join_model=Profile, + join_on=Author.profile_id == Profile.id, + join_schema_to_select=ReadProfileSchema, + nest_joins=True, + offset=0, + limit=10, + relationship_type='one-to-one', # note that this is the default behavior + ) + # Expect 'profile' to be nested as a dictionary under each user + ``` + + Example using one-to-many relationship: + + ```python + results = await author_crud.get_multi_joined( + db=session, + schema_to_select=ReadAuthorSchema, + join_model=Article, + join_on=Author.id == Article.author_id, + join_schema_to_select=ReadArticleSchema, + nest_joins=True, + offset=0, + limit=10, + relationship_type='one-to-many', + ) + # Expect 'posts' to be nested as a list of dictionaries under each user + ``` """ if joins_config and ( join_model diff --git a/fastcrud/crud/helper.py b/fastcrud/crud/helper.py index 99afc25..402ee3e 100644 --- a/fastcrud/crud/helper.py +++ b/fastcrud/crud/helper.py @@ -166,23 +166,30 @@ def _handle_one_to_one(nested_data, nested_key, nested_field, value): dict[str, Any]: The updated nested data dictionary. Examples: + Input: + + ```python nested_data = { 'id': 1, - 'name': 'Test User' + 'name': 'Test Author', } nested_key = 'profile' nested_field = 'bio' value = 'This is a bio.' + ``` Output: + + ```json { 'id': 1, - 'name': 'Test User', + 'name': 'Test Author', 'profile': { 'bio': 'This is a bio.' } } + ``` """ if nested_key not in nested_data or not isinstance(nested_data[nested_key], dict): nested_data[nested_key] = {} @@ -204,56 +211,69 @@ def _handle_one_to_many(nested_data, nested_key, nested_field, value): dict[str, Any]: The updated nested data dictionary. Examples: + Input: + + ```python nested_data = { 'id': 1, - 'name': 'Test User', - 'posts': [ + 'name': 'Test Author', + 'articles': [ { - 'title': 'First Post', - 'content': 'Content of the first post' + 'title': 'First Article', + 'content': 'Content of the first article!', } - ] + ], } - nested_key = 'posts' + nested_key = 'articles' nested_field = 'title' - value = 'Second Post' + value = 'Second Article' + ``` Output: + + ```json { 'id': 1, - 'name': 'Test User', - 'posts': [ + 'name': 'Test Author', + 'articles': [ { - 'title': 'First Post', - 'content': 'Content of the first post' + 'title': 'First Article', + 'content': 'Content of the first article!' }, { - 'title': 'Second Post' + 'title': 'Second Article' } ] } + ``` Input: + + ```python nested_data = { 'id': 1, - 'name': 'Test User', - 'posts': [] + 'name': 'Test Author', + 'articles': [], } - nested_key = 'posts' + nested_key = 'articles' nested_field = 'title' - value = 'First Post' + value = 'First Article' + ``` Output: + + ```json { 'id': 1, - 'name': 'Test User', - 'posts': [ + 'name': 'Test Author', + 'articles': [ { - 'title': 'First Post' + 'title': 'First Article' } ] } + ``` """ if nested_key not in nested_data or not isinstance(nested_data[nested_key], list): nested_data[nested_key] = [] @@ -286,13 +306,16 @@ def _nest_join_data( dict[str, Any]: A dictionary with nested structures for joined table data. Examples: + Input: + + ```python data = { 'id': 1, - 'title': 'Test Card', + 'title': 'Test Author', 'joined__articles_id': 1, 'joined__articles_title': 'Article 1', - 'joined__articles_card_id': 1 + 'joined__articles_author_id': 1 } join_definitions = [ @@ -302,24 +325,30 @@ def _nest_join_data( relationship_type='one-to-many', ), ] + ``` Output: + + ```json { 'id': 1, - 'title': 'Test Card', + 'title': 'Test Author', 'articles': [ { 'id': 1, 'title': 'Article 1', - 'card_id': 1 + 'author_id': 1 } ] } + ``` Input: + + ```python data = { 'id': 1, - 'title': 'Test Card', + 'title': 'Test Article', 'joined__author_id': 1, 'joined__author_name': 'Author 1' } @@ -331,16 +360,20 @@ def _nest_join_data( relationship_type='one-to-one', ), ] + ``` Output: + + ```json { 'id': 1, - 'title': 'Test Card', + 'title': 'Test Article', 'author': { 'id': 1, 'name': 'Author 1' } } + ``` """ if nested_data is None: nested_data = {} @@ -434,53 +467,60 @@ def _nest_multi_join_data( Sequence[Union[dict, BaseModel]]: A list of dictionaries with nested structures for joined table data or Pydantic models. Example: + Input: + + ```python data = [ - {'id': 1, 'title': 'Test Card', 'articles': [{'id': 1, 'title': 'Article 1', 'card_id': 1}]}, - {'id': 2, 'title': 'Test Card 2', 'articles': [{'id': 2, 'title': 'Article 2', 'card_id': 2}]}, - {'id': 2, 'title': 'Test Card 2', 'articles': [{'id': 3, 'title': 'Article 3', 'card_id': 2}]}, - {'id': 3, 'title': 'Test Card 3', 'articles': [{'id': None, 'title': None, 'card_id': None}]} + {'id': 1, 'title': 'Test Author', 'articles': [{'id': 1, 'title': 'Article 1', 'author_id': 1}]}, + {'id': 2, 'title': 'Test Author 2', 'articles': [{'id': 2, 'title': 'Article 2', 'author_id': 2}]}, + {'id': 2, 'title': 'Test Author 2', 'articles': [{'id': 3, 'title': 'Article 3', 'author_id': 2}]}, + {'id': 3, 'title': 'Test Author 3', 'articles': [{'id': None, 'title': None, 'author_id': None}]}, ] joins_config = [ JoinConfig(model=Article, join_prefix='articles_', relationship_type='one-to-many') ] + ``` Output: + + ```json [ { 'id': 1, - 'title': 'Test Card', + 'title': 'Test Author', 'articles': [ { 'id': 1, 'title': 'Article 1', - 'card_id': 1 + 'author_id': 1 } ] }, { 'id': 2, - 'title': 'Test Card 2', + 'title': 'Test Author 2', 'articles': [ { 'id': 2, 'title': 'Article 2', - 'card_id': 2 + 'author_id': 2 }, { 'id': 3, 'title': 'Article 3', - 'card_id': 2 + 'author_id': 2 } ] }, { 'id': 3, - 'title': 'Test Card 3', + 'title': 'Test Author 3', 'articles': [] } ] + ``` """ pre_nested_data = {}