Skip to content

Pydantic field only

Ormar allows you to declare plain pydantic fields in its model, so you can access all basic and custom pydantic fields such as str, int, HttpUrl, PaymentCardNumber, etc.

You can even declare fields leading to nested pydantic models only, not just individual fields.

Because these fields are not stored in the database (that's all they mean), you must provide them with a meaningful value, either by setting a default value or during model initialization.

If ormar fails to resolve the value of the pydantic field, it fails during the load of data from the database and lacks the required value for the declared pydantic field.

The options for supplying values are described below.

Of course, you can combine some or all of them into one model.

optional field

If the field is set to optional, it defaults to None if not provided, which is exactly what happens during loading from the database.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
base_ormar_config = ormar.OrmarConfig(
    metadata=sqlalchemy.MetaData(),
    database=databases.Database(DATABASE_URL),
)


class ModelTest(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=200)
    number: Optional[PaymentCardNumber]

test = ModelTest(name="Test")
assert test.name == "Test"
assert test.number is None
test.number = "123456789015"

await test.save()
test_check = await ModelTest.objects.get()

assert test_check.name == "Test"
# after load it's back to None
assert test_check.number is None

Fields with default values

By setting the default value, the value is set at initialization and database load time. Note that setting the default value to None is the same as setting the field to Optional.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
base_ormar_config = ormar.OrmarConfig(
    metadata=sqlalchemy.MetaData(),
    database=databases.Database(DATABASE_URL),
)


class ModelTest(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=200)
    url: HttpUrl = "https://www.example.com"

test = ModelTest(name="Test")
assert test.name == "Test"
assert test.url == "https://www.example.com"

test.url = "https://www.sdta.ada.pt"
assert test.url == "https://www.sdta.ada.pt"

await test.save()
test_check = await ModelTest.objects.get()

assert test_check.name == "Test"
# after load it's back to default
assert test_check.url == "https://www.example.com"

Default Factory Features

By setting the default_factory function, the result of the function call is set at initialization and each time the database is loaded.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from pydantic import Field, PaymentCardNumber
# ...


base_ormar_config = ormar.OrmarConfig(
    metadata=sqlalchemy.MetaData(),
    database=databases.Database(DATABASE_URL),
)


CARD_NUMBERS = [
    "123456789007",
    "123456789015",
    "123456789023",
    "123456789031",
    "123456789049",
]


def get_number():
    return random.choice(CARD_NUMBERS)


class ModelTest2(ormar.Model):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=200)
    # note that you do not call the function, just pass reference
    number: PaymentCardNumber = Field(default_factory=get_number)

# note that you still CAN provide a value 
test = ModelTest2(name="Test2", number="4000000000000002")
assert test.name == "Test2"
assert test.number == "4000000000000002"

await test.save()
test_check = await ModelTest2.objects.get()

assert test_check.name == "Test2"
# after load value is set to be one of the CARD_NUMBERS
assert test_check.number in CARD_NUMBERS
assert test_check.number != test.number

Custom settings in__init__

You can provide a value for this field in the__init__() method before calling the super() init method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from pydantic import BaseModel
# ...


base_ormar_config = ormar.OrmarConfig(
    metadata=sqlalchemy.MetaData(),
    database=databases.Database(DATABASE_URL),
)


class PydanticTest(BaseModel):
    aa: str
    bb: int


class ModelTest3(ormar.Model):
    ormar_config = base_ormar_config.copy()

    # provide your custom init function
    def __init__(self, **kwargs):
        # add value for required field without default value
        kwargs["pydantic_test"] = PydanticTest(aa="random", bb=42)
        # remember to call ormar.Model init!
        super().__init__(**kwargs)

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=200)
    pydantic_test: PydanticTest

test = ModelTest3(name="Test3")
assert test.name == "Test3"
assert test.pydantic_test.bb == 42
test.pydantic.aa = "new value"
assert test.pydantic.aa == "new value"

await test.save()
test_check = await ModelTest3.objects.get()

assert test_check.name == "Test3"
# after load it's back to value provided in init
assert test_check.pydantic_test.aa == "random"

!!!Warning If you do not provide a value in one of the above ways, a ValidationError will be raised when loading from the database.