Ormar allows you to declare normal pydantic fields in its model, so you have access to
all basic and custom pydantic fields like str, int, HttpUrl, PaymentCardNumber etc.
You can even declare fields leading to nested pydantic only Models, not only single fields.
Since those fields are not stored in database (that's the whole point of those fields),
you have to provide a meaningful value for them, either by setting a default one or
providing one during model initialization.
If ormar cannot resolve the value for pydantic field it will fail during loading data from the database,
with missing required value for declared pydantic field.
Options to provide a value are described below.
Of course you can combine few or all of them in one model.
Optional field
If you set a field as Optional, it defaults to None if not provided and that's
exactly what's going to happen during loading from database.
database=databases.Database(DATABASE_URL)metadata=sqlalchemy.MetaData()classBaseMeta(ormar.ModelMeta):metadata=metadatadatabase=databaseclassModelTest(ormar.Model):classMeta(BaseMeta):passid:int=ormar.Integer(primary_key=True)name:str=ormar.String(max_length=200)number:Optional[PaymentCardNumber]test=ModelTest(name="Test")asserttest.name=="Test"asserttest.numberisNonetest.number="123456789015"awaittest.save()test_check=awaitModelTest.objects.get()asserttest_check.name=="Test"# after load it's back to Noneasserttest_check.numberisNone
Field with default value
By setting a default value, this value will be set on initialization and database load.
Note that setting a default to None is the same as setting the field to Optional.
database=databases.Database(DATABASE_URL)metadata=sqlalchemy.MetaData()classBaseMeta(ormar.ModelMeta):metadata=metadatadatabase=databaseclassModelTest(ormar.Model):classMeta(BaseMeta):passid:int=ormar.Integer(primary_key=True)name:str=ormar.String(max_length=200)url:HttpUrl="https://www.example.com"test=ModelTest(name="Test")asserttest.name=="Test"asserttest.url=="https://www.example.com"test.url="https://www.sdta.ada.pt"asserttest.url=="https://www.sdta.ada.pt"awaittest.save()test_check=awaitModelTest.objects.get()asserttest_check.name=="Test"# after load it's back to defaultasserttest_check.url=="https://www.example.com"
Default factory function
By setting a default_factory function, this result of the function call will be set
on initialization and each database load.
frompydanticimportField,PaymentCardNumber# ...database=databases.Database(DATABASE_URL)metadata=sqlalchemy.MetaData()classBaseMeta(ormar.ModelMeta):metadata=metadatadatabase=databaseCARD_NUMBERS=["123456789007","123456789015","123456789023","123456789031","123456789049",]defget_number():returnrandom.choice(CARD_NUMBERS)classModelTest2(ormar.Model):classMeta(BaseMeta):passid:int=ormar.Integer(primary_key=True)name:str=ormar.String(max_length=200)# note that you do not call the function, just pass referencenumber:PaymentCardNumber=Field(default_factory=get_number)# note that you still CAN provide a value test=ModelTest2(name="Test2",number="4000000000000002")asserttest.name=="Test2"asserttest.number=="4000000000000002"awaittest.save()test_check=awaitModelTest2.objects.get()asserttest_check.name=="Test2"# after load value is set to be one of the CARD_NUMBERSasserttest_check.numberinCARD_NUMBERSasserttest_check.number!=test.number
Custom setup in __init__
You can provide a value for the field in your __init__() method before calling a super() init method.
frompydanticimportBaseModel# ...database=databases.Database(DATABASE_URL)metadata=sqlalchemy.MetaData()classBaseMeta(ormar.ModelMeta):metadata=metadatadatabase=databaseclassPydanticTest(BaseModel):aa:strbb:intclassModelTest3(ormar.Model):classMeta(BaseMeta):pass# provide your custom init functiondef__init__(self,**kwargs):# add value for required field without default valuekwargs["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:PydanticTesttest=ModelTest3(name="Test3")asserttest.name=="Test3"asserttest.pydantic_test.bb==42test.pydantic.aa="new value"asserttest.pydantic.aa=="new value"awaittest.save()test_check=awaitModelTest3.objects.get()asserttest_check.name=="Test3"# after load it's back to value provided in initasserttest_check.pydantic_test.aa=="random"
Warning
If you do not provide a value in one of the above ways ValidationError will be raised on load from database.