Pydantic: Python에서 데이터 유효성 검사 단순화 II

2024. 4. 25. 20:58python/intermediate

모델 사용

 

데이터 스키마를 정의하는 Pydantic의 기본 방법은 모델을 이용하는 것입니다. Pydantic 모델은 주석이 달린 필드가 있는 엔터티에 대한 데이터를 정의하고 저장하는 Python 데이터 클래스 와 유사한 객체입니다. 데이터 클래스와 달리 Pydantic의 초점은 자동 데이터 구문 분석, 검증 및 직렬화에 중점을 둡니다.

이를 이해하는 가장 좋은 방법은 자신만의 모델을 만드는 것입니다. 이것이 바로 다음에 수행할 작업입니다.

 

Pydantic BaseModel 작업

 

인사부에서 직원 정보를 관리하는 데 사용하는 애플리케이션을 구축 중이고 신입 직원 정보가 올바른 형식인지 확인하는 방법이 필요하다고 가정해 보겠습니다. 예를 들어, 각 직원에게는 ID, 이름, 이메일, 생년월일, 급여, 부서 및 혜택 선택이 있어야 합니다. 이것은 Pydantic 모델의 완벽한 사용 사례입니다!

직원 모델을 정의하려면 Pydantic의 BaseModel을 상속하는 클래스를 생성합니다.

 
[ ]:
 
 
 
# pydantic_models.py
from datetime import date
from uuid import UUID, uuid4
from enum import Enum
from pydantic import BaseModel, EmailStr
class Department(Enum):
    HR = "HR"
    SALES = "SALES"
    IT = "IT"
    ENGINEERING = "ENGINEERING"
class Employee(BaseModel):
    employee_id: UUID = uuid4()
    name: str
    email: EmailStr
    date_of_birth: date
    salary: float
    department: Department
    elected_benefits: bool
 
 

먼저 직원 모델을 정의하는 데 필요한 종속성을 가져옵니다. 그런 다음 회사의 다양한 부서를 나타내는 열거형을 만들고 이를 사용하여 department 직원 모델의 필드에 주석을 추가합니다.

그런 다음 BaseModel에서 상속 하고, 주석을 통해 직원 필드의 이름과 예상 유형을 정의 하는 Pydantic 모델인 Employee를 정의합니다. 다음은 Employee에서 정의한 각 필드에 대한 분석과 Employee 객체가 인스턴스화될 때 Pydantic이 이를 검증하는 방법입니다.

  • employee_id: 정보를 저장하려는 직원의 UUID 입니다. 주석을 사용함으로써 UUIDPydantic은 이 필드가 항상 유효한 UUID인지 확인합니다. uuid4()를 호출하여 지정한 대로 Employee의 각 인스턴스에는 기본적으로 UUID가 할당됩니다.
  • name: Pydantic이 문자열로 예상하는 직원의 이름입니다.
  • email: Pydantic은 내부적으로 Python의 email-validator 라이브러리를 사용하여 각 직원의 email이 유효한지 확인합니다.
  • date_of_birth: 각 직원의 생년월일은 Python datetime 모듈로 부터 date의 주석에 따라 유효한 날짜여야 합니다. 문자열을 date_of_birth에 전달하면 Pydantic은 이를 구문 분석하고 date 객체로 변환하려고 시도합니다.
  • salary: 직원의 급여이며, 부동수가 될 것으로 예상됩니다.
  • department: 각 직원의 부서는 Department 열거형에 정의된 대로 HR, SALES, IT 또는 ENGINEERING중 하나여야 합니다.
  • elected_benefits: 이 필드는 직원이 혜택을 선택했는지 여부를 저장하며, Pydantic은 그것이 부울일 것으로 기대합니다.

Employee 객체를 생성하는 가장 간단한 방법은 다른 Python 객체처럼 인스턴스화하는 것입니다. 이렇게 하려면 Python REP 을 열고 다음 코드를 실행하세요.

 
[ ]:
 
 
 
from pydantic_models import Employee
Employee(
    name="Chris DeTuma",
    email="cdetuma@example.com",
    date_of_birth="1998-04-02",
    salary=123_000.00,
    department="IT",
    elected_benefits=True,
)
 
 

이 블록에서는 필수 직원 필드가 모두 포함된 Employee를 가져오고 개체를 만듭니다. Pydantic은 전달한 필드를 성공적으로 검증하고 강제하며 유효한 Employee 객체를 생성합니다. Pydantic이 어떻게 자동으로 날짜 문자열을 date 객체로 변환하고 IT문자열을 해당 Department열거형으로 변환하는지 확인하세요.

