user18140022
user18140022

Reputation: 375

How to get Pydantic to check on the spot

I am not sure if I am using Pydantic incorrectly but I want users to type in a terminal and if they get it wrong, I want them to keep trying until entering a valid range.

class Input(BaseModel):
    number_range:str
    name:str

    @validator("number_range")
    def all_numbers_are_valid(cls, value:str) -> str:
        string_list:list = value.split(",")
        string_list = list(map(cls.remove_spaces, string_list))
        string_bool = list(map(lambda x: x.isnumeric(), string_list))
        if False in string_bool:
            flag = True
            while flag:
                print("Invalid characters, please try again.")
                value = input("Range of floors:")
                string_list:list = value.split(",")
                string_list = list(map(cls.remove_spaces, string_list))
                string_bool = list(map(lambda x: x.isnumeric(), string_list))
                flag = True if False in string_bool else False

        return ", ".join(map(str,string_list))

    @validator("name")
    def no_space_name(cls, value:str) -> str:
        return cls.remove_spaces(value)

Input(number_range=input("Enter a range of numbers:"), name=input("Enter name:"))

Example of input:

Enter a range of numbers: 0,1,a
Enter name: he   llo

I want user to be prompted with entering the a valid range of numbers BEFORE moving onto next field. Is that possible?

Upvotes: 1

Views: 796

Answers (2)

JacekK
JacekK

Reputation: 793

You probably overcomplicate your model. The better way is to move the input process out of the class.

Firstly, if you want the number_range to be the list of integers it is better to declare it in the class

class Input(BaseModel):
    number_range: List[int]
    name: str

Secondly, if you want a name without spaces between words, you should handle this by a validator.

@validator("name", pre=True)
def no_space_name(cls, v):
# your logic or...
    return v.replace(" ", "")

Then we move the whole input process outside of the class. This way you are still able to simply create a class instance in other places in your code without this whole input process.

whole code:

from typing import List
from pydantic import BaseModel, validator


class Input(BaseModel):
    number_range: List[int]
    name: str

    @validator("name", pre=True)
    def no_space_name(cls, v):
        # your logic or...
        return v.replace(" ", "")


if __name__ == '__main__':
    input_kwargs = {}
    while not input_kwargs.get("number_range"):
        range_data = input("Enter a range of numbers (coma separated): ").split(",")
        if all([item.isnumeric() for item in range_data]):
            input_kwargs["number_range"] = range_data
        else:
            print("All values must be coma separated integers")

    while not input_kwargs.get("name"):
        input_kwargs["name"] = input("Enter name: ").strip()

    my_instance = Input(**input_kwargs)
    print(my_instance.json())

Example:

>>> Enter a range of numbers (coma separated): 
1, 3, 4, a
All values must be coma separated integers
>>> Enter a range of numbers (coma separated):
1,3,4,5
>>> Enter name:
Two Words
{"number_range": [1, 3, 4, 5], "name": "TwoWords"}

And if you need, you can still declare your class like this:

>>> other_instance = Input(name="TheName", number_range=[1,3,4])
>>> other_instance.json()
'{"number_range": [1, 3, 4], "name": "TheName"}'

Upvotes: 3

josix
josix

Reputation: 1

I guess it's not possible since the class Input has to wait for all the values from the accomplished input("Enter a range of numbers:") and input("Enter name:"), then it is able to run the validator decorator and your customized checking scripts.

How about using questionary? I think it will satisfy your needs. You could refer to it here.

Upvotes: 0

Related Questions