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.

"AttributeError: __pydantic_model__" when response_model is union

See original GitHub issue

First Check

  • I added a very descriptive title to this issue.
  • 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.
  • I already read and followed all the tutorial in the docs and didn’t find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

import typing

import fastapi
import httpx
import pydantic as pydantic
import pytest
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import ValidationError
from starlette import status
from starlette.responses import JSONResponse


class ResponseOne(pydantic.BaseModel):
    val: str


class ResponseTwo(pydantic.BaseModel):
    val: str


app = FastAPI()
incorrect_response = {"abc": "def"}


@app.exception_handler(ValidationError)
async def validation_exception_handler(request: fastapi.Request, exc: ValidationError):
    return JSONResponse(
        status_code=status.HTTP_400_BAD_REQUEST,
        content=jsonable_encoder({"detail": exc.errors()}),
    )


@app.get("/union", response_model=typing.Union[ResponseOne, ResponseTwo])
async def union():
    return incorrect_response


@app.get("/single", response_model=ResponseOne)
async def single():
    return incorrect_response


@pytest.fixture
async def client():
    async with httpx.AsyncClient(app=app, base_url="http://localhost") as client:
        yield client


async def test_unprintable(client):
    res = await client.get("/union")
    assert res.status_code == 400


async def test_400(client):
    res = await client.get("/single")
    assert res.status_code == 400

Description

When response_model is set to a union of two possible models and I return something that does not match one of the two models, FastAPI returns a 500 error:

test_.py:30: in validation_exception_handler
    content=jsonable_encoder({"detail": exc.errors()}),
../../../../../Library/Caches/pypoetry/virtualenvs/bla/lib/python3.9/site-packages/pydantic/error_wrappers.py:63: in errors
    config = self.model.__pydantic_model__.__config__  # type: ignore
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = typing.Union[tests.test_.ResponseOne, tests.test_.ResponseTwo]
attr = '__pydantic_model__'

    def __getattr__(self, attr):
        # We are careful for copy and pickle.
        # Also for simplicity we just don't relay all dunder names
        if '__origin__' in self.__dict__ and not _is_dunder(attr):
            return getattr(self.__origin__, attr)
>       raise AttributeError(attr)
E       AttributeError: __pydantic_model__

../../../../../.pyenv/versions/3.9.9/lib/python3.9/typing.py:710: AttributeError

Operating System

macOS

Operating System Details

No response

FastAPI Version

0.81.0

Python Version

3.9.9

Additional Context

It also does not work in FastAPI 0.7* and the pydantic version (1.9 vs 1.0) has no influence, either.

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
chbndrhnnscommented, Sep 2, 2022

Here is a test with pure pydantic which shows the behaviour.

ValidationError seems not to support a union of models for the model parameter.

def test_error_from_union():
    class One(BaseModel):
        val: int

    class Two(BaseModel):
        val: int

    with pytest.raises(ValidationError) as err_one:
        One()

    with pytest.raises(ValidationError) as err_two:
        Two(val="abc")

    model = typing.Union[One, Two]

    error = ValidationError([err_one, err_two], model=model)
    assert error.errors()

0reactions
chbndrhnnscommented, Sep 7, 2022

@JarroVGIT thanks for bringing it up. Yes, it is.

Read more comments on GitHub >

github_iconTop Results From Across the Web

UNION[ ] in response_model( ) chooses the wrong Pydantic ...
UNION [ ] in response_model( ) chooses the wrong Pydantic model and returns a relationship column data when it shouldn't #2783.
Read more >
Distinguishing between Pydantic Models with same fields
As of today (pydantic v1.8.2), the most canonical way to distinguish models when parsing in a Union (in case of ambiguity) is to...
Read more >
Cool Things You Can Do With Pydantic - Medium
Pydantic is a useful library for data parsing and validation. ... When creating models with aliases we pass inputs that match the aliases....
Read more >
Models - pydantic
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 >
Why I use attrs instead of pydantic - The Three of Wands
Going back to the question of validation: assuming your model has a list containing 10000 integers, the validation question has a better answer....
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