다음으로, 유효하지 않은 데이터를 Employee 인스턴스에 전달하려고 할 때 Pydantic이 어떻게 반응하는지 살펴보십시오.

 
[ ]:
 
 
 
Employee(
    employee_id="123",
    name=False,
    email="cdetumaexamplecom",
    date_of_birth="1939804-02",
    salary="high paying",
    department="PRODUCT",
    elected_benefits=300,
)
 
 

이 예에서는 잘못된 데이터 필드가 있는 Employee 개체를 생성했습니다. Pydantic은 각 필드에 대한 자세한 오류 메시지를 제공하여 예상한 내용, 수신된 내용 및 오류에 대해 자세히 알아볼 수 있는 위치를 알려줍니다.

이 상세한 검증은 잘못된 데이터를 Employee에 저장하는 것에서 막아 주기 때문에 강력합니다. 또한 오류 없이 인스턴스화한 Employee 개체에 예상한 데이터가 포함되어 있다는 확신을 갖게 되며, 코드나 다른 애플리케이션에서 이 데이터 다운스트림을 신뢰할 수 있습니다.

Pydantic의 BaseModel은 사전 및 JSON과 같은 다른 개체에서 모델을 쉽게 생성할 수 있는 일련의 메서드를 갖추고 있습니다 . 예를 들어, 사전에서 Employee 객체를 인스턴스화하려면 .model_validate() 클래스 메서드를 사용할 수 있습니다.

 
[ ]:
 
 
 
(참고) 아래 코드는 shell의 python에서 실행하세요
 
 
[ ]:
 
 
 
new_employee_dict = {
    "name": "Chris DeTuma",
    "email": "cdetuma@example.com",
    "date_of_birth": "1998-04-02",
    "salary": 123_000.00,
    "department": "IT",
    "elected_benefits": True,
}
 
 
[ ]:
 
 
 
Employee.model_validate(new_employee_dict)
 
 

여기서는 직원 필드가 포함된 사전인를 new_employee_dict를 생성하고 이를 Employee 인스턴스를 생성하는 .model_validate()에게 전달합니다. 내부적으로 Pydantic은 각 사전 항목의 유효성을 검사하여 예상한 데이터와 일치하는지 확인합니다. 데이터 중 하나라도 유효하지 않은 경우 Pydantic은 이전에 본 것과 같은 방식으로 오류를 발생시킵니다. 사전에서 필드가 누락된 경우에도 알림을 받게 됩니다.

.model_validate_json()을 사용하여 JSON 객체로 동일한 작업을 수행할 수 있습니다.

 
[ ]:
 
 
 
new_employee_json = """
 {"employee_id":"d2e7b773-926b-49df-939a-5e98cbb9c9eb",
 "name":"Eric Slogrenta",
 "email":"eslogrenta@example.com",
 "date_of_birth":"1990-01-02",
 "salary":125000.0,
 "department":"HR",
 "elected_benefits":false}
 """
 
 
[ ]:
 
 
 
new_employee = Employee.model_validate_json(new_employee_json)
 
 
[ ]:
 
 
new_employee
 
 

이 예에서는 new_employee_json은 직원 필드를 저장하는 유효한 JSON 문자열이며 new_employee_json에서 Employee 객체 검증하고 생성하는 데 .model_validate_json()을 사용합니다. 미묘해 보일 수도 있지만 JSON에서 Pydantic 모델을 생성하고 검증하는 기능은 강력합니다. 왜냐하면 JSON은 웹을 통해 데이터를 전송하는 가장 널리 사용되는 방법 중 하나이기 때문입니다. 이것이 FastAPI가 Pydantic을 사용하여 REST API를 생성하는 이유 중 하나입니다.

Pydantic 모델을 사전 및 JSON으로 직렬화할 수도 있습니다.

 
[ ]:
 
 
 
new_employee.model_dump()
 
 
[ ]:
 
 
 
new_employee.model_dump_json()
 
 

여기에서는 .model_dump() 및 .model_dump_json()를 사용하여 new_employee 모델을 각각 사전 및 JSON 문자열로 변환합니다. 어떻게 문자열로 저장된 date_of_birth와 department와 같은JSON 객체를 반환하는 .model_dump_json() 방법에 주목하세요.

