question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[BUG] Response model validation doesn't work with `orm_mode = True` on all Pydantic attributes

See original GitHub issue

To Reproduce

  1. Create a file with:
from fastapi import FastAPI
from typing import List
from pydantic import BaseModel

app = FastAPI()

# Subscriptions
class SubscriptionCreate(BaseModel):
    type: str = None
    topic: str = None

class Subscription(SubscriptionCreate):
    id: int = None

    class Config:
        orm_mode = True

# Create a post
@app.post("/subscriptions", response_model=List[Subscription])
def create_subscription(data: List[SubscriptionCreate]):
    print(data)
    return [5]
  1. Submit a POST request from Postman with this body:
[
    {
        "type": "email",
        "topic": "books"
    },
    {
        "type": "email",
        "topic": "animals"
    }
]

Actual behavior

No error and response is this:

[
    {
        "type": null,
        "topic": null,
        "id": null
    }
]

Expected behavior

The error below is what I expect and I do get that if I remove class Config: orm_mode = True from the model.

pydantic.error_wrappers.ValidationError: 1 validation error for Subscription
response -> 0
  value is not a valid dict (type=type_error.dict)

Environment

  • Windows 10
  • Pydantic 1.4
  • FastAPI 0.48.0

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:2
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
tiangolocommented, Jun 4, 2020

So, FastAPI doesn’t really ensure the response has some specific form, that’s not what the response_model really does.

It uses the response_model to convert the data to that shape doing its “best-effort”. This is what allows you to return an ORM model for a User that could contain a hashed password, and then if the response_model doesn’t include it, it will be filtered out, instead of raising an error.

And orm_mode is just a way to tell Pydantic to be more permissive and try even harder. This is because ORM models don’t work like dicts, their data can’t be extracted as would be from a dict. And even more, they don’t even behave like normal classes, if you do dir(some_model) it will probably not contain the actual fields of the model. Because data is actually stored somewhere else, and access is done with getters and setters. And the specifics of this depend on each ORM. So, Pydantic just does its best to extract the data using getattr which is the closest possible to the way ORMs expect to be used.

In fact, Samuel Colvin doesn’t even like ORMs, he implemented orm_mode to allow those use cases and to simplify that for us, that lets us just return an ORM object (or list of ORM objects, etc). It is there only to try and serialize that data skipping private info and doing it with the best effort to save you from writing some extra lines everywhere converting your models to serialized data.

But it is not really made to ensure your code is correct. That’s not its purpose. The fact that, if you return invalid data, it throws an error is more like a side effect of it. But if you enable orm_mode that just tells it “accept even weirder data from arbitrary ORM-like objects”.

If you want to have orm_mode while still having more strict validations, the way to go would be to implement a custom GetterDict made exactly for your custom ORM, detecting if data is there or not, and detecting when it is an error that there’s no data and when not.

The other option you have would be to serialize your models by hand and then make sure you are returning JSONable dicts and lists. But that’s probably not very fun.

1reaction
tiangolocommented, Apr 13, 2020

Thanks for the explanation, didn’t realize it only happens when all fields have default values. This still seems like a bug to me.

Hmm, I’m not sure I get what you mean.

I would think orm_mode implies the object can additionally be an ORM model, not any arbitrary Python object.

orm_mode is the name of the setting, because it has to have some name 🤷 . But it doesn’t mean that it does something specific inferred by the config name.

It accepts arbitrary class instances. To know exactly how it works, check the docs in Pydantic, on the section “ORM Mode (aka Arbitrary Class Instances)”: https://pydantic-docs.helpmanual.io/usage/models/#orm-mode-aka-arbitrary-class-instances


But 5.id and 5[‘id’] raise exceptions, they don’t return None. I think the better behavior would be to only allow dicts and ORM models, or at least disallow objects that throw errors when the attributes are accessed. But I guess regardless of your opinion this is probably a pydantic issue not fastapi

Pydantic actually uses getattr, so it doesn’t raise the attribute doesn’t exist.

If you need to have some custom behavior in how your Pydantic models with orm_mode work, for example adding additional logic to check if you passed valid objects to them or you accidentally passed a list of int, you can use a GetterDict (explained in those same docs).

Read more comments on GitHub >

github_iconTop Results From Across the Web

tiangolo/fastapi - Gitter
I have tried using this error class previously but it changes unexpectedly! David Montague. @dmontagu ?
Read more >
Pydantic validation error field required in FastAPI
UserBase pydantic model contains sent_articles field that must have a list of dicts with "id": value. A validation error is: pydantic.
Read more >
Models - pydantic
Initialisation of the object will perform all parsing and validation, if no ValidationError is raised, you know the resulting model instance is valid....
Read more >
3.1. Pydantic Models - Python
Untrusted data can be passed to a model, and after parsing and validation pydantic guarantees that the fields of the resultant model instance...
Read more >
SQL (Relational) Databases - FastAPI
FastAPI doesn't require you to use a SQL (relational) database. ... And with this, the Pydantic model is compatible with ORMs, and you...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found