Skip to content
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

flask_sqlalchemy example : different types with the same name in the schema: EmployeeConnection, EmployeeConnection. #153

Open
tomahim opened this issue Aug 1, 2018 · 29 comments

Comments

@tomahim
Copy link
Contributor

tomahim commented Aug 1, 2018

Hi,

I followed the instruction to use the flask_sqlalchemy example with a Python 2.7 version, when I run the app.py script, I've got the following error :

(py27) λ python app.py                                                                                                                      
Traceback (most recent call last):                                                                                                          
  File "app.py", line 7, in <module>                                                                                                        
    from schema import schema                                                                                                               
  File "D:\Thomas\Dev\graphene-sqlalchemy\examples\flask_sqlalchemy\schema.py", line 60, in <module>                                        
    schema = graphene.Schema(query=Query, types=[Department, Employee, Role])         

....
                            
  File "D:\Programmes\Anaconda2\envs\py27\lib\site-packages\graphene\types\typemap.py", line 99, in graphene_reducer                        
    ).format(_type.graphene_type, type)                                                                                                     
AssertionError: Found different types with the same name in the schema: EmployeeConnection, EmployeeConnection.  

What am I doing wrong ?

Thank you

@tomahim tomahim changed the title flask_sqlalchemy example : AssertionError: Found different types with the same name in the schema: EmployeeConnection, EmployeeConnection. flask_sqlalchemy example : different types with the same name in the schema: EmployeeConnection, EmployeeConnection. Aug 1, 2018
@stan-sack
Copy link

+1

2 similar comments
@cdestiawan
Copy link

+1

@mahrz24
Copy link

mahrz24 commented Aug 6, 2018

+1

@wichert
Copy link
Contributor

wichert commented Aug 7, 2018

I suspect that #98 will fix this.

@young-k
Copy link

young-k commented Aug 8, 2018

+1

1 similar comment
@cekk
Copy link

cekk commented Aug 11, 2018

+1

@ghost
Copy link

ghost commented Aug 14, 2018

+1

Seems like this error only occurs, when using sqlalchemy.orm.relationship in the model class with a backref.

btw. tested with Python 3.6 and 3.7

@kowenzhang
Copy link

+1 with python3.6

2 similar comments
@pangxudong
Copy link

+1 with python3.6

@suxin1
Copy link

suxin1 commented Aug 17, 2018

+1 with python3.6

@hoffrocket
Copy link

any quick fix to get a working example going?

@ivanvorona
Copy link

+1

@ivanvorona
Copy link

@suxin1 - your fix doesn't work for me, i got this error:
TypeError: init_subclass_with_meta() got an unexpected keyword argument 'emp
loyees'
or this(if exactly like in your example):
raise ImportError("%s doesn't look like a module path" % dotted_path)
ImportError: Employee doesn't look like a module path

@suxin1
Copy link

suxin1 commented Aug 20, 2018

@ivanvorona So tricky. I actually did not test it. Sorry for my bad assumption. I'll post what i did in the original post.

@ivanvorona
Copy link

@suxin1 thank you for your code but i was lazy reading it :) anyway, the fix is as simple as replacing "EmployeeConnection" with "EmployeeConnections", following post helped me identify root cause:
graphql-python/graphene-django#185 (comment)
hope will help someone else, all the best.

@tomahim
Copy link
Contributor Author

tomahim commented Aug 24, 2018

@ivanvorona @suxin1 Good work, renaming to EmployeeConnections fix the example !

However for my own understanding, why this error occurs for EmployeeConnection and not for RoleConnection and DepartmentConnection ? I just try to identify the real issue, but I don't understand why the EmployeeConnection exists at the first place.

@shahinism
Copy link

@ivanvorona Looks like there is nothing special about that plural s. Name EmployeeConnection anything but EmployeeConnection (eg. EmployeeCon, ShahinConnection) will work just fine 🤔

@shahinism
Copy link

