본문 바로가기

개발하자

하나가 실패하더라도 모든 Pydantic 검증자를 거친 다음 FastAPI 응답에서 여러 ValueErrors를 제기하는 방법은 무엇입니까?

반응형

하나가 실패하더라도 모든 Pydantic 검증자를 거친 다음 FastAPI 응답에서 여러 ValueErrors를 제기하는 방법은 무엇입니까?

모든 검증자에게 전화를 걸어 오류 목록 전체를 확인할 수 있습니까?

@validator('password', always=True)
def validate_password1(cls, value):
    password = value.get_secret_value()

    min_length = 8
    if len(password) < min_length:
        raise ValueError('Password must be at least 8 characters long.')

    return value

@validator('password', always=True)
def validate_password2(cls, value):
    password = value.get_secret_value()

    if not any(character.islower() for character in password):
        raise ValueError('Password should contain at least one lowercase character.')

    return value

현재 동작은 한 번에 하나의 검증자를 호출하는 것 같습니다.

나의 파이단틱 수업:

class User(BaseModel):
    email: EmailStr
    password: SecretStr

요청에 , 또는 필드를 포함하지 않으면 어레이에서 두 개의 유효성 검사 실패가 모두 발생합니다. 이는 필드에 대해 내가 수행하고자 하는 작업입니다. 그러나 현재 동작은 하나를 호출하는 것처럼 보이며, 실패하면 오류가 즉시 발생합니다.




너는 그렇게 사용할 수 없다; 그것은 항상 그것들 중 하나처럼 보인다.

답변을 얻기 위해 다음 두 가지 방법을 사용할 수 있습니다

1 - 모든 상태를 확인하는 주 검증기를 하나 사용할 수 있습니다

@validator('password', always=True)
def validate_password(cls, value):
    password = value.get_secret_value()

    validate_password1(password)
    validate_password2(password)

    return value
    
def validate_password1(password):

    min_length = 8
    if len(password) < min_length:
        raise ValueError('Password must be at least 8 characters long.')


def validate_password2(password):

    if not any(character.islower() for character in password):
        raise ValueError('Password should contain at least one lowercase character.')


2 - 모형에서 중복 변수를 사용하여 조건을 확인할 수 있습니다

class User(BaseModel):
    email: EmailStr
    password: SecretStr
    password2: SecretStr

그리고 분명히, 당신의 장식가들은 다음과 같아야 한다:

@validator('password', always=True)
def validate_password1(cls, value):

그리고.

@validator('password2', always=True)
def validate_password2(cls, value):

OP는 모든 오류를 제기하기를 원하므로 업데이트된 답변은 다음과 같습니다.

첫 번째 총알 외에도 다음과 같은 것을 시도할 수 있습니다:

@validator('password', always=True)
def validate_password(cls, value):
    password = value.get_secret_value()

    try:
        validate_password1(password)
    except Exception as e:
        print('First error: ' + str(e))
        
    try:
        validate_password2(password)
    except Exception as e:
        print('Second error: ' + str(e))

    return value
    
def validate_password1(password):

    min_length = 8
    if len(password) < min_length:
        raise ValueError('Password must be at least 8 characters long.')


def validate_password2(password):

    if not any(character.islower() for character in password):
        raise ValueError('Password should contain at least one lowercase character.')


그러나 를 반환할 때는 주의하십시오. 메인 코드에 사용자 지정 예외를 하나 더 추가할 수 있습니다.




질문에서 설명한 것처럼 특정 필드에 대해 여러 개의 오류/예외를 제기할 수 없습니다. 제안된 해결책은 다음과 같습니다.

옵션 1

단일 변수를 사용하여 오류 메시지를 연결하고 마지막에 오류 메시지를 한 번 올립니다(오류가 발생한 경우):

@validator('password', always=True)
def validate_password1(cls, value):
    password = value.get_secret_value()
    min_length = 8
    errors = ''
    if len(password) < min_length:
        errors += 'Password must be at least 8 characters long. '
    if not any(character.islower() for character in password):
        errors += 'Password should contain at least one lowercase character.'
    if errors:
        raise ValueError(errors)
        
    return value

위의 조건문이 모두 충족되는 경우 출력은 다음과 같습니다:

{
  "detail": [
    {
      "loc": [
        "body",
        "password"
      ],
      "msg": "Password must be at least 8 characters long. Password should contain at least one lowercase character.",
      "type": "value_error"
    }
  ]
}

옵션 2

갱신하다

Pydantic V2에서는 제거되었습니다. 를 참조하십시오. 자체적으로 구현하려면 Pydantic V1.9를 참조하십시오.

원답

클래스 목록을 사용하여 직접 올리십시오.

from pydantic import ValidationError
from pydantic.error_wrappers import ErrorWrapper

@validator('password', always=True)
def validate_password1(cls, value):
    password = value.get_secret_value()
    min_length = 8
    errors = []
    if len(password) < min_length:
        errors.append(ErrorWrapper(ValueError('Password must be at least 8 characters long.'), loc=None))
    if not any(character.islower() for character in password):
        errors.append(ErrorWrapper(ValueError('Password should contain at least one lowercase character.'), loc=None))
    if errors:
        raise ValidationError(errors, model=User)
        
    return value

FastAPI는 속성 자체를 추가하는 것처럼 보이기 때문에 에 속성(필수 매개 변수)을 사용하여 에 추가된 경우 이름( )을 두 번 갖게 됩니다. 따라서 다음과 같이 를 통해 나중에 제거할 수 있는 빈 상태로 둘 수 있습니다( 를 사용):

from fastapi import Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    for error in exc.errors(): 
        error['loc'] = [x for x in error['loc'] if x]  # remove null attributes
        
    return JSONResponse(content=jsonable_encoder({"detail": exc.errors()}), status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)

위의 조건문이 모두 충족되는 경우 출력은 다음과 같습니다:

{
  "detail": [
    {
      "loc": [
        "body",
        "password"
      ],
      "msg": "Password must be at least 8 characters long.",
      "type": "value_error"
    },
    {
      "loc": [
        "body",
        "password"
      ],
      "msg": "Password should contain at least one lowercase character.",
      "type": "value_error"
    }
  ]
}

반응형