graphene-sqlalchemy: SQLAlchemyObjectType hides original SQLAlchemy exception

Hi all!

Here’s a minimal code that explains the issue.

import graphene
from graphene_sqlalchemy import SQLAlchemyObjectType
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

Base = declarative_base()


class A(Base):
    __tablename__ = 'a'
    id = Column(Integer, primary_key=True)


class B(Base):
    __tablename__ = 'b'
    id = Column(Integer, primary_key=True)

    a = relationship(A)  # This is the breaking line


class BSchema(SQLAlchemyObjectType):
    class Meta:
        model = B


schema = graphene.Schema(query=BSchema)

When I try to run this file I only get this error:

Traceback (most recent call last):
  File "issue.py", line 22, in <module>
    class BSchema(SQLAlchemyObjectType):
  File "./.venv/lib/python3.6/site-packages/graphene/utils/subclass_with_meta.py", line 40, in __init_subclass__
    super_class.__init_subclass_with_meta__(**options)
  File "./.venv/src/graphene-sqlalchemy/graphene_sqlalchemy/types.py", line 97, in __init_subclass_with_meta__
    ).format(cls.__name__, model)
AssertionError: You need to pass a valid SQLAlchemy Model in BSchema.Meta, received "<class '__main__.B'>".

But I’m passing a valid SQLAlchemy Model in BSchema.Meta.

The problem is actually another. SQLAlchemy is raising this exception: {NoForeignKeysError}Could not determine join condition between parent/child tables on relationship B.a - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.

But this exception is lost here https://github.com/graphql-python/graphene-sqlalchemy/blob/a2fe9263d73b3baccd63b192b983cda5abfb88f0/graphene_sqlalchemy/utils.py#L21-L27 (called from https://github.com/graphql-python/graphene-sqlalchemy/blob/a2fe9263d73b3baccd63b192b983cda5abfb88f0/graphene_sqlalchemy/types.py#L94) making the debug process for a simple mistake a pain.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 33
  • Comments: 15 (5 by maintainers)

Commits related to this issue

Most upvoted comments

This really makes troubleshooting model issues very difficult. I hit this problem on a regular basis.

Why does this line not at least print out the stack trace? I spent 1 hours finding what the root cause before find out this issue.

👍 I lost much time in troubleshooting to understand the real problem

Yup, hit that problem too. It seems assertions are used a lot in the codebase, where proper exceptions would be more appropriate and yield more useful stack traces.

Thanks! Looks good!

Neat! 😄 The conflicts aren’t too difficult, so I went ahead and rebased my PR and integrated the suggested changes. I also switched the git repository references in .pre-commit-config.yaml from git:// to https://, since GitHub no longer supports the unauthenticated git:// protocol

Tests pass locally on my machine, so hopefully CI is happy with them as well 😂

@connorbrinton, thanks for the quick reply after such a long time! I just gained access to this repo, so I can merge PRs and manage things. Still, I’m currently getting an overview of old and stagnant issues to see what was planned and ready to deploy, so any help or hints are highly appreciated!

@erikwrede I gave up on my PR after waiting a few months for interaction from maintainers. Nowadays I use Ariadne to create my GraphQL services. While it doesn’t have a SQLAlchemy integration available, there’s less “magic” involved in resolving queries, which makes the apps easier to understand in my opinion

If someone wants these enhancements merged to graphene-sqlalchemy, I think they’ll either need to become a maintainer or fork the project 🤷‍♂️

How annoying this is. For anyone else coming here with a Flask app, I had to comment out importing my graphqlview and schema.

in myproject/__init__.py

...
#from myproject.routes.graphql import graphql_view
...

def create_app():
    ...
    #app.add_url_rule('/graphql', view_func=graphql_view())

in myproject.routes.graphql, where schema.py was the problem

from flask_graphql import GraphQLView

from myproject.schema import schema
from myproject.routes.decorators import authentication_required

def graphql_view():
    return authentication_required(GraphQLView.as_view('graphql', schema=schema, graphiql=False))

this allowed me to flask shell without an error and when I tried to query a problematic model I got the real error.

It seems like there are multiple solutions proposed for this. I was taking a look at this as well and was thinking of a different one. Something like the following. I was wondering why we are catching ArgumentError.

def __init_subclass_with_meta__(...):
        ...
        if not is_mapped_class(model):
            raise Exception(
                'You need to pass a valid SQLAlchemy Model in {}.Meta, received "{}".'.format(
                    cls.__name__, model
                )
            )
        # assert is_mapped_class(model), (
        #    "You need to pass a valid SQLAlchemy Model in " '{}.Meta, received "{}".'
        # ).format(cls.__name__, model)
        ...

def is_mapped_class(cls):
    try:
        class_mapper(cls)
    except (UnmappedClassError):
    #except (ArgumentError, UnmappedClassError):
        return False
    else:
        return True