@tomahim It should be the relationship field (the only significant difference between the two models 😅 ). Graphene-SQLAlchemy produces a connection to resolve employees field inside allDepartment query, Looks like that connection will get the same name as EmployeeConnection in final mapping that has been passed the graphene_reducer.

@delgadofarid
Copy link

+1

@Forever-Young
Copy link
Contributor

What about changing auto-naming here:

        if use_connection and not connection:
            # We create the connection automatically
            if not connection_class:
                connection_class = Connection

            connection = connection_class.create_type(
                "{}Connection".format(cls.__name__), node=cls
            )

to something having distinct auto-generated suffix/prefix?

@RonaldZhao
Copy link

I solved this problem by changing a few names in schema.py, here is my code:

import graphene
from graphene import relay
from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
from models import Department, Employee


class DepartmentNode(SQLAlchemyObjectType):
    class Meta:
        model = Department
        interfaces = (relay.Node,)


class DepartmentConnection(relay.Connection):
    class Meta:
        node = DepartmentNode


class EmployeeNode(SQLAlchemyObjectType):
    class Meta:
        model = Employee
        interfaces = (relay.Node,)


class EmployeeConnection(relay.Connection):
    class Meta:
        node = EmployeeNode


class Query(graphene.ObjectType):
    node = relay.Node.Field()
    all_employees = SQLAlchemyConnectionField(EmployeeConnection)
    all_departments = SQLAlchemyConnectionField(DepartmentConnection, sort=None)


schema = graphene.Schema(query=Query)

But why? I do not know, just lucky.

@thejcannon
Copy link

thejcannon commented Apr 1, 2019

Two questions I have:

  1. How do I reference the generated connection? If it satisfies my needs in all_employees I should just be able to reference the connection.
  2. How can I specify the connection to use for the relationship? If the generated connection doesn't meet my needs, I'd like to be able to specify the connection type to use.

Having the same connection schema or slightly differing ones both with similar names sounds confusing and not ideal.

@thejcannon
Copy link

I think I answered my own questions by looking at SQLAlchemyObjectType.__init_subclass_with_meta__.

  1. It looks like you can reference the generated connection by accessing _meta.connection on the class itself. (E.g. Department._meta.connection)
  2. You can specify the connection through the Meta option connection.

So if you wanted to fix the example "correctly", don't define the "Connection" classes, and use the generated ones:

class Query(graphene.ObjectType):
    node = relay.Node.Field()
    all_employees = SQLAlchemyConnectionField(Employee._meta.connection)
    all_departments = SQLAlchemyConnectionField(Department._meta.connection, sort=None)

@mhdsyarif
Copy link

mhdsyarif commented Apr 9, 2019

I think I answered my own questions by looking at SQLAlchemyObjectType.__init_subclass_with_meta__.

  1. It looks like you can reference the generated connection by accessing _meta.connection on the class itself. (E.g. Department._meta.connection)
  2. You can specify the connection through the Meta option connection.

So if you wanted to fix the example "correctly", don't define the "Connection" classes, and use the generated ones:

class Query(graphene.ObjectType):
    node = relay.Node.Field()
    all_employees = SQLAlchemyConnectionField(Employee._meta.connection)
    all_departments = SQLAlchemyConnectionField(Department._meta.connection, sort=None)

Work for me

@zmwangx
Copy link

zmwangx commented Jun 14, 2019

Expanding on previous comments, here's a working example of a custom connection type (graphene-sqlalchemy==2.2.0):

from graphene import Int, NonNull, ObjectType
from graphene.relay import Connection, Node
from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField


# Implements the totalCount field in a connection.
class CountedConnection(Connection):
    class Meta:
        # Being abstract is important because we can't reference the
        # node type here without creating a circular reference. Also, it
        # makes reuse easy.
        #
        # The node type will be populated later with
        # `CountedConnection.create_class()` in `Foo`'s
        # `__init_subclass_with_meta__()`.
        abstract = True

    total_count = NonNull(Int)

    def resolve_total_count(self, info, **kwargs):
        return self.length


