Skip to content

Nested Models

The very first thing you need to create for pydantic-redis are the models (or schemas) that the data you are to save in redis is to be based on.

It is possible to refer one model in another model in a parent-child relationship.

Import Pydantic-redis' Model

First, import pydantic-redis' Model

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Create the Child Model

Next, declare a new model as a class that inherits from Model.

Use standard Python types for all attributes.

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Set the _primary_key_field of the Child Model

Set the _primary_key_field attribute to the name of the attribute that is to act as a unique identifier for each instance of the Model.

Example

In this case, there can be no two authors with the same name.

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Create the Parent Model

Next, declare another model as a class that inherits from Model.

Use standard Python types for all attributes, as before.

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Add the Nested Model to the Parent Model

Annotate the field that is to hold the child model with the child class.

Example

In this case, the field author is annotated with Author class.

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Set the _primary_key_field of the Parent Model

Set the _primary_key_field attribute to the name of the attribute that is to act as a unique identifier for each instance of the parent Model.

Example

In this case, there can be no two books with the same title.

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Register the Models in the Store

Then, in order for the store to know the existence of each given model, register it using the register_model method of Store

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Use the Parent Model

Then you can use the parent model class to:

  • insert into the store
  • update an instance of the model
  • delete from store
  • select from store

Note

The child model will be automatically inserted, or updated if it already exists

Info

The store is connected to the Redis instance, so any changes you make will reflect in redis itself.

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Use the Child Model Independently

You can also use the child model independently.

Info

Any mutation on the child model will also be reflected in the any parent model instances fetched from redis after that mutation.

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Indirectly Update Child Model

A child model can be indirectly updated via the parent model.

Set the attribute containing the child model with an instance of the child model

Note

The new instance of the child model should have the SAME primary key as the original child model.

import pprint
from datetime import date
from typing import List, Tuple

from pydantic_redis import Model, Store, RedisConfig


class Author(Model):
    _primary_key_field: str = "name"
    name: str
    active_years: Tuple[int, int]


class Book(Model):
    _primary_key_field: str = "title"
    title: str
    author: Author
    rating: float
    published_on: date
    tags: List[str] = []
    in_stock: bool = True


if __name__ == "__main__":
    pp = pprint.PrettyPrinter(indent=4)
    store = Store(
        name="some_name",
        redis_config=RedisConfig(db=5, host="localhost", port=6379),
        life_span_in_seconds=3600,
    )

    store.register_model(Author)
    store.register_model(Book)

    Book.insert(
        Book(
            title="Oliver Twist",
            author=Author(name="Charles Dickens", active_years=(1999, 2007)),
            published_on=date(year=1215, month=4, day=4),
            in_stock=False,
            rating=2,
            tags=["Classic"],
        )
    )

    book_response = Book.select(ids=["Oliver Twist"])
    author_response = Author.select(ids=["Charles Dickens"])

    Author.update(_id="Charles Dickens", data={"active_years": (1227, 1277)})
    updated_book_response = Book.select(ids=["Oliver Twist"])

    Book.update(
        _id="Oliver Twist",
        data={"author": Author(name="Charles Dickens", active_years=(1969, 1999))},
    )
    updated_author_response = Author.select(ids=["Charles Dickens"])

    print("book:")
    pp.pprint(book_response)
    print("\nauthor:")
    pp.pprint(author_response)

    print("\nindirectly updated book:")
    pp.pprint(updated_book_response)
    print("\nindirectly updated author:")
    pp.pprint(updated_author_response)

Run the App

Running the above code in a file main.py would produce:

Tip

Probably FLUSHALL redis first

$ python main.py
book:
[   Book(title='Oliver Twist', author=Author(name='Charles Dickens', active_years=(1999, 2007)), rating=2.0, published_on=datetime.date(1215, 4, 4), tags=['Classic'], in_stock=False)]

author:
[Author(name='Charles Dickens', active_years=(1999, 2007))]

indirectly updated book:
[   Book(title='Oliver Twist', author=Author(name='Charles Dickens', active_years=(1227, 1277)), rating=2.0, published_on=datetime.date(1215, 4, 4), tags=['Classic'], in_stock=False)]

indirectly updated author:
[Author(name='Charles Dickens', active_years=(1969, 1999))]