pythonfastapipydantic

Possible fields values to depend on other values


I have those 3 fields: event, category, subcategory Depending on the name I allow different categories and depending on the category I allow different subcategories. Example:

How can I do that? I need it for FastAPI validation and if there is a way of doing it more efficiently that would be great.


Solution

  • I couldn't find an easy way to implement this dependence with the Pydantic schema itself. I used model_validator (A newer version of validator suggested in the comments) with a dictionary schema.

    Example

    Schema defining the the allowed name, category and subcategory combinations:

    events_schema = {
        """
        Example:
    
        "name": {
            "category": ["subcategory", "subcategory"],
            "category": ["subcategory", None], # None If we want to allow a category without a subcategory.
            None: [None]  # If we want to allow events without a category and subcategory.
        },
        """
        
        "a": {
            "aa": ["aaa", "aab"],
            "ab": ["aba", "abb"],
            None: [None]
    
        },
        "b": {
            "ba": ["bba", "bbb"],
            "bb": ["bca", "bcb"],
            "bc": [None]
        },
        # More events can be added here
    }
    

    Use Pydantic's model_validator to validate the request data:

    class EventRequest(BaseModel):
        name: str
        category: Optional[str]
        subcategory: Optional[str]
    
        @model_validator(mode="after")
        def verify_event_hierarchy(self) -> "EventRequest":
            # Check if name exists in the hierarchy
            if self.name not in events_schema:
                raise ValueError(f"Invalid name: '{self.name}'")
    
            # Check if category exists for the given name
            if self.category not in events_schema[self.name]:
                raise ValueError(f"Invalid category '{self.category}' for name '{self.name}'.")
    
            # Check if subcategory exists for the given category under the name
            if self.subcategory not in events_schema[self.name].get(self.category, []):
                raise ValueError(
                    f"Invalid subcategory '{self.subcategory}' for category '{self.category}' under name '{self.name}'."
                )
    
            return self