Open tomahim opened 6 years ago
+1
+1
+1
I suspect that #98 will fix this.
+1
+1
+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
+1 with python3.6
+1 with python3.6
+1 with python3.6
any quick fix to get a working example going?
+1
@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
@ivanvorona So tricky. I actually did not test it. Sorry for my bad assumption. I'll post what i did in the original post.
@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: https://github.com/graphql-python/graphene-django/issues/185#issuecomment-388469296 hope will help someone else, all the best.
@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.
@ivanvorona Looks like there is nothing special about that plural s. Name EmployeeConnection
anything but EmployeeConnection
(eg. EmployeeCon
, ShahinConnection
) will work just fine :thinking:
@tomahim It should be the relationship field (the only significant difference between the two models :sweat_smile: ). 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
.
+1
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?
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.
Two questions I have:
all_employees
I should just be able to reference the connection.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.
I think I answered my own questions by looking at SQLAlchemyObjectType.__init_subclass_with_meta__
.
_meta.connection
on the class itself. (E.g. Department._meta.connection
)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)
I think I answered my own questions by looking at
SQLAlchemyObjectType.__init_subclass_with_meta__
.
- It looks like you can reference the generated connection by accessing
_meta.connection
on the class itself. (E.g.Department._meta.connection
)- You can specify the connection through the
Meta
optionconnection
.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
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)
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__
.
- It looks like you can reference the generated connection by accessing
_meta.connection
on the class itself. (E.g.Department._meta.connection
)- You can specify the connection through the
Meta
optionconnection
.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.
Rename Connections Classes :-
from DepartmentConnection
to DepartmentConnections
from EmployeeConnection
to EmployeeConnections
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.
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!!
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 :
What am I doing wrong ?
Thank you