5. Introduction to Pydantic for FastAPI


When our API/App is deployed, It is open to the world. There are all kinds of users, some may not understand exactly what to fill in a form, and some are malicious users who want to check if our application has security loopholes. Have you heard of SQL Injection attack? There are people out there who fill in SQL Query in the website forms. Basically, we can't always trust the web request data. In these cases, Pydantic comes to the rescue. Pydantic is useful for data validation and type hints.

To use pydantic you need to make sure that your virtual environment is activated and do a pip install pydantic. Now, we are ready to learn pydantic.

from pydantic import BaseModel

class Blog(BaseModel):
    title: str 
    is_active: bool

Blog(title="My First Blog",is_active=True)

The above snippet shows how we can inherit from BaseModel class of Pydantic and utilize its features. The above initialization of the Blog class with the given title and is_active flag is perfectly valid. However, it's the ideal case. Some people may try to send data with invalid inputs e.g.

Blog(title='Second One',is_active='yup!')

In this case the is_active flag got an incorrect value, We had defined the is_active field of type bool. So, the only allowed values are True/False. If you try to initialize with this input. You will get:

ValidationError: 1 validation error for Blog
is_active
  value could not be parsed to a boolean (type=type_error.bool)

Thus, pydantic makes sure that the input values respect the type hints. Now, let's explore some of the most common use cases.

 1. Accepting optional values:
Let's say that we want yet another field named description. However, a description has the liberty to be blank. It is not compulsory. We can make use of the Optional type hint that we learned in first article.

from pydantic import BaseModel

class Blog(BaseModel):
    title: str 
    description: Optional[str]=None
    is_active: bool

Blog(title="My First Blog",is_active=True)
# Output : Blog(title='My First Blog', description=None, is_active=True)


2. Accepting only a subset of strings.
It's a very common scenario. Many times what we want is to allow only a subset of strings. Let's say our Blog class has a property named language. Since, we only allow blogs on Java, Python, and Go. We want to restrict to only these 3 strings. We can accomplish this as follow.

from pydantic import BaseModel
from enum import Enum

class Languages(str,Enum):
    PY = "Python"
    JAVA = "Java"
    GO = "Go"

class Blog(BaseModel):
    title: str 
    language : Languages = Languages.PY
    is_active: bool
    


Blog(title="My First Blog",language="Java",is_active=True)
# Output: Blog(title='My First Blog', language=<Languages.JAVA: 'Java'>, is_active=True)

Blog(title="My First Blog",language="C++",is_active=True)
# Output: ValidationError: 1 validation error for Blog language value is not a valid enumeration member; permitted: 'Python', 'Java', 'Go' 

We can get the value of the language by querying a blog object as blog_object.language.value

3. Getting dynamic values at runtime
The simplest example for this would be to add a property named created_at in the blog. This property should get created dynamically whenever a blog object is created. You might be tempted to think that this is easy, all we need to use is to initialize the property with datetime.now().

import time
from pydantic import BaseModel
from datetime import datetime

class Blog(BaseModel):
    title: str 
    created_at: datetime = datetime.now()
    is_active: bool

print(Blog(title="Our First Blog",is_active=True))
time.sleep(5)
print(Blog(title="Our Second Blog",is_active=True))

#Output:
#title='Our First Blog' created_at=datetime.datetime(2022, 10, 7, 15, 52, 53, 748289) is_active=True
#title='Our Second Blog' created_at=datetime.datetime(2022, 10, 7, 15, 52, 53, 748289) is_active=True

However, this won't work, It is because the datetime instantiation will be done only at the time when the module is imported. To tackle such scenarios Pydantic provides us with a default_factory argument on a Field function.

import time
from pydantic import BaseModel,Field
from datetime import datetime

class Blog(BaseModel):
    title: str 
    created_at: datetime = Field(default_factory=datetime.now)
    is_active: bool

print(Blog(title="Our First Blog",is_active=True))
time.sleep(3)
print(Blog(title="Our Second Blog",is_active=True))

#Output: 
#title='...' created_at=datetime.datetime(2022, 10, 7, 15, 57, 46, 257846) is_active=True
#title='...' created_at=datetime.datetime(2022, 10, 7, 15, 57, 49, 261350) is_active=True

Notice the first object is instantiated at the 46th second and the 2nd one at the 49th second. The default_factory is not just restricted to call datetime.now, We can use it whenever we want to evaluate the value of a property at runtime🏃.

  

Till now, we have seen 3 common use cases of Pydantic. Let's move forward with the other common use cases.

4. Properties as Pydantic Models
We can actually have a nested pydantic model. I mean to say, we can use Pydantic models as fields and can have sub-objects! Say, we updated our blog to have a comment system. In this case, we can have a property named comment which itself can be a Pydantic class.

from pydantic import BaseModel
from typing import List

class Comment(BaseModel):
    text: Optional[str]=None

class Blog(BaseModel):
    title: str 
    comment: Optional[List[Comment]]
    is_active: bool

Blog(title="Our First Blog",comment=[{'text':'My comment'},{'text':'Your comment'},],is_active=True))

#Output: title='Our First Blog' comment=[Comment(text='My comment'), Comment(text='Your comment')] is_active=True

 

FastAPITutorial

Brige the gap between Tutorial hell and Industry. We want to bring in the culture of Clean Code, Test Driven Development.

We know, we might make it hard for you but definitely worth the efforts.

Contacts

Refunds:

Refund Policy
Social

Follow us on our social media channels to stay updated.

© Copyright 2022-23 Team FastAPITutorial