Metadata-Version: 2.4
Name: pydantic-validation-formatter
Version: 0.2
Summary: This is a package that allows to customize pydantic built-in validation error messages
Project-URL: Repository, https://github.com/soamicharan/pydantic-validation-formatter
Author-email: Soami Charan <charansoami@gmail.com>
Maintainer-email: Soami Charan <charansoami@gmail.com>
License: Apache-2.0
License-File: LICENSE
Keywords: pydantic,python,sqlmodel
Requires-Python: <4,>=3.8
Requires-Dist: pydantic<3,>=2.6.3
Provides-Extra: test
Requires-Dist: pytest>=8; extra == 'test'
Description-Content-Type: text/markdown

# Pydantic Validation Formatter

[![GitHub license badge](https://raw.githubusercontent.com/soamicharan/sqlmodel-plus/main/badges/badge-license.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![pypi](https://img.shields.io/pypi/v/sqlmodel-plus.svg)](https://pypi.org/project/pydantic-validation-formatter/)

## Installation

Install package using pip -> `pip install pydantic-validation-formatter`

## Usage

### Simple Usage
Use `@customize_validation_message` decorator on pydantic class to apply message templates on specific validation error message.

```python
from pydantic_validation_formatter import customize_validation_message
from pydantic import BaseModel, Field, ValidationError, ConfigDict

@customize_validation_message
class Hero(BaseModel):
    id: int = Field(gt=0, lt=5)
    name: str
    model_config = ConfigDict(
        validation_message_template = {
            "id": {
                "greater_than": "id value should be greater than {gt} but received {input}",
                "missing": "id field is required",
            },
        }
    )

try:
    Hero(id=10, name="hero")
except ValidationError as exc:
    print(exc.errors())
```

This customize the `msg` field of validation error as follows - 
```
[
    {
        'type': 'greater_than',
        'loc': ('id',),
        'msg': 'id value should be greater than 0 but received -1',     # The default generated message will be 'Input should be greater than 0' but it customize the message.
        'input': -1,
        'ctx': {'gt': 0},
        'url': 'https://errors.pydantic.dev/2.6/v/greater_than'
    }
]
```

The validation error message can be templated with following variables
- `input` - The input value in validation error payload
- `field` - The last item in `loc` key value from validation error payload
- `error_type` - The `type` key value from validation error payload

If any other keys found in `ctx` dict, then you can use those values in templated validation error message.

To provide custom validation templated message, you need to define `validation_message_template` attribute in model `ConfigDict` or `Config` class (for V1 pydantic). <br>
This should be a dict value which contains field name as keys (same as attribute name defined in pydantic class)
and values should be dict of validation error type and customize templated error message mapping. <br>
To know what kind of error type available, follow the official docs -> https://docs.pydantic.dev/latest/errors/validation_errors/


### Advanced Usage
For nested pydantic models, you need to define `validation_message_template` Configuration on root model to trigger the message template customization, also field names should json path like reference to nested path fields.

```python
from pydantic_validation_formatter import customize_validation_message
from pydantic import BaseModel, Field, ValidationError, ConfigDict
from typing import Annotated

@customize_validation_message
class Hero(BaseModel):
    id: int = Field(gt=0, lt=5)



@customize_validation_message
class NestedModel(BaseModel):
    heros: list[Hero]
    leader_hero: Hero
    ranks: list[Annotated[int, Field(gt=0)]]
    hero_matrix: list[list[Hero]]

    model_config = ConfigDict(
        validation_message_template = {
            "heros.*.id": {
                "greater_than": "id value should be greater than {gt} but received {input}",
                "missing": "id field is required",
            },
            "leader_hero.id": {
                "greater_than": "id value should be greater than {gt} but received {input}",
                "missing": "id field is required",
            },
            "ranks": {
                "greater_than": "id value should be greater than {gt} but received {input}",
                "missing": "id field is required",
            },
            "hero_matrix.*.*.id": {
                "greater_than": "id value should be greater than {gt} but received {input}",
                "missing": "id field is required",
            },
        }
    )

try:
    NestedModel(heros=[{{"id": 1}}, leader_hero={{"id": 1}}, ranks=[-1], hero_matrix=[[{"id": 2}]]])
except ValidationError as exc:
    print(exc.errors())
```

### Field References

Here the terminal data type referes to such data type which does not have further nested structure like int, str, list of int etc.
Root model field with terminal data type -> just field name
Root model field with iterable terminal data type -> just field name
Root model field with single nested model -> <Root model field name>.<Nested model field name>
Root model field with iterable nested model -> <Root model field name>.*.<Nested model field name>
