tortoise-orm icon indicating copy to clipboard operation
tortoise-orm copied to clipboard

Automatic conversion of validators in pydantic_model_creator

Open jordireinsma opened this issue 4 years ago • 2 comments

Is your feature request related to a problem? Please describe. Consider the following example of a route using FastAPI:

class Person(Model):
  name = fields.CharField(50)
  age = fields.IntField(validators=[MinValueValidator(1)]

PersonSchema = pydantic_model_creator(Person)

@fast_api_app.post("/", response_model=PersonSchema)
async def create_person(data: PersonSchema):
  p = Person.create(**data.dict())
  return p

The PersonSchema does not contain the MinValueValidator. So the PersonSchema can hold an invalid age value, only throwing ValidationError when calling Person.create.

Describe the solution you'd like I would like that pydantic_model_creator would create a model with the same validators as specified on the Tortoise Model.

Describe alternatives you've considered I tried this:

def create_pydantic_model(model: Model, **kwargs):
    schema = pydantic_model_creator(model, **kwargs)
    for name, field in model._meta.fields_map.items():
        validators = [Validator(v) for v in field.validators]
        [setattr(v.func, "__name__", name + "_validator") for v in validators]
        schema.__validators__[name] = validators
    return schema

But Tortoise validators have this signature function(value: T) -> None and Pydantic validators are function(value: T) -> T, so they are kinda incompatible.

The final alternative was implementing an exception handler for Tortoise ValidationErrors.

jordireinsma avatar Oct 22 '21 20:10 jordireinsma

Faced with the same issue. Would be cool to have this working out of the box.

ivanmotorniy avatar Jun 16 '22 13:06 ivanmotorniy

I second this issue. When creating a pydantic schema, there are many constraints that can't be defined in a tortoise model. Since tortoise models are generally used for application internals and pydantic schemas are used for communicating with external services, pydantic models must be able to properly validate data.

Consider the following:

from pydantic import BaseModel, constr
from tortoise.models import Model
from tortoise.contrib.pydantic import pydantic_model_creator

class SomeData_Pydantic_Handmade(BaseModel):
    text: constr(min_length=1, max_length=10)  # Get a string between 1 and 10 chars

class SomeData(Model):
    text= fields.CharField(max_length=10)

SomeData_Pydantic_Generated = pydantic_model_creator(SomeData)

Of course, SomeData_Pydantic_Generated and SomeData_Pydantic_Handmade will validate the entered data type, but the generated schema will not validate min/max length. The lack of full pydantic validator support makes pydantic_model_creator() less useful in some cases.

Would it be possible to add pydantic validators as comments to tortoise models, which pydantic_model_creator() could then pick up in order to construct a full blown pydantic model ?

Example:

class SomeData(Model):
    text= fields.CharField(max_length=10)  #pydantic-type constr(min_length=1, max_length=10)

SomeData_Pydantic_Generated = pydantic_model_creator(SomeData)

This would make SomeData_Pydantic_Generated equal to above's example SomeData_Pydantic_Handmade, and would lead to less code duplication since most of the times schemas and models are somehow equivalent. The overhead of having a different pydantic schema for creation purposes without id and created_at entries would still be achieved the way it is now with exclude statements in the model class.

PS: Sorry if there is already some automagic solution I didn't see to resolve the code duplication

deajan avatar Jul 22 '22 08:07 deajan