Document working with single-table design tables
Assuming I have table following a single-table design principle and composite keys, and records like:
# User records
{ pk: 'User#<userId>', sk: 'User', ... }
{ pk: 'User#<userId>', sk: 'Review#<reviewId>', ... }
# Post records
{ pk: 'Post#<postId>, sk: 'Post', ... }
{ pk: 'Post#<postId>, sk: 'Comment#<commentId>', ...}
Based on the docs, it's unclear how I'd define the following models:
UserUserReviewPostPostComment
I assume I could override all the CRUD methods to do something like:
class UserReview(Dyntastic):
__table_name__: ...
__hash_key__: 'pk'
__range_key__: 'sk'
@classmethod
def get(cls, user_id: str, review_id: str) -> Self:
pk = f'User#{user_id}'
sk = f'Review#{review_id}'
return cls.get(pk, sk)
Although this feels a little cumbersome.
Hey! Unfortunately, there's not a whole lot of lift that this library currently does for a single-table design (though it's something that I'd love to eventually support well). Something like you wrote is probably about as good as you can get right now.
One thing that is supported is overriding get_model on a parent class, which may your life a little easier by reusing a single parent table definition that loads the appropriate model based on the key prefixes:
class MyTable(Dyntastic):
__table_name__ = ...
__hash_key__ = "pk"
__range_key__ = "sk"
pk: str
sk: str
@classmethod
def get_model(cls, item):
# Whatever logic needed from the pk/sk to determine which sub-model is relevant
pk_label = item["pk"].split("#")[0]
sk_label = item["sk"].split("#")[0]
match (pk_label, sk_label):
case ("User", "User"): return User
case ("User", "Review"): return UserReview
case ("Post", "Post"): return Post
case ("Post", "Comment"): return Comment
class User(MyTable):
# ... additional fields relevant for user here
class UserReview(MyTable):
# ... additional fields relevant for user review here
...
This unfortunately still requires adding logic somewhere to inject the key prefixes when getting / constructing models, based on which model is being accessed. There's probably some clever way to do that without having to duplicate get and __init__ in all the subclasses, but I'd have to play around with it a bit to give a concrete suggestion.
That's cool! I don't mind this solution that much and it definitely avoids a lot of copy-pasting. Thanks!