[PYTHON] Getting Started with Pydantic

Hello, are you Kakyo. This time, I would like to write an article about pydantic.

What is Pydantic

Pydantic is a library that uses Python type annotations to provide run-time type hints and easily provide error settings during data validation.

This library is useful for defining database models in SQLAlchemy.

model

First, when defining it, define it as follows.


from datetime import datetime
from typing import List
from pydantic import BaseModel

class Summer(BaseModel):
    id: int
    name: str   # (variable):(Mold)として、Moldを宣言する
    friends: List[int] = []  # "="You can also use to define a default value
    created_at: datetime


external_data={
    'id': '1',
    'name' :'Beast senior',
    'created_at': '2019-11-03 03:34',
    'friends': [114,'514']
}
summer = Summer(**external_data)

print(summer.id)

#> 1 #Even if the entered value is a string type, it will be automatically converted to an Int type.

print(repr(summer.created_at))

#> datetime.datetime(2019, 11, 3, 3, 34)

print(user.friends)

#> [114,514]

print(user.dict())

"""
{
   'id': 1,
   'name': 'Beast senior', 
   'created_at': datetime.datetime(2019, 11, 3, 3, 34),
   'friends': [114, 514]
}

"""

So, when a Validation Error occurs, it looks like this:

try:
    User(created_at="Painted in black",friends=[1,'2','luxury car'])

except ValidationError as e:
    print(e.json())

"""
[
  {
    "loc": [
      "id"
    ],
    "msg": "field required",
    "type": "value_error.missing"
  },
  {
    "loc": [
      "name"
    ],
    "msg": "field required",
    "type": "value_error.missing"
  },
  {
    "loc": [
      "created_at"
    ],
    "msg": "invalid datetime format",
    "type": "type_error.datetime"
  },
  {
    "loc": [
      "friends",
      2
    ],
    "msg": "value is not a valid integer",
    "type": "type_error.integer"
  }
]

"""

Here, the objects returned when an error occurs are as follows.

Try it in combination with SQLAlchemy

Now, using SQLAlchemy, Python's ORM Wrapper, we'll do the following: You can design an SQL model.

The code below quotes from here.

from typing import List
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.declarative import declarative_base
from pydantic import BaseModel, constr

Base = declarative_base()

class CompanyOrm(Base):
    __tablename__ = 'companies'
    id = Column(Integer, primary_key=True, nullable=False)
    public_key = Column(String(20), index=True, nullable=False, unique=True)
    name = Column(String(63), unique=True)
    domains = Column(ARRAY(String(255)))

class CompanyModel(BaseModel):
    id: int
    public_key: constr(max_length=20)
    name: constr(max_length=63)
    domains: List[constr(max_length=255)]

    class Config:
        orm_mode = True
        
co_orm = CompanyOrm(
    id=123,
    public_key='foobar',
    name='Testing',
    domains=['example.com', 'foobar.com']
)
print(co_orm)
#> <orm_mode.CompanyOrm object at 0x7f0de1bc1cd0>
co_model = CompanyModel.from_orm(co_orm)
print(co_model)
#> id=123 public_key='foobar' name='Testing' domains=['example.com',
#> 'foobar.com']

Validators

You can use the ** Validator Decorator ** to see if the values you enter are correct and the complex relationships between the objects.

from pydantic import BaseModel, ValidationError, validator

class UserModel(BaseModel):
    name: str
    username: str
    password1: str
    password2: str

    @validator('name')
    def name_must_contain_space(cls, v):
        if ' ' not in v:
            raise ValueError('must contain a space')
        return v.title()

    @validator('password2')
    def passwords_match(cls, v, values, **kwargs):
        if 'password1' in values and v != values['password1']:
            raise ValueError('passwords do not match')
        return v

    @validator('username')
    def username_alphanumeric(cls, v):
        assert v.isalpha(), 'must be alphanumeric'
        return v

print(UserModel(name='samuel colvin', username='scolvin', password1='zxcvbn',
                password2='zxcvbn'))
#> name='Samuel Colvin' username='scolvin' password1='zxcvbn' password2='zxcvbn'

try:
    UserModel(name='samuel', username='scolvin', password1='zxcvbn',
              password2='zxcvbn2')
except ValidationError as e:
    print(e)
"""
2 validation errors for UserModel
name
  must contain a space (type=value_error)
password2
  passwords do not match (type=value_error)
"""

