0

it seems impossible to set a regex constraint with a __root__ field like this one:

class Cars(BaseModel):
    __root__: Dict[str, CarData]

so, i've resorted to doing it at the endpoint:

@app.post("/cars")
async def get_cars(cars: Cars = Body(...)):
    x = cars.json()
    y = json.loads(x)
    keys = list(y.keys())
    try:
        if any([re.search(r'^\d+$', i) is None for i in keys]):
            raise ValidationError
    except ValidationError as ex:
        return 'wrong type'
    return 'works'

this works well in that i get wrong type returned if i dont use a digit in the request body.

but i'd like to return something similar to what pydantic returns but with a custom message:

{
  "detail": [
    {
      "loc": [
        "body",
        "__root__",
      ],
      "msg": "hey there, you can only use digits!",
      "type": "type_error.???"
    }
  ]
}
avnav99
  • 532
  • 5
  • 16
  • Do you have an example of _what problem you're actually trying to solve_? i.e. it's unclear to me what `__root__: Dict[str, CarData]` is trying to do or validate. – MatsLindh Oct 06 '22 at 05:18
  • 1
    Please have a look at related answers [here](https://stackoverflow.com/a/72003724/17865804), [here](https://stackoverflow.com/a/72003764/17865804), as well as [here](https://stackoverflow.com/a/71258131/17865804) and [here](https://stackoverflow.com/a/71228281/17865804). – Chris Oct 06 '22 at 06:57
  • @MatsLindh basically trying to make sure that `str` is a digit (but really, testing regex), for example something like this ```class Cars(BaseModel): __root__: Dict[str, CarData] @pydantic.validator(__root__) @classmethod def car_id_is_digit(cls, value): if re.search(r'^\d+$', value): raise ValueError("car_id must be a string that is a digit.")``` – avnav99 Oct 09 '22 at 02:07
  • @Chris i appreciate this, and its helpful in general - but in this case i'm not sure it's possible to validate a ```__root__``` variable... unless you can point me in a better direction – avnav99 Oct 09 '22 at 02:18

3 Answers3

1

This will do:

from pydantic import ValidationError
from pydantic.error_wrappers import ErrorWrapper

print(ValidationError(
    [
        ErrorWrapper(ValueError('Wrong car error 1.'), '/cars'), 
        ErrorWrapper(ValueError('Wrong car error 2.'), '/cars')
    ], 
    Cars
))

You should provide a list of python exceptions wrapped in ErrorWrapper as the first argument and validated model's class as the second. This way pydantic will generate the expected message for you:

2 validation errors for Cars
/cars
  Wrong car error 1. (type=value_error)
/cars
  Wrong car error 2. (type=value_error)
winwin
  • 958
  • 7
  • 25
0

if it helps anyone, here is how i validated a dynamic field:

class Cars(BaseModel):
    __root__: Dict[str, CarData]
    
    @pydantic.root_validator(pre=True)
    @classmethod
    def car_id_is_digit(cls, fields):
        car_ids = list(list(fields.values())[0].keys())
        print(car_ids)
        if any([bool(re.search(r'^\d+$', car_id)) == False for car_id in car_ids]):
            raise ValueError("car_id must be a string that is a digit.")
        else:
            return fields

since a regular field validator requires a field name as an argument, i used the root_validator which validates all fields - and does not require that argument.

all this, because __root__ cannot be referenced in the regular field validator, it seems.

however, this means you can only have __root__ fields - and they will all be under the same validation rules...not sure how to added more fields with this.

avnav99
  • 532
  • 5
  • 16
-1

You can pass your own error string by using raise ValidationError("Wrong data type"). Hope it helps.

Shashi Kant
  • 117
  • 3
  • This is incorrect. The Pydantic ValidationError class accepts a sequence of errors and the model which is getting validated. Your solution will raise a validation error, but it will be misleading and will not contain the string that you passed in. – Benjamin May 26 '23 at 15:39