ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • pydantic이란? (DRF Serializer를 대체할 수 있을까?)
    파이썬/Django 2021. 12. 14. 22:45

    들어가며

    pydantic은 타입 애너테이션을 사용해서 데이터를 검증하고 설정들을 관리하는 라이브러리이다. pydantic은 런타임 환경에서 타입을 강제하고 타입이 유효하지 않을 때 에러를 발생시켜준다. FastAPI, Project Jupyter, Microsoft, AWS 등 많은 곳에서 사용된다.

    pydantic의 기본과 간단한 사용법을 알아보고 DRF Serializer과 바교해보자

     

    한눈에 보기

    from datetime import datetime
    from typing import List, Optional
    from pydantic import BaseModel
    
    class User(BaseModel):
        id: int
        name = '홍길동'
        date_joined: Optional[datetime] = None
        friends: List[int] = []
    
    
    data = {
        'id': 1,
        'date_joined': '2021-12-08 10:02',
        'friends': [2, 3, '4']
    }
    
    user = User(**data)
    print(user.id) # 1
    print(user.date_joined) # datetime.datetime(2019, 6, 1, 12, 22)
    print(friends) # 2, 3, 4

     

    각 필드를 보면서 알아보자

    id

    타입 애너테이션으로만 정의할 경우, pydantic 에게 이 필드가 required라는 것을 알려준다. str, float, byte 이 input으로 들어오면 int 형으로 강제변환해준다.

    name

    디폴트로 str 타입의 '홍길동'이 정의되었으므로 str형이라는 것을 알 수 있다. 디폴트 값이 들어있으므로 required는 아니다.

    date_joined

    datetime 필드이고 None이 디폴트로 정의되었으므로 required는 아니다. pydantic은 int형의 유닉스 타임스팸프나 str 형식으로 date와 time을 나타내고 있으면 알아서 처리해준다.

    friends

    python의 typing 모듈을 사용한다. List 안에 Integer-like 원소가 있으면 Int형으로 바꿔준다.

     

    특징

    1. 빠르다.

    DRF(3.12.2)의 Serializer보다 12배 정도 빠르다.

    2. 복잡한 구조를 validation 처리할 수 있다.

    DRF처럼 nested 구조를 validation 처리 가능하다.

    class Location(BaseModel):
        latitude: float = None
        longitude: float = None
    
    class Address(BaseModel):
        id: int
        name: str
        location: Location = None
    
        @validator('name')
        def validate_name(cls, v):
            ...
    
    
    data = {
        'id': 1,
        'name': 'My home',
        'location': {
            'latitude': 123.456,
           'longitude': 123.456
        }
    }
    
    my_home = Address(**data)
    print(repr(my_home.location)) # Location(latitude=123.456, longitude=123.456)

    3. 커스텀한 데이터 형식을 정의하거나 validator 데코레이터를 붙인 메서드를 만들어 추가적인 validation이 가능하다.

    class Address(BaseModel):
        id: int
        name: str
        location: Location = None
    
        @validate('name')
        def validate_name(cls, v):
            ...

     

    주요 기능

    BaseModel

    입력데이터를 보장해주는게 아니라 입력을 받아 데이터 형식과 제약조건을 보장해주는 모델이다.

    In other words, pydantic guarantees the types and constraints of the output model, not the input data.
    from pydantic import BaseModel
    
    class User(BaseModel):
        id: int
        name = '홍길동'
    
    user = User(id=1)

    User 클래스는 id, name 필드를 가지고 있다. id 는 int형 데이터이며 required이다. id에 대한 입력 데이터가 없으면 ValidationError가 발생한다. name은 기본 값에서 데이터 형식을 유추할 수 있어 따로 애너테이션이 필요 없고 name 에 대한 입력 데이터가 없어도 된다.

    주요 메서드

    dict()

    객체의 필드를 dict 타입으로 반환해준다.

    >>> user.dict()
    {'id': 1, 'name': '홍길동'}

    json()

    객체의 필드를 json 형식에 맞춰 반환해준다.

    >>> user.json() 
    '{"id": 1, "name": "\\\\ud64d\\\\uae38\\\\ub3d9"}'

    schema()

    모델 객체에 대한 스키마를 dict 타입으로 반환해준다.

    schema_json()

    모델 객체에 대한 스키마를 json 형식에 맞춰 반환해준다.

    >>> print(user.schema_json(indent=2))
    {
      "title": "User",
      "type": "object",
      "properties": {
        "id": {
          "title": "Id",
          "type": "integer"
        },
        "name": { 
          "title": "Name",
          "default": "\\ud64d\\uae38\\ub3d9", 
          "type": "string" 
        }
      },
      "required": [
        "id"
      ] 
    }

     

    validator

    각각의 필드에 대한 유효성 검사를 할 수 있다.

    from pydantic import BaseModel, validate
    
    class User(BaseModel):
        username: str
        password1: str
        password2: str
    		
        @validator('username')
        def validate_username(cls, v):
            if not v.isalnum():
                raise ValueError('usernaem field must be alphanumeric.')
            return v		
    
        @validator('password2')
        def validate_password2(cls, v, values-> {'username': '1fq}', **kwargs):
            if 'password1' in values and v != values['password1']:
                raise ValueError('passwords do not match.')
            return v

    validator 데코레이터는 첫 번째 인자에 필드 이름을 넣어 각 필드에 대한 유효성 검사를 할 수 있다.

    validator 메서드들은 클래스 메서드이다.

    데코레이터 첫 번째 인자에 User 클래스를 받고, 두 번째 인자에 유효성 검사할 필드에 대한 데이터이다.

    이외에 키워드 파라미터를 추가할 수 있다.

    • values : dict 형태로 pre-validated values가 담겨 있음
    • config : 모델의 Config클래스
    • field : 검증 중인 필드 클래스 pydantic.fields.ModelField

     

    dataclass

    pydantic의 BaseModel 을 사용하지 않을 경우 python의 빌트인 모듈인 dataclasses 기반인 dataclass 데코레이터로 동일하게 유효성 검사를 할 수 있다.

    from datetime import datetime
    from pydantic.dataclasses import dataclass
    
    @dataclass
    class User:
        id: int
        name = '홍길동'
    
    user = User(id='42')
    

     

    DRF Serializer을 pydantic으로 대체할 수 있을까?

    우선 주요 기능별로 표를 만들어본다.

    주요 기능 Serializer Pydantic
    필드(CharField, IntegerField, ...) validation O X
    필드 custom validation O O
    모든 필드를 종합적으로 validation O O
    Serialization O X
    인스턴트 생성 O X
    인스턴트 업데이트 O X
    Nested validation O O
    속도 느림 빠름

    DRF 시리얼라이저의 느린 성능을 pydantic의 BaseModel로 대체하고자 한다면

    • 필드(CharField, IntegerField, ...) validation
    • Serialization
    • 인스턴트 생성
    • 인스턴트 업데이트

    등을 구현할 필요가 있다. 또한, to_representation, to_internal_value 같이 적절한 시점에 비즈니스 로직을 넣을 수 없다.

    django에서 pydantic을 사용할 수 있도록 현재 개발되고 있는 오픈소스 프로젝트는 아래와 같다. 올해 4월에 생긴 프로젝트이다. 아직 개발중인 오픈소스다 보니 실서비스에 사용하기는 위험부담이 큰 것 같다.

    https://github.com/jordaneremieff/djantic

     

    GitHub - jordaneremieff/djantic: Pydantic models for Django

    Pydantic models for Django. Contribute to jordaneremieff/djantic development by creating an account on GitHub.

    github.com

     

    마치며

    pydantic은 타입 어노테이션을 사용한 데이터 검증 도구이다. 추가적인 validation, nested validation 등 가능하며 속도도 빠르다. 시리얼라이저와 pydantic validataion 성능에서는 DRF 시리얼라이저의 validataion 보다 12배 빠르다.

    그러나, pydantic은 validation만 해주기 떄문에 시리얼라이저의 기능들을 모두 포함하지는 않는다. 따라,서 DRF의 시리얼라이저를 대체하려면 validation이외의 기능들을 모두 구현해야 한다.

    그리고 벤치마크에서 12배라는 수치는 오직 validation(data → valid data) 속도 테스트이다.

    Serialization(object → valid data) 벤치마킹은 아니다.

     

    Reference

     

    GitHub - samuelcolvin/pydantic: Data parsing and validation using Python type hints

    Data parsing and validation using Python type hints - GitHub - samuelcolvin/pydantic: Data parsing and validation using Python type hints

    github.com

    댓글

Designed by Tistory.