redis-om-python icon indicating copy to clipboard operation
redis-om-python copied to clipboard

When using `StrictInt`, integer values can't be retrieved with `get`?

Open simonprickett opened this issue 2 years ago • 2 comments

I've created a basic model and set an int field to be StrictInt... this saves to Redis fine, but fails on retrieval... I imagine because this is really stored as a string in Redis.

Example code:

from redis_om import HashModel
from pydantic import StrictInt, ValidationError

class Adoptable(HashModel):
    name: str
    species: str
    age: StrictInt
    weight: float
    description: str

try:
    fido = Adoptable(
        name = "Fido",
        species = "dog",
        age = 3,
        weight = 37.2,
        description = "Fido is a fantastic dog in need of a new home"
    )

    print(fido.key())
    print("Saving Fido...")
    fido.save()

    print("Retrieving Fido...")
    an_adoptable = Adoptable.get(fido.pk)

    print(an_adoptable)

except ValidationError as e:
    print(e)

Running this yields:

$ python demo.py
:__main__.Adoptable:01FP6B9YCW6CZHNN1EY336WZ89
Saving Fido...
Retrieving Fido...
1 validation error for Adoptable
age
  value is not a valid integer (type=type_error.integer)

Python version:

$ python --version
Python 3.9.5

redis-om-python version:

redis-om==0.0.15

I was using this as a guide: https://github.com/redis/redis-om-python/blob/main/docs/getting_started.md#strict-validation

simonprickett avatar Dec 05 '21 22:12 simonprickett

Guess I didn't test that well enough!

You're right, a strict field will have trouble reading strings from Redis.

To use strict fields, you'd want to have two models -- one for creating data (when you'd want to check the incoming values) and one for reading data from redis. Like this:

import abc
from redis_om import HashModel
from pydantic import StrictInt, ValidationError


class AdoptableBase(HashModel, abc.ABC):
    name: str
    species: str
    weight: float
    description: str


class ReadAdoptable(AdoptableBase):
    age: int

    class Meta:
        model_key_prefix = "adoptable"


class WriteAdoptable(AdoptableBase):
    age: StrictInt

    class Meta:
        model_key_prefix = "adoptable"


try:
    fido = WriteAdoptable(
        name = "Fido",
        species = "dog",
        age = 3,
        weight = 37.2,
        description = "Fido is a fantastic dog in need of a new home"
    )

    print(fido.key())
    print("Saving Fido...")
    fido.save()

    print("Retrieving Fido...")
    an_adoptable = ReadAdoptable.get(fido.pk)

    print(an_adoptable)

except ValidationError as e:
    print(e)

It's a little annoying that a model doesn't inherit the model_key_prefix from its parent class's Meta class. It probably should, for cases like this. If it did, the models would be less verbose:

class AdoptableBase(HashModel, abc.ABC):
    name: str
    species: str
    weight: float
    description: str


class ReadAdoptable(AdoptableBase):
    age: int


class WriteAdoptable(AdoptableBase):
    age: StrictInt

NOTE: With Pydantic (the validation library under this stuff), you can't just subclass a class and override a single property, so we need a base class here and two subclasses.

And for reference, here is the code that unsets model_key_prefix if a subclass gets it from a parent: https://github.com/redis/redis-om-python/blob/3a3ed91ed03732789cca12b6ca3aed6cb5c5467b/aredis_om/model/model.py#L1029

This logic should probably be:

  1. Is the parent class an ABC? Then it's ok to inherit those two values.
  2. Otherwise, we should unset them.

abrookins avatar Dec 05 '21 23:12 abrookins

Oh, there's also the construct() method: https://pydantic-docs.helpmanual.io/usage/models/#creating-models-without-validation

I didn't go that route, but we could treat the data in Redis as already validated when we pull it back out. This probably makes sense. The only thing is, we'd need to test if that will coerce the values to their expected types, like parse_obj() does.

Here's one of the usages of parse_obj(): https://github.com/redis/redis-om-python/blob/3a3ed91ed03732789cca12b6ca3aed6cb5c5467b/aredis_om/model/model.py#L1326

abrookins avatar Dec 06 '21 00:12 abrookins