Multi-Table Relationships
This example demonstrates working with multiple related tables in Da Vinci.
Overview
We’ll build a blog system with:
Users table
Posts table (belongs to user)
Comments table (belongs to post)
Relationships between tables
Table Definitions
from da_vinci.core.orm.table_object import (
TableObject,
TableObjectAttribute,
TableObjectAttributeType,
)
class UserTable(TableObject):
table_name = "users"
partition_key_attribute = "user_id"
attributes = [
TableObjectAttribute(
name="user_id",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="username",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="email",
attribute_type=TableObjectAttributeType.STRING,
),
]
class PostTable(TableObject):
table_name = "posts"
partition_key_attribute = "post_id"
global_secondary_indexes = [
{
"index_name": "user_posts_index",
"partition_key": "user_id",
"sort_key": "created_at",
}
]
attributes = [
TableObjectAttribute(
name="post_id",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="user_id",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="title",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="content",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="created_at",
attribute_type=TableObjectAttributeType.DATETIME,
),
]
class CommentTable(TableObject):
table_name = "comments"
partition_key_attribute = "comment_id"
global_secondary_indexes = [
{
"index_name": "post_comments_index",
"partition_key": "post_id",
"sort_key": "created_at",
}
]
attributes = [
TableObjectAttribute(
name="comment_id",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="post_id",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="user_id",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="content",
attribute_type=TableObjectAttributeType.STRING,
),
TableObjectAttribute(
name="created_at",
attribute_type=TableObjectAttributeType.DATETIME,
),
]
Service Implementation
import uuid
from datetime import UTC, datetime
from da_vinci.core.orm.client import TableClient
from tables import UserTable, PostTable, CommentTable
class BlogService:
def __init__(self):
self.user_client = TableClient(UserTable)
self.post_client = TableClient(PostTable)
self.comment_client = TableClient(CommentTable)
def create_post(self, user_id: str, title: str, content: str):
"""Create a new post"""
post = PostTable(
post_id=str(uuid.uuid4()),
user_id=user_id,
title=title,
content=content,
created_at=datetime.now(UTC),
)
self.post_client.put(post)
return post
def get_user_posts(self, user_id: str):
"""Get all posts by a user"""
return list(self.post_client.query(
index_name="user_posts_index",
partition_key_value=user_id
))
def add_comment(self, post_id: str, user_id: str, content: str):
"""Add a comment to a post"""
comment = CommentTable(
comment_id=str(uuid.uuid4()),
post_id=post_id,
user_id=user_id,
content=content,
created_at=datetime.now(UTC),
)
self.comment_client.put(comment)
return comment
def get_post_comments(self, post_id: str):
"""Get all comments for a post"""
return list(self.comment_client.query(
index_name="post_comments_index",
partition_key_value=post_id
))
def get_post_with_details(self, post_id: str):
"""Get post with author and comments"""
post = self.post_client.get(post_id)
author = self.user_client.get(post.user_id)
comments = self.get_post_comments(post_id)
return {
"post": post,
"author": author,
"comments": comments,
}
Usage Example
service = BlogService()
# Create a user
user = service.user_client.put(UserTable(
user_id="user-1",
username="alice",
email="alice@example.com"
))
# Create posts
post1 = service.create_post(
user_id=user.user_id,
title="My First Post",
content="Hello, world!"
)
post2 = service.create_post(
user_id=user.user_id,
title="Second Post",
content="More content here"
)
# Add comments
service.add_comment(
post_id=post1.post_id,
user_id=user.user_id,
content="Great post!"
)
# Get user's posts
user_posts = service.get_user_posts(user.user_id)
print(f"User has {len(user_posts)} posts")
# Get post with full details
details = service.get_post_with_details(post1.post_id)
print(f"Post: {details['post'].title}")
print(f"Author: {details['author'].username}")
print(f"Comments: {len(details['comments'])}")
Key Concepts
- Foreign Keys
Use string attributes (
user_id,post_id) to reference other tables.- GSI for Relationships
Create GSIs with foreign keys as partition keys to query related items.
- Service Aggregation
Service layer aggregates data from multiple tables.
- Denormalization
Consider duplicating data when read patterns require it.