Pydantic은 이미 이러한 필드를 검증하고 모델을 JSON으로 변환했지만 이 JSON 다운스트림을 사용하는 사람은 date_of_birth가 date가 유효해야 하고, Department가 열거형 department의 카테고리여야 할 필요가 있는 것을 알지 못할 것입니다. 이 문제를 해결하려면 Employee 모델에서 JSON 스키마를 생성하면 됩니다.

JSON 스키마는 예상되는 필드와 JSON 개체에 표시되는 값을 알려줍니다. 이것을 Employee 클래스 정의의 JSON 버전으로 생각할 수 있습니다. Employee에 대한 JSON 스키마를 생성하는 방법은 다음과 같습니다.

 
[ ]:
 
 
Employee.model_json_schema()
 
 

.model_json_schema()을 호출하면 모델의 JSON 스키마를 나타내는 사전을 얻게 됩니다. 표시되는 첫 번째 항목은 department가 취할 수 있는 가치를 보여줍니다 . 또한 필드 형식을 지정하는 방법에 대한 정보도 볼 수 있습니다. 예를 들어 이 JSON 스키마에 따르면 employee_id가 UUID로 기대되며 date_of_birth는 날짜가 될 것으로 예상됩니다.

json.dumps()를 사용하여 JSON 스키마를 JSON 문자열로 변환할 수 있습니다. 이를 통해 거의 모든 프로그래밍 언어에서 Employee 모델 에서 생성된 JSON 개체의 유효성을 검사할 수 있습니다. 즉, Pydantic은 수신 데이터의 유효성을 검사하고 이를 JSON으로 직렬화할 수 있을 뿐만 아니라 JSON 스키마를 통해 모델 데이터의 유효성을 검사하는 데 필요한 정보를 다른 프로그래밍 언어에 제공합니다.

이로써 이제 Pydantic의 BaseModel을 사용하여 데이터를 검증하고 직렬화하는 방법을 이해하게 되었습니다. 다음에는 필드를 사용하여 유효성 검사를 추가로 사용자 지정하는 방법을 알아봅니다.

 

사용자 정의 및 메타데이터에 필드 사용

 

지금까지 Employee 모델은 각 필드의 데이터 유형을 검증하고 email, date_of_birth 및 department와 같은 일부 필드가 유효한 형식을 사용하는지 확인합니다. 그러나 salary가 양수이고, name가 빈 문자열이 아니며, email이 회사의 도메인 이름이 포함되어 있는지도 확인하고 싶다고 가정해 보겠습니다. 이를 수행하려면 Pydantic의 Field 클래스를 사용할 수 있습니다.

Field 클래스를 사용하면 모델 필드에 메타데이터를 사용자 정의하고 추가할 수 있습니다. 이것이 어떻게 작동하는지 보려면 다음 예를 살펴보십시오.

 
[ ]:
 
 
# pydantic_models.py
from datetime import date
from uuid import UUID, uuid4
from enum import Enum
from pydantic import BaseModel, EmailStr, Field
class Department(Enum):
    HR = "HR"
    SALES = "SALES"
    IT = "IT"
    ENGINEERING = "ENGINEERING"
class Employee(BaseModel):
    employee_id: UUID = Field(default_factory=uuid4, frozen=True)
    name: str = Field(min_length=1, frozen=True)
    email: EmailStr = Field(pattern=r".+@example\.com$")
    date_of_birth: date = Field(alias="birth_date", repr=False, frozen=True)
    salary: float = Field(alias="compensation", gt=0, repr=False)
    department: Department
    elected_benefits: bool
 
 

