-
Notifications
You must be signed in to change notification settings - Fork 0
Models
A model is a representation of your data, the data you're going to save!
A model is just a python class with some attributes.
Every model in this package should be an instance of TEntity
.
from sjd import TEntity
A model's attributes that you want to save are called properties and you have to mark them and specify their type as well.
You can have access to all properties like this:
from sjd import TEntity, properties as props
Let us continue with an example.
Assume you wanna save data about your Blog posts, then you'll create a Post class as model.
class BlogPost(TEntity):
pass
Well that's obvious this piece of code is useless and meaningless too, since we need to specify properties.
Thinking of a blog post, it may contain title
, publisher_name
or author_name
,
text
, published_date
and etc.
We'll implement first three for now.
from sjd import TEntity, properties as props
class BlogPost(TEntity):
title = props.string(required=True)
author_name = props.string(required=True)
text = props.string(required=True)
This's a good setup, but it's a pain to create instance of models without a proper __init__
, so we'll include it and we use props.auto_collect()
(Read more) to collect props from __init__
method.
So, here is another setup that dose exactly the same thing as above but better.
from sjd import TEntity, properties as props
@props.auto_collect()
class BlogPost(TEntity):
def __init__(self, title: str, author_name: str, text: str):
self.title = title
self.author_name = author_name
self.text = text
And just that simple, you created a working model.
Now you can setup the Engine and work with data.
Here we can go deeper about models and build more complex models.
The package can even save datetime
objects. So we can now add a field for published_date
property.
Datetime objects are saved as string and in ISO format.
import datetime
from sjd import TEntity, properties as props
@props.auto_collect()
class BlogPost(TEntity):
def __init__(self, title: str, author_name: str, text: str, published_date: datetime.datetime):
self.title = title
self.author_name = author_name
self.text = text
self.published_date = published_date
In real world models, there're always some properties that are not required. You can mark them as optional for better type hinting.
In BlogPost
example, we can include another property: description
. This property
is optional and can be None
.
# ---- sniff ----
@props.auto_collect()
class BlogPost(TEntity):
def __init__(
self,
title: str,
author_name: str,
text: str,
published_date: datetime.datetime,
description: str | None = None
):
self.title = title
self.author_name = author_name
self.text = text
self.published_date = published_date
self.description = description
SJD will know that description
can be missing and it's not required (And you must set default value for optional properties).
You can also include a list of objects inside your model.
Every blog post can have some tags
that is useful for searching and categorizing.
Not even a problem!
# ---- sniff ----
@props.auto_collect()
class BlogPost(TEntity):
def __init__(
self,
title: str,
author_name: str,
text: str,
published_date: datetime.datetime,
tags: list[str],
description: str | None = None
):
self.title = title
self.author_name = author_name
self.text = text
self.published_date = published_date
self.tags = tags
self.description = description
Not complex numbers! from complex, i mean other models.
You can use another model inside your model as an EmbeddedEntity
.
As instance we may want to add information about an admin who verified the post.
import datetime
from sjd import EmbeddedEntity, TEntity, properties as props
@props.auto_collect()
class VerifyInfo(EmbeddedEntity):
def __init__(self, name: str, verified_at: datetime.datetime, admin_rate: int):
self.name = name
self.verified_at = verified_at
self.admin_rate = admin_rate
Now, you can use it inside your main model.
# ---- sniff ----
@props.auto_collect()
class VerifyInfo(EmbeddedEntity):
def __init__(self, name: str, verified_at: datetime.datetime, admin_rate: int):
self.name = name
self.verified_at = verified_at
self.admin_rate = admin_rate
@props.auto_collect()
class BlogPost(TEntity):
def __init__(
self,
title: str,
author_name: str,
text: str,
published_date: datetime.datetime,
tags: list[str],
verify_info: VerifyInfo,
description: str | None = None
):
self.title = title
self.author_name = author_name
self.text = text
self.published_date = published_date
self.tags = tags
self.verify_info = verify_info
self.description = description
It's getting big ... yeah?!
Using complex properties inside the model is a good idea, but sometimes it's not necessary and you don't always need all data together.
Here you can setup relations between two separate TEntity
s, not using EmbeddedEntity
.
About the author of post, it can be saved inside another model like User
in a separate model. then assign this BlogPost
to the User
.
Let's create user model:
# ---- sniff ----
@props.auto_collect()
class User(TEntity):
def __init__(self, first_name: str, last_name: str | None, username: str | None):
self.first_name = first_name
self.last_name = last_name
self.username = username
Now it's our time to shine! let make this User
related to a BlogPost
.
-
BlogPost
entity should have a reference field whereUser
can point to.# ---- sniff ---- @props.auto_collect() class BlogPost(TEntity): user_id = props.reference() # That's all. def __init__( self, title: str, author_name: str, text: str, published_date: datetime.datetime, tags: list[str], verify_info: VerifyInfo, description: str | None = None ): self.title = title self.author_name = author_name self.text = text self.published_date = published_date self.tags = tags self.verify_info = verify_info self.description = description
-
Add field
blog_post
to theUser
.# ---- sniff ---- @props.auto_collect() class User(TEntity): blog_post = props.from_entity(BlogPost, "user_id") def __init__( self, first_name: str, last_name: str | None, username: str | None, blog_post: BlogPost | None = None ): self.first_name = first_name self.last_name = last_name self.username = username self.blog_post = blog_post
Since from_entity()
and reference()
are not normal properties, you must define them directly and not only in __init__
.
Full code looks like this:
# ---- sniff ----
@props.auto_collect()
class VerifyInfo(EmbeddedEntity):
def __init__(self, name: str, verified_at: datetime.datetime, admin_rate: int):
self.name = name
self.verified_at = verified_at
self.admin_rate = admin_rate
@props.auto_collect()
class BlogPost(TEntity):
user_id = props.reference()
def __init__(
self,
title: str,
text: str,
published_date: datetime.datetime,
tags: list[str],
verify_info: VerifyInfo,
description: str | None = None
):
self.title = title
self.text = text
self.published_date = published_date
self.tags = tags
self.verify_info = verify_info
self.description = description
@props.auto_collect()
class User(TEntity):
blog_post = props.from_entity(BlogPost, "user_id")
def __init__(
self,
first_name: str,
last_name: str | None,
username: str | None,
blog_post: BlogPost | None = None
):
self.first_name = first_name
self.last_name = last_name
self.username = username
self.blog_post = blog_post
Note that, author_name
is removed from BlogPost
, since it's not needed anymore.
Now, BlogPost
and User
are saved in different places. But these are related to each other.
See Virtual Properties to learn more about related models.
I know that a user can have more than one BlogPost
and it's easy to implement.
Just replace props.from_entity()
with props.from_entities()
in User
model.
@props.auto_collect()
class User(TEntity):
blog_posts = props.from_entities(BlogPost, "user_id")
def __init__(
self,
first_name: str,
last_name: str | None,
username: str | None,
blog_posts: list[BlogPost] = []
):
self.first_name = first_name
self.last_name = last_name
self.username = username
self.blog_posts = blog_posts
Now, User
has a virtual list of BlogPosts
.