-
Notifications
You must be signed in to change notification settings - Fork 227
How to have edges/totalCount on a relationship field? #58
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
Comments
+1 |
Also, I tried the work-around suggested here - graphql-python/graphene#151 by using https://pypi.python.org/pypi/graphene-sqlalchemy/2.0.dev2017072601 but the I had to fork from graphene-sqlalchemy-1.1.1 and add this patch (d66aa07) to get it working. |
@shanmugh, can you make a pull request to add your modification to graphene-sqlalchemy? Or post a working example, so that someone else can do so? I'd be happy to make that pull request for you, if you'd like. |
@singingwolfboy, that modification (length) is already added, but if you need totalCount in your connection, you must inherit Connection class. Because totalCount is not in relay specification and not everyone needs it. |
@neriusmika Can you show me how to do that? This is the code I have so far, and it doesn't seem to work... import graphene
from graphene.relay import Node
from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
from myapp import models
class CountableConnection(SQLAlchemyConnectionField):
total_count = graphene.Int()
def resolve_total_count(self, info):
return self.length
class Contact(SQLAlchemyObjectType, interfaces=[Node]):
class Meta:
model = models.Contact
class User(SQLAlchemyObjectType, interfaces=[Node]):
class Meta:
model = models.User
contacts = CountableConnection(Contact) |
You need to inherit CountableConnection from graphene.relay.Connection, not from SQLAlchemyConnectionField. But there is a problem to pass that connection to your Contact class. It has connection parameter, but now it's useless, because Connection class needs node parameter and you will have circular reference. Probably the simplest way would be to reassign graphene.relay.Connection (look at #65 (comment)), but then it will be used in all your connections. The other way (which I prefer) is to change SQLAlchemyObjectType implementation. |
@neriusmika That's helpful, but do you think you could provide an actual code example? I find it's easier to refer to an example, and tweak it from there. |
Based on your example, you can try this code, but I did not check it: import graphene
from graphene.relay import Node
from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField
from myapp import models
class CountableConnection(graphene.relay.Connection):
class Meta:
abstract = True
total_count = graphene.Int()
@staticmethod
def resolve_total_count(root, info):
return root.length
graphene.relay.Connection = CountableConnection
class Contact(SQLAlchemyObjectType, interfaces=[Node]):
class Meta:
model = models.Contact
class User(SQLAlchemyObjectType, interfaces=[Node]):
class Meta:
model = models.User
class Query(graphene.ObjectType):
node = Node.Field()
all_users = SQLAlchemyConnectionField(User) |
That seems to work. Thank you for the help! |
Im looking for the same, but this approach is not working for me. I ended up subclassing SQLAlchemyObjectType to force it to use the CountableConnecition, like this: class CountableConnection(graphene.relay.Connection):
class Meta:
abstract = True
total_count = graphene.Int()
@staticmethod
def resolve_total_count(root, info, *args, **kwargs):
return root.length
class CustomSQLAlchemyObjectType(SQLAlchemyObjectType):
class Meta:
abstract = True
@classmethod
def __init_subclass_with_meta__(cls, model=None, registry=None, skip_registry=False,
only_fields=(), exclude_fields=(), connection=None,
use_connection=None, interfaces=(), id=None, **options):
# Force it to use the countable connection
countable_conn = connection or CountableConnection.create_type(
"{}CountableConnection".format(model.__name__),
node=cls)
super(CustomSQLAlchemyObjectType, cls).__init_subclass_with_meta__(
model,
registry,
skip_registry,
only_fields,
exclude_fields,
countable_conn,
use_connection,
interfaces,
id,
**options) Thanks |
I've added a PR to fix this issue here #104 |
Here is a complete working example which works without modifying
With this, all conections you define in this way will automatically and uniformly have the appropriate sort: and filters: options.
).
(In the above, using
If you happen to receive error
If you would want to disable automatic wrapping of relationships into connections, so that querying |
I have been trying the above suggestion and struggling to succeed. Constantly getting the below, |
I had the same requirement to add a class ExtendedSQLAlchemyConnectionField(SQLAlchemyConnectionField):
@property
def type(self):
connection_type = super().type
node_type = connection_type._meta.node # !!!!
# We have to define the class at run time in a closure to access the specific `node_type` class
# of the ConnectionField to be able to supply the convenience field `nodes` at the top level of
# the connection object.
class ExtendedConnection(relay.Connection):
class Meta:
node = node_type
total_count = graphene.Int()
edge_count = graphene.Int()
nodes = graphene.List(node_type) # Inject the specific `node` class Field Type
@staticmethod
def resolve_total_count(root, info):
return root.length
@staticmethod
def resolve_edge_count(root, info):
return len(root.edges)
@staticmethod
def resolve_nodes(root, info):
return [edge.node for edge in root.edges]
return ExtendedConnection This lets me define my types in the normal way: class Dog(SQLAlchemyObjectType):
class Meta:
model = DogModel
interfaces = (relay.Node,) And my query: class Query(graphene.ObjectType):
all_dogs = ExtendedSQLAlchemyConnectionField(Dog.connection) and run queries like: query {
allDogs {
totalCount
edgeCount
nodes {
id
}
edges {
cursor
node {
id
}
}
}
} Not sure if this is the best approach, but it works and if it saves someone else from trying to understand the inner workings of graphene, I'm glad to share it. |
@AlexEshoo Thanks a lot sharing. I was able to finally able to get it to working this morning. Nonetheless, it seems like the value of total count I get is actually before Graphql does a DISTINCT on the resultset. To be clear, if my query fetched 50 matching records from my db and only 20 are unique, I see 50 in total count but only get 20 unique records in result. Is there a way to calculate length of unique root instead of |
Very nice! However, @neriusmika's simpler approach in #58 (comment) of defining just a custom standalone class Dog(SQLAlchemyObjectType):
class Meta:
model = DogModel
interfaces = (relay.Node,)
# explicitly define the connection
connection_class = CountableConnection ...and the standard: class Query(graphene.ObjectType):
all_dogs = SQLAlchemyConnectionField(Dog.connection)
all_employees = SQLAlchemyConnectionField(Employee.connection) ...seems to have the added benefit of providing query {
allEmployees {
# totalCount supported with both ExtendedSQLAlchemyConnectionField
# and connection_class=CountableConnection
totalCount
edges {
node {
name
dogs {
# totalCount not supported with ExtendedSQLAlchemyConnectionField,
# but supported with connection_class=CountableConnection
totalCount
edges {
node {
name
}
}
}
}
}
}
} Maybe this won't work for top-level convenience helpers. But for just (All thanks for the examples!) |
This didn't work for me, I get |
Dude figured this out you need the beta/newest version: #272 |
Closing this as the implementation of such a feature seems to be possible. It might be handy to add the recipe presented here to the docs/examples. I'll link this in a separate issue. If you feel that this should be a default feature instead of a manual recipe, please comment there :) |
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. |
Hey,
I'd like to have edges/node/totalCount support on a relationship field.
E.g. if looking at the flask_alchemy example app, I'd like to have query such as this (changes are in bold):
Can this be done?
Thank you
The text was updated successfully, but these errors were encountered: