Mohamed Haydar
Mohamed Haydar

Reputation: 413

Why do I get attribution error in fast-api? AttributeError: type object has no attribute

I'm using fast-api and pydantic for modeling in my project, I created a function convert the attributes to dictionary.

from pydantic import BaseModel


class WidgetItem(BaseModel):
    """Class for WidgetItem"""
    adId: str = ''

    @classmethod
    def generate_widget_item_dict(cls):
        return {
            'adId': cls.adId
        }

Im trying to call generate_widget_item_dict() but I got this error:

AttributeError: type object 'WidgetItem' has no attribute 'adId'

Upvotes: -1

Views: 1907

Answers (2)

Yaakov Bressler
Yaakov Bressler

Reputation: 12008

In the case where you want only some of the attributes of a given class, I would create multiple classes and return the correctly "masked" class. Here's an example to illustrate:
Note: This can get tricky when you have lots of dependencies.

from pydantic import BaseModel


class WidgetItem1(BaseModel):
    """
    Class for WidgetItem

    Note: this class only has 1 attribute
    """
    attr_1: str = 'one'


class WidgetItem2(WidgetItem1):
    """
    A more granular class for WidgetItem ->
    Inherited from `WidgetItem1`

    Note: this class has 3 attributes
    """
    attr_2: str = 'two'
    attr_3: str = 'three'

Given a fastapi endpoint, you can choose to return specific attributes, depending on your response_model:

  • WidgetItem1: will return only attr_1
  • WidgetItem2: will return 3 attributes: attr_1, attr_2, attr_3 Here's some code to demonstrate:
# Choose the response model you want to return

from fastapi import FastAPI

app = FastAPI()


# Create a function to use for multiple endpoints
def get_widget():
    """
    Returns a dummy widget to demonstrate
    functionality. Change the `response_model` to
    see different attributes returned.
    """
    # Initially set all 3 attributes and see
    # which are returned by fastapi based on `response_model`
    result = {
        "attr_1": "one",
        "attr_2": "two",
        "attr_3": "three"
    }
    return result


@app.get("/widgets-1", response_model=WidgetItem1)
def get_widget_1():
    """
    Endpoint which wraps `get_widget()` 
    with `WidgetItem1` as the response model
    """
    return get_widget()

@app.get("/widgets-2", response_model=WidgetItem2)
def get_widget_2():
    """
    Endpoint which wraps `get_widget()` 
    with `WidgetItem2` as the response model
    """
    return get_widget()


Alternatively, you can go ahead and create custom functionality to return a specifically formatted dictionary. If this is the case, the other answers should work for you.

Upvotes: 1

OlafdeL
OlafdeL

Reputation: 290

It happens since WidgetItem should be initiated since it inherits from BaseModel.

This is different than for instance:

class WidgetModel:
    adId: str = ""

    @classmethod
    def generate_widget_item_dict(cls):
        return {
            'adId': cls.adId
        }

In such a case it will work. However this is not a Pydantic model.

If you want to create a dict from a Pydantic model you can just do:

from pydantic import BaseModel


class WidgetItem(BaseModel):
    """Class for WidgetItem"""
    adId: str


w = WidgetItem(adId="id")
w.dict()

Upvotes: 2

Related Questions