25

I would like to create pydantic model to validate users form. one of my model values should be validated from a list of names. I succeed to create the model using enum as follow:

from enum import Enum
class Fruit(str, Enum):
    APPLE = 'apple'
    BANANA = 'banana'
    MELON = 'melon'

from pydantic import BaseModel
class UserForm(BaseModel):
    fruit: Fruit
    name: str

Now I would like to switch the enum to a list of values in my code:

fruit = ['apple','banana','melon']

How can I manage to do so?

tnx

0

5 Answers 5

26

You could do this also by means of a list of Literal type. Like so:

import pydantic
from typing import Literal, List

class M(pydantic.BaseModel):
    fruits: List[Literal["apple", "orange"]]

print(M.parse_obj({"fruits":["apple", "orange"]}))  # OK fruits=['apple', 'orange']
print(M.parse_obj({"fruits":["apple", "orange", "potato"]}))  # Error unexpected value potato
Sign up to request clarification or add additional context in comments.

1 Comment

The question is a bit loose, but I don't think the input is going to be a list. However, using Literal instead of Enum is the answer.
26

I am proposing an elegant solution.

from pydantic import BaseModel
from typing import List
from enum import Enum


class Fruit(str, Enum):
    APPLE = 'apple'
    BANANA = 'banana'
    MELON = 'melon'


class UserForm(BaseModel):
    fruits: List[Fruit]
    name: str

And that's it.

  • you don't need to write your own validator
  • just tell pydantic you need a list of Fruit object, and it will do it for you

Check the above code:

put the above code in a file main.py.

Run

python -i main.py
>>> uf = UserForm(fruits=['apple','banana'],name='hello')
>>> uf
UserForm(fruits=[<Fruit.APPLE: 'apple'>, <Fruit.BANANA: 'banana'>], name='hello')


>>> af = UserForm(fruits=['monkey','apple'],name='hello')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pydantic/main.py", line 400, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for UserForm
fruits -> 0
  value is not a valid enumeration member; permitted: 'apple', 'banana', 'melon' (type=type_error.enum; enum_values=[<Fruit.APPLE: 'apple'>, <Fruit.BANANA: 'banana'>, <Fruit.MELON: 'melon'>])
>>> 

pydantic will raise an error, as monkey is not in fruits.

5 Comments

Indeed, elegant ;)
One problem with Enum s is that if integer is given, it will always raise value error, not a type error.
I believe he meant to replace the Fruit class with a list of strings instead. I don't think he meant to make fruit plural (accepting multiple values in a list).
TypeError: Type List cannot be instantiated; use list() instead
can I use list rather than typing.List in pydantic schema model?
8

You can use validator in the following way:

 from pydantic import BaseModel, ValidationError, validator
 class UserForm(BaseModel):
    fruit: str
    name: str
    @validator('fruit')
    def fruit_must_be_in_fruits(cls,fruit):
      fruits=['apple','banana','melon']
      if fruit not in fruits:
        raise ValueError(f'must be in {fruits}')
      return fruit
 try:
    UserForm(fruit="apple",name="apple")
 except ValidationError as e:
    print(e)

It will raise a validation error if it doesn't match the criteria.

1 Comment

Hi, this is great thank you. But it still not working as I would like. I am using this UserForm in fastapi and that means that while swagger/docs will automatically suggest all the values for the user when I am using the enum solution it will not suggest them in this solution.
1

You can get informations about the enum by its .__members__ dictionary - here you can simply iterate it keys though:

from enum import Enum
class Fruit(str, Enum):
    APPLE = 'apple'
    BANANA = 'banana'
    MELON = 'melon'

# only need __members__ if you need more infos about it

print(Fruit.__members__)

# you do not need the __members__ if you just want the keys
print([name.lower() for name in Fruit])

Output:

# enums __members__ dictionary
{'APPLE': <Fruit.APPLE: 'apple'>, 
 'BANANA': <Fruit.BANANA: 'banana'>, 
 'MELON': <Fruit.MELON: 'melon'>} 

# lower keys
['apple', 'banana', 'melon']

Comments

0

As Yassine said, the question is looking for an alternative to using Enums. For example, Literal

from pydantic import BaseModel
from typing import Literal

class UserForm(BaseModel):
    fruit: Literal['apple','banana','melon']
    name: str

form_data = {'fruit':'apple', 'name': 'someuser'}
user_form = UserForm(**form_data)
print(user_form.fruit) # apple

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.