[PYTHON] Premiers pas avec Pydantic

Bonjour, êtes-vous Kakyo. Cette fois, j'aimerais écrire un article sur pydantic.

Qu'est-ce que Pydantic

Pydantic est une bibliothèque qui utilise des annotations de type Python pour fournir des indications de type lors de l'exécution et pour fournir facilement des paramètres d'erreur lors de la validation des données.

Cette bibliothèque est utile pour définir un modèle de base de données dans SQLAlchemy.

modèle

Tout d'abord, lorsque vous le définissez, définissez-le comme suit.


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

class Summer(BaseModel):
    id: int
    name: str   # (variable):(Moule)として、Mouleを宣言する
    friends: List[int] = []  # "="Vous pouvez également utiliser pour définir une valeur par défaut
    created_at: datetime


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

print(summer.id)

#> 1 #Même si la valeur saisie est de type chaîne, elle sera automatiquement convertie en type Int.

print(repr(summer.created_at))

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

print(user.friends)

#> [114,514]

print(user.dict())

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

"""

Ainsi, lorsqu'une erreur de validation se produit, cela ressemble à ceci:

try:
    User(created_at="Peint en noir",friends=[1,'2','voiture de luxe'])

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"
  }
]

"""

Ici, les objets renvoyés lorsqu'une erreur se produit sont les suivants.

Essayez de combiner avec SQLAlchemy

Maintenant, en utilisant SQLAlchemy, l'ORM Wrapper de Python, nous allons faire ce qui suit: Vous pouvez concevoir un modèle SQL.

Le code ci-dessous cite à partir d'ici.

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

Vous pouvez utiliser le ** Validator Decorator ** pour voir si les valeurs que vous entrez sont correctes et les relations complexes entre les objets.

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 Pour définir l'ordre de priorité lors du démarrage de Validator, utilisez les arguments suivants pre, pre_item. pre démarre le validateur avant tout autre validateur que vous avez défini. Si ʻeach_item = True`, la validation sera exécutée pour chaque élément tel que la liste et le dictionnaire.

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 Pour des raisons de performances, par défaut, ce validateur ne démarrera pas si aucune valeur n'est donnée. Cependant, l'argument doit être défini sur ʻalways = True` pour être exécuté même si aucune valeur n'est donnée.


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)

finalement

Ce qui précède est l'utilisation de base de Pydantic. Vous pouvez l'utiliser pour définir des validations et des types dans Python pour les steamers SQL. Pour les paramètres détaillés, reportez-vous au document officiel ci-dessous.

Les références

Documentation officielle de Pydanit (https://pydantic-docs.helpmanual.io/)

Recommended Posts

Premiers pas avec Pydantic
Premiers pas avec Android!
1.1 Premiers pas avec Python
Premiers pas avec apache2
Premiers pas avec Python
Premiers pas avec Django 1
Introduction à l'optimisation
Premiers pas avec Numpy
Premiers pas avec Spark
Premiers pas avec Python
Premiers pas avec Jython
Premiers pas avec Django 2
Traduire Premiers pas avec TensorFlow
Introduction aux fonctions Python
Introduction à Tkinter 2: Button
Premiers pas avec Go Assembly
Premiers pas avec PKI avec Golang ―― 4
Premiers pas avec Python Django (1)
Premiers pas avec Python Django (4)
Premiers pas avec Python Django (3)
Introduction à Python Django (6)
Premiers pas avec Python Django (5)
Introduction à Git (1) Stockage d'historique
Premiers pas avec Sphinx. Générer docstring avec Sphinx
Premiers pas avec Python pour les classes PHPer
Premiers pas avec Julia pour Pythonista
Premiers pas avec Python Bases de Python
Premiers pas avec Cisco Spark REST-API
Commençant par USD sur Windows
Premiers pas avec les algorithmes génétiques Python
Premiers pas avec Python 3.8 sous Windows
Premiers pas avec Python pour les fonctions PHPer
Premiers pas avec CPU Steal Time
Grails pour commencer
Premiers pas avec Python Web Scraping Practice
Premiers pas avec Python pour PHPer-Super Basics
Premiers pas avec Python Web Scraping Practice
Premiers pas avec Dynamo de Python boto
Premiers pas avec Lisp pour Pythonista: Supplément
Premiers pas avec Heroku, déploiement de l'application Flask
Premiers pas avec TDD avec Cyber-dojo chez MobPro
Démarrer avec Python avec 100 coups sur le traitement du langage
Django 1.11 a démarré avec Python3.6
Principes de base de MongoDB: Premiers pas avec CRUD avec JAVA
Premiers pas avec le dessin avec matplotlib: écrire des fonctions simples
Premiers pas avec la traduction japonaise du modèle séquentiel Keras
[Français] Premiers pas avec Rust pour les programmeurs Python
Django Getting Started Part 2 avec eclipse Plugin (PyDev)
Démarrez avec MicroPython
Premiers pas avec AWS IoT facilement en Python
Premiers pas avec le module ast de Python (à l'aide de NodeVisitor)
Matériel à lire lors de la mise en route de Python
Paramètres pour démarrer avec MongoDB avec python
Premiers pas avec les pandas: connaissances de base à retenir en premier
Premiers pas avec Google App Engine pour Python et PHP