graphql-python / graphene-sqlalchemy

Graphene SQLAlchemy integration
http://docs.graphene-python.org/projects/sqlalchemy/en/latest/
MIT License
974 stars 225 forks source link

Union of SQLAlchemy connections and simple objects #369

Closed jmartinhoj closed 1 year ago

jmartinhoj commented 1 year ago

I need a query to return either a list of SQLAlchemy objects (via a connection) or an error message. Here's the code:

from models import Users
from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
import graphene

class User(SQLAlchemyObjectType):
    class Meta:
        model = Users #SQLAlchemy model
        interfaces = (graphene.relay.Node,)

class Message(graphene.ObjectType):
    message = graphene.String()

class UserResults(graphene.ObjectType):
    users = SQLAlchemyConnectionField(User.connection)

class UnionUsers(graphene.Union):
    class Meta:
        types = (UserResults, Message)

    @classmethod
    def resolve_type(cls, instance, info):
        if(type(instance) is list):
            return UserResults
        return Message

class Query(graphene.ObjectType):
    users = graphene.Field(type=UnionUsers)

    def resolve_all_users(self, info, **kwargs):
        if('''condition'''):
            return Message(message="asd")
        query = User.get_query(info)
        return query.filter_by(email = "foo@foo.foo").all()

When the ''''condition''' is met and a Message is returned, it works well. Unfortunately, otherwise, when a list of Users is returned, the list of users that is returned in graphql is the complete list of all users, no matter the filter in the query. Is this a bug or should I do it differently?

erikwrede commented 1 year ago

Field users of UserResults has its own resolver (provided by SQLAlchemyConnectionField(User.connection)).

When your resolve_all_users resolver returns the filtered query result, it is passed as a parent to UserResults. Then,UserResults runs the field resolver for users, which is specified in the SQLAlchemyConnectionField. Since the list of filtered user is the parent value of this field, not the field's value itself, this resolver queries the database again. Now, you get the full list of users.

The simplest fix would be to create a custom resolver:

class UserResults(graphene.ObjectType):
    users = SQLAlchemyConnectionField(User.connection)

    def resolve_users(parent, info): # parent is of type list because it was returned by `resolve_all_users`
        return parent

Hope that helps!

jmartinhoj commented 1 year ago

That's it! Thank you!

github-actions[bot] commented 1 year ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related topics referencing this issue.