Pre and per-item validators To set the priority when starting the Validator, use the following arguments pre, pre_item. pre starts the Validator before any other Validator you have set. If ʻeach_item = True`, Validation will be executed for each element such as list and dictionary.

from typing import List
from pydantic import BaseModel, ValidationError, validator

class DemoModel(BaseModel):
    square_numbers: List[int] = []
    cube_numbers: List[int] = []

    # '*' is the same as 'cube_numbers', 'square_numbers' here:
    @validator('*', pre=True)
    def split_str(cls, v):
        if isinstance(v, str):
            return v.split('|')
        return v

    @validator('cube_numbers', 'square_numbers')
    def check_sum(cls, v):
        if sum(v) > 42:
            raise ValueError(f'sum of numbers greater than 42')
        return v

    @validator('square_numbers', each_item=True)
    def check_squares(cls, v):
        assert v ** 0.5 % 1 == 0, f'{v} is not a square number'
        return v

    @validator('cube_numbers', each_item=True)
    def check_cubes(cls, v):
        # 64 ** (1 / 3) == 3.9999999999999996 (!)
        # this is not a good way of checking cubes
        assert v ** (1 / 3) % 1 == 0, f'{v} is not a cubed number'
        return v

print(DemoModel(square_numbers=[1, 4, 9]))
#> square_numbers=[1, 4, 9] cube_numbers=[]
print(DemoModel(square_numbers='1|4|16'))
#> square_numbers=[1, 4, 16] cube_numbers=[]
print(DemoModel(square_numbers=[16], cube_numbers=[8, 27]))
#> square_numbers=[16] cube_numbers=[8, 27]
try:
    DemoModel(square_numbers=[1, 4, 2])
except ValidationError as e:
    print(e)
"""
1 validation error for DemoModel
square_numbers -> 2
  2 is not a square number (type=assertion_error)
"""

try:
    DemoModel(cube_numbers=[27, 27])
except ValidationError as e:
    print(e)
"""
1 validation error for DemoModel
cube_numbers
  sum of numbers greater than 42 (type=value_error)
"""

Validate Always By default, for performance reasons, this Validator will not start if no value is given. However, the argument must be set to ʻalways = True` in order to run even if no value is given.


from datetime import datetime

from pydantic import BaseModel, validator

class DemoModel(BaseModel):
    ts: datetime = None

    @validator('ts', pre=True, always=True)
    def set_ts_now(cls, v):
        return v or datetime.now()

print(DemoModel())
#> ts=datetime.datetime(2019, 10, 24, 15, 7, 51, 449261)
print(DemoModel(ts='2017-11-08T14:00'))
#> ts=datetime.datetime(2017, 11, 8, 14, 0)

Finally

The above is the basic usage of Pydantic. You can use this to set validation and types in the SQL steamer in Python. For detailed settings, refer to the official document below.

References

Official Pydanit documentation (https://pydantic-docs.helpmanual.io/)

Recommended Posts

Getting Started with Pydantic
Getting started with Android!
1.1 Getting Started with Python
Getting Started with Golang 2
Getting started with apache2
Getting Started with Golang 1
Getting Started with Python
Getting Started with Django 1
Getting Started with Optimization
Getting Started with Golang 3
Getting Started with Numpy
Getting started with Spark
Getting Started with Python
Getting Started with Golang 4
Getting Started with Jython
Getting Started with Django 2
Translate Getting Started With TensorFlow
Getting Started with Python Functions
Getting Started with Tkinter 2: Buttons
Getting Started with Go Assembly
Getting Started with PKI with Golang ―― 4
Getting Started with Python Django (1)
Getting Started with Python Django (4)
Getting Started with Python Django (3)
Getting Started with Python Django (6)
Python3 | Getting Started with numpy
Getting Started with Python Django (5)
Getting Started with Git (1) History Storage
Getting started with Sphinx. Generate docstring with Sphinx
Getting Started with Python for PHPer-Classes
Getting Started with Julia for Pythonista
Getting Started with Python Basics of Python
Getting Started with Cisco Spark REST-API
Getting started with USD on Windows
Getting Started with Python Genetic Algorithms
Getting started with Python 3.8 on Windows
Getting Started with Python for PHPer-Functions
Getting Started with CPU Steal Time
Grails getting started
Getting Started with Flask with Azure Web Apps
Getting Started with Python Web Scraping Practice
Getting Started with Python for PHPer-Super Basics
Getting Started with Python Web Scraping Practice
Getting started with Dynamo from Python boto
Getting Started with Lisp for Pythonista: Supplement
Getting Started with Heroku, Deploying Flask App
Getting Started with TDD with Cyber-dojo at MobPro
Getting started with Python with 100 knocks on language processing
Django 1.11 started with Python3.6
MongoDB Basics: Getting Started with CRUD in JAVA
Getting Started with Drawing with matplotlib: Writing Simple Functions
Getting started with Keras Sequential model Japanese translation
[Translation] Getting Started with Rust for Python Programmers
Django Getting Started Part 2 with eclipse Plugin (PyDev)
Get started with MicroPython
Getting started with AWS IoT easily in Python
Getting Started with Python's ast Module (Using NodeVisitor)
Materials to read when getting started with Python
Settings for getting started with MongoDB in python
Getting Started with pandas: Basic Knowledge to Remember First
Getting Started with Google App Engine for Python & PHP