Skip to content

Abstract type 'TemplateItemsORMInterface' must resolve to an Object type at runtime for field 'ItemORM.ItemTemplate'. #233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
bojanstavrikj opened this issue Feb 4, 2025 · 1 comment
Labels
bug Something isn't working

Comments

@bojanstavrikj
Copy link

bojanstavrikj commented Feb 4, 2025

Hello all, I have been stuck on this problem and can't figure out how to resolve it.

I am using the StrawberrySQLAlchemyMapper to create my strawberry classes based on my ORM classes. It all works fine until I reach a table which is polymorphic.

These are my ORM classes:

class PurchaseORM(BaseORM):
    __tablename__ = "purchases"
    
    id: Mapped[uuid] = mapped_column(nullable=False, primary_key=True)
    templateId: Mapped[uuid] = mapped_column(nullable=False)
    createdBy: Mapped[str] = mapped_column(nullable=False)
    creationDate: Mapped[datetime] 
    
    Items: Mapped[List["ItemORM"]] = relationship( "ItemORM",  back_populates='purchase' )

class ItemORM(BaseORM):
    __tablename__ = 'items'

    id: Mapped[uuid] = mapped_column(nullable=False, primary_key=True)
    purchaseId: Mapped[uuid] = mapped_column(ForeignKey("purchases.id"), nullable=False)
    templateItemId: Mapped[uuid] = mapped_column( nullable=False)
    status: Mapped[str] = mapped_column(nullable=True)
    updatedAt: Mapped[datetime] = mapped_column(nullable=False, default=func.current_timestamp(), onupdate=func.current_timestamp())
    createdAt: Mapped[datetime] = mapped_column(nullable=False, default=func.current_timestamp())
    
    purchase: Mapped["PurchaseORM"] = relationship("SnapshotContext", back_populates='Items')
    itemTemplate: Mapped["TemplateItemsORM"] = relationship("TemplateItemsORM", back_populates='Item')

class TemplateItemsORM(BaseORM):
    __tablename__ = 'template_items'
    id: Mapped[uuid] = mapped_column(ForeignKey("items.templateItemId"), nullable=False, primary_key=True)
    name: Mapped[str]
    type: Mapped[str]
    description: Mapped[str]
    
    Item: Mapped["ItemORM"] = relationship("ItemORM", back_populates='itemTemplate')

    __mapper_args__ = {
        'polymorphic_on': 'type', 
        'polymorphic_identity': 'TemplateItemsORM',
    }

class alarmTemplateItemORM(TemplateItemsORM):
    
    __mapper_args__ = {
        'polymorphic_identity': 'alarm',
    }
    
class sensorTemplateItemORM(TemplateItemsORM):
    
    __mapper_args__ = {
        'polymorphic_identity': 'sensor',
    }

Then I create the related strawberry classes using strawberry StrawberrySQLAlchemyMapper as follows:

strawberry_sqlalchemy_mapper = StrawberrySQLAlchemyMapper()

@strawberry.input
class PurchaseInput:
    id: str

@strawberry_sqlalchemy_mapper.type(PurchaseORM)
class Purchase:
    pass

@strawberry_sqlalchemy_mapper.type(ItemORM)
class Item:
    pass

@strawberry_sqlalchemy_mapper.type(TemplateItemsORM)
class TemplateItem:
    pass

@strawberry_sqlalchemy_mapper.type(alarmTemplateItemsORM)
class alarmTemplateItems:
    pass

@strawberry_sqlalchemy_mapper.type(sensorTemplateItemsORM)
class sensorTemplateItems:
    pass

@strawberry.type
class Query:
    @strawberry.field
    def getPurchaseById(self, filter: PurchaseInput) -> Purchase:
        session = bootstrap.bootstrapDBEngine(config.get('db_config', {}))
        with session() as session:
            pm = with_polymorphic(TemplateItemsORM, [alarmTemplateItemsORM, sensorTemplateItemsORM])
            sql = select(PurchaseORM).filter(PurchaseORM.id==filter.id).options(
                joinedload(PurchaseORM.Items).options(joinedload(
                    TemplateItemsORM.itemTemplate.of_type(pm)
                )),
            )
            snapshot = session.execute(sql).scalars().first()
            
            return snapshot

And then the query I execute in the playground is the following:

query MyQuery { getPurchaseById(filter: {id: "334dbb48-31b8-4d18-a9c0-123d7be7a17c"}) { id createdBy Items { edges { node { id status TemplateItem { id name description } } } } } }

I get the following error and I cannot figure out how to resolve it:

graphql.error.graphql_error.GraphQLError: Abstract type 'TemplateItemsORMInterface' must resolve to an Object type at runtime for field 'ItemORM.ItemTemplate'. Either the 'TemplateItemsORMInterface' type should provide a 'resolve_type' function or each possible type should provide an 'is_type_of' function.

I have tested the query inside getPurchaseById, and the sqlalchemy query returns the what I am expecting.

I havent been able to figure out where and how to define these resolve_type or is_type_of functions. Any help is aprpeciated!

@Ckk3 Ckk3 added the bug Something isn't working label Feb 4, 2025
@fruitymedley
Copy link
Contributor

fruitymedley commented Feb 8, 2025

As the error says, you need to define a resolve_type function. This can be done on your TemplateItemsORM class in the following manner:

class TemplateItemsORM(BaseORM):

    ...

    def resolve_type(self) -> Type[Any]:
        return {
            "alarm": alarmTemplateItemORM,
            "sensor": sensorTemplateItemORM
        }[self.type]

This function tells strawberry how to interpret the polymorphic type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants