Skip to content
immmdreza edited this page Jun 6, 2022 · 2 revisions

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

Read more about properties.

Intro

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.

More about Models

Here we can go deeper about models and build more complex models.

Datetime property

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

Optional properties

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).

List of thing as property

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

Complex properties

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?!

Relations

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 TEntitys, 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.

  1. BlogPost entity should have a reference field where User 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
  2. Add field blog_post to the User.

    # ---- 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.

One to Many relation

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.

Clone this wiki locally