여기서는 이전에 사용한 다른 종속성과 함께 Field를 가져오고 일부 Employee 필드에 기본값을 할당합니다. 필드에 추가 유효성 검사 및 메타데이터를 추가하는 데 사용한 Field 매개변수에 대한 분석은 다음과 같습니다.

  • default_factory: 이를 사용하여 기본값을 생성하는 콜러블을 정의합니다. 위의 예에서는 default_factory로 uuid4를 설정했습니다. 필요할 때 e룰 위한 mployee_id룰 위한 임의의 UUID를 생성하도록 uuid4()를 호출합니다. 유연성을 높이기 위해 람다 함수를 사용할 수도 있습니다 .
  • frozen: 필드를 변경할 수 없도록 설정할 수 있는 부울 매개변수입니다. 즉, frozen을 True로 설정 하면 모델이 인스턴스화된 후에 해당 필드를 변경할 수 없습니다. 이 예에서는 frozen 매개변수를 사용하여 employee_id, name 및 date_of_birth를 변경할 수 없게 만듭니다.
  • min_length: min_length 및 max_length를 사용하여 문자열 필드의 길이를 제어할 수 있습니다 . 위의 예에서는 name길이가 1자 이상인지 확인합니다.
  • pattern: 문자열 필드의 경우 해당 필드에 대해 예상하는 패턴과 일치하도록 정규 표현식에 pattern을 설정할 수 있습니다. 예를 들어 위의 예에서 email에 대한 정규 표현식을 사용하면 Pydantic은 모든 이메일이 @example.com로 끝나는지 확인합니다.
  • alias: 필드에 별칭을 할당하려는 경우 이 매개변수를 사용할 수 있습니다. 예를 들어 date_of_birth가 birth_date로 전화를 받거나 salary가 compensation로 불렀지도록 허용 할 수 있습니다. 모델을 인스턴스화하거나 직렬화할 때 이러한 별칭을 사용할 수 있습니다.
  • gt: "보다 큼"의 약어인 이 매개변수는 숫자 필드에 사용되어 최소값을 설정합니다. 이 예에서 gt=0이라는 보장 설정은 항상 salary가 양수입니다. Pydantic에는 "보다 작음"의 약어 와 같은 다른 숫자 제약 조건 도 있습니다.lt
  • repr: 이 부울 매개변수는 모델의 필드 표현에 필드가 표시되는지 여부를 결정합니다. 이 예에서는 Employee 인스턴스를 인쇄할 때 date_of_birth 또는 salary를 볼 수 없습니다.

이 추가 검증이 실제로 수행되는 모습을 보려면 잘못된 데이터로 Employee 모델을 생성하려고 할 때 어떤 일이 발생하는지 확인하세요.

 
[ ]:
 
 
from pydantic_models import Employee
incorrect_employee_data = {
    "name": "",
    "email": "cdetuma@fakedomain.com",
    "birth_date": "1998-04-02",
    "salary": -10,
    "department": "IT",
    "elected_benefits": True,
}
 
 
[ ]:
 
 
Employee.model_validate(incorrect_employee_data)
 
 

여기서는 업데이트된 Employee모델을 가져오고 잘못된 데이터가 있는 사전의 유효성을 검사해 봅니다. 이에 대한 응답으로 Pydantic은 name은 최소 한 글자 이상이어야 하고, email은 회사의 도메인 이름과 일치해야 하며, salary는 0보다 커야 한다는 세 가지 유효성 검사 오류를 제공합니다.

이제 올바른 Employee 데이터의 유효성을 검사할 때 얻을 수 있는 추가 기능을 확인하세요.

 
[ ]:
 
 
 
employee_data = {
    "name": "Clyde Harwell",
    "email": "charwell@example.com",
    "birth_date": "2000-06-12",
    "compensation": 100_000,
    "department": "ENGINEERING",
    "elected_benefits": True,
}
 
 
[ ]:
 
 
 
employee = Employee.model_validate(employee_data)
 
 
[ ]:
 
 
employee
 
 
[ ]:
 
 
employee.salary
 
 
[ ]:
 
 
 
employee.date_of_birth
 
 

이 블록에서는 .model_validate()를 사용하여 사전과 Employee 모델을 생성합니다. employee_data에서는 date_of_birth 대신 birth_date를 salary 대신 compensation을 어떻게 사용했는지 살펴보세요. Pydantic은 이러한 별칭을 인식하고 해당 값을 내부적으로 올바른 필드 이름에 할당합니다.

repr=False을 설정했기 때문에 Employee에는 표시되지 않는 salary와 date_of_birth를 볼 수 있습니다. 해당 값을 보려면 속성으로 명시적으로 액세스해야 합니다. 마지막으로 고정된 필드를 변경하려고 하면 어떤 일이 발생하는지 확인하세요.

 
[ ]:
employee.department = "HR"
employee.name = "Andrew TuGrendele"
 
 

여기서는 먼저 department의 값을 IT와 HR로 변경 합니다. 이는 department가 얼어붙은 field가 아니기 때문에 완벽하게 허용됩니다. 그러나 name을 변경하려고 하면 Pydantic에서 해당 필드가 고정된 필드라는 오류를 표시합니다.

이제 Pydantic BaseModel과 Field 클래스를 확실하게 이해하게 되었습니다. 이것만으로도 데이터 스키마에 대한 다양한 유효성 검사 규칙과 메타데이터를 정의할 수 있지만 때로는 이것만으로는 충분하지 않습니다. 다음에는 Pydantic 검증기를 사용하여 현장 검증을 더욱 강화해 보겠습니다.

 

<출처 : https://realpython.com/python-pydantic/>