# FooConnection is autogenerated due to the Node interface.
class Foo(SQLAlchemyObjectType):
    class Meta:
        model = FooModel
        interfaces = (Node,)
        connection_class = CountedConnection


# The connection field can be customized too.
class FooConnectionField(SQLAlchemyConnectionField):
    pass


class Query(ObjectType):
    fooList = FooConnectionField(Foo)

pg1992 pushed a commit to pg1992/graphql-python-tutorial that referenced this issue Aug 26, 2019
@mfrlin
Copy link

mfrlin commented Sep 5, 2019

Ignore fixes with renaming, this comment is on the right track.

I think I answered my own questions by looking at SQLAlchemyObjectType.__init_subclass_with_meta__.

  1. It looks like you can reference the generated connection by accessing _meta.connection on the class itself. (E.g. Department._meta.connection)
  2. You can specify the connection through the Meta option connection.

So if you wanted to fix the example "correctly", don't define the "Connection" classes, and use the generated ones:

class Query(graphene.ObjectType):
    node = relay.Node.Field()
    all_employees = SQLAlchemyConnectionField(Employee._meta.connection)
    all_departments = SQLAlchemyConnectionField(Department._meta.connection, sort=None)

Example has been since fixed with:

class Query(graphene.ObjectType):
    node = relay.Node.Field()
    # Allow only single column sorting
    all_employees = SQLAlchemyConnectionField(
        Employee, sort=Employee.sort_argument())
    # Allows sorting over multiple columns, by default over the primary key
    all_roles = SQLAlchemyConnectionField(Role)
    # Disable sorting over this field
    all_departments = SQLAlchemyConnectionField(Department, sort=None)

So you do not have to access private variable _meta.

This issue should be closed along with pull requests #162.

@kaguru
Copy link

kaguru commented Nov 7, 2019

Rename Connections Classes :-
from DepartmentConnection to DepartmentConnections
from EmployeeConnection to EmployeeConnections

@KrishyV
Copy link

KrishyV commented Oct 20, 2020

I was facing this issue trying to reuse an enum and I do not use Relay like most of the people here. After looking through the source code which was referenced in this comment , I found a method to reuse the auto-generated types if you need to reference them.

@classmethod
    def enum_for_field(cls, field_name):
        return enum_for_field(cls, field_name)

I'll share my solution in-case someone down the road needs this.

class UpdatePickingListMutation(graphene.Mutation):
    picking_list = graphene.List(PickingListQuery)

    class Arguments:
        pickingListStatus = graphene.Argument(PickingListQuery.enum_for_field('pickingListStatus'))

This allowed me to reference the enum from my SQLAlchemy models that was converted into a graphene type.

class PickingList(SQLAlchemyObjectType):
    class Meta:
        model = PickingListModel

TLDR: Use enum_for_field(name_of_sqlalchemy_column) to reference the auto-generated Graphene type if you are using Enum.

@klintan
Copy link

klintan commented Mar 6, 2021

I was facing this issue trying to reuse an enum and I do not use Relay like most of the people here. After looking through the source code which was referenced in this comment , I found a method to reuse the auto-generated types if you need to reference them.

@classmethod
    def enum_for_field(cls, field_name):
        return enum_for_field(cls, field_name)

I'll share my solution in-case someone down the road needs this.

class UpdatePickingListMutation(graphene.Mutation):
    picking_list = graphene.List(PickingListQuery)

    class Arguments:
        pickingListStatus = graphene.Argument(PickingListQuery.enum_for_field('pickingListStatus'))

This allowed me to reference the enum from my SQLAlchemy models that was converted into a graphene type.

class PickingList(SQLAlchemyObjectType):
    class Meta:
        model = PickingListModel

TLDR: Use enum_for_field(name_of_sqlalchemy_column) to reference the auto-generated Graphene type if you are using Enum.

Oh man this really needs to be documented somewhere. Wasted too much time on this and separately found this exact solution.

Thanks for sharing it!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests