Skip to content

What is the correct way to define custom pydantic types that also play nicely with JSON Schema? #1285

@calvinwyoung

Description

@calvinwyoung

First check

  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google "How to X in FastAPI" and didn't find any information.

Description

What's the best way to define a custom data type that works with both pydantic and JSON Schema?

To illustrate, let's say we want to implement a custom Arrow datetime type (this was also asked in #1186). Borrowing the approach from #452, we could implement the following:

class Arrow(datetime.datetime):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, v):
        return arrow.get(v)

This is kind of weird for 2 reasons:

  1. We have to inherit from datetime.datetime in order for JSON Schema to recognize that this should be represented as a datetime-formatted string. This isn't entirely intuitive — someone could reasonably wonder why we're inherting from datetime.datetime, but then returning an Arrow object in validate.

  2. This only lets us define how we want to decode from JSON. To handle encoding, I have to use json_encoders in my model:

class TestModel(pydantic.BaseModel):
    datetime: Arrow

    class Config:
        json_encoders = {arrow.Arrow: lambda obj: obj.isoformat()}

Notice how the json_encoders entry references an arrow.Arrow object, and not the Arrow type that was defined earlier. This also seems suboptimal.


Is this the correct way to implement custom types? Or is there a better solution?

Ideally I'd love to be able to be able to define a custom type similar to the way marshmallow handles it (i.e., by overriding _serialize and _deserialize methods):
https://marshmallow.readthedocs.io/en/stable/custom_fields.html

Additional context

I recognize that the second point is more closely related to pydantic than it is to fastapi, and certainly there's been some discussion about it (see pydantic/pydantic#951). But that still doesn't address the first point around playing nicely with JSON schema.

I'm kind hoping fastapi can do something on top of pydantic to make this all a bit smoother.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions