Skip to content

Native SQLAlchemy integration/bindings #74

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

Closed
mekarpeles opened this issue Dec 23, 2015 · 10 comments
Closed

Native SQLAlchemy integration/bindings #74

mekarpeles opened this issue Dec 23, 2015 · 10 comments

Comments

@mekarpeles
Copy link

See #51, similarly GraphQL-python-archive/graphql-epoxy#5

@mekarpeles
Copy link
Author

I'm working on something with no security implications (all data is public), so s a make-shift solution (don't use in production), I'm registering (pseudo code) all models using the following approach crufty:

import Base # = some declarative_base(cls=mixin), see [1]
import graphene
class Query(graphene.ObjectType): pass
for k, v in Base._decl_class_registry.items():
    setattr(Query, k.lower(), v)
    setattr(Query, 'resolve_%s' % k, lambda self, args, info: v.get(args.get('id')).dict()) #dict() coming from [1]

[1] see https://github.com/thegroovebox/api.groovebox.org/blob/master/groovebox/api/core.py#L33

Note: _decl_class_registry has some non-models registered with it (e.g. _sa_module_registry), so additional error handling / logic is required.

@syrusakbary
Copy link
Member

@jfeinstein10 already did an amazing work trying to integrate sqlalchemy into graphene.

I'm going to fork its sqlalchemy graphene branch and preserve his work updated with the recent changes of graphene.

@syrusakbary
Copy link
Member

Work in progress pull request: #87

@gwind
Copy link

gwind commented Jan 23, 2016

@jfeinstein10 I saw this code :

class SQLAlchemyNode(BaseNode, SQLAlchemyInterface):
    id = GlobalIDField()

    @classmethod
    def get_node(cls, id, info=None):
        try:
            instance = cls._meta.model.filter(id=id).one()
            return cls(instance)
        except cls._meta.model.DoesNotExist:
            return None

In the cls._meta.model.filter(id=id).one() , I'm concern:

  1. use a django like model query , not original sqlalchemy orm usage.
  2. use id as id. Relay ID is a string, model id maybe integer always.

@syrusakbary I'm working on sqlalchemy backend, any progress would be interested.

@syrusakbary
Copy link
Member

@gwind some code ported by @jfeinstein10 was not updated to reflect the SQLAlchemy nature, as you exposed.

However is actually fixed by some commits I did recently: https://github.com/graphql-python/graphene/blob/sqlalchemy/graphene/contrib/sqlalchemy/types.py#L122

  1. The Relay ID field have to be a String, by spec. The field id in the SQLAlchemy model is mapped to an integer, but this query .filter(id='1') works also well in this cases.

Feel free to fork this work and do any improvements!

@gwind
Copy link

gwind commented Jan 23, 2016

@syrusakbary may be a raise NotImplementedError() would be a good choice.

I have a local sqlachemy binding, but it is very basic, and i am not familiar with graphql - relay.
Thanks,

@gwind
Copy link

gwind commented Jan 24, 2016

@syrusakbary I've saw that graphene.relay.NodeField in root query would convert original id (int or string) to a uniq ID ( type + id => base64) .

So my concern about instance = cls._meta.model.filter(id=id).one() was wrong: id is the object id in table exactly now.

I've another question: the graphene convert all id to type + id => base64 string, but how to get object by id nicely?

example:

class RootQuery(graphene.ObjectType):
    group = graphene.Field(Group, id=graphene.String().NonNull)

    def resolve_group(self, args, info):
        print("args.get('id') = ", args.get('id'))

this query is ok:

query MyQuery {
  node (id: "R3JvdXA6NA==") {
    id
    ... on Group {
      name
      description
    }
  }
}

but query one object is failed:

query MyQuery {
    group(id:"R3JvdXA6NA==") {
      id
      name
    }
}

In graphene/relay/types , there is a to_global_id() , but no a reverse method such like from_global_id() :

class Node(six.with_metaclass(NodeMeta, Interface)):
    '''An object with an ID'''
    id = GlobalIDField()

    class Meta:
        abstract = True

    def to_global_id(self):
        type_name = self._meta.type_name
        return to_global_id(type_name, self.id)

    connection_type = Connection
    edge_type = Edge

    @classmethod
    def get_connection_type(cls):
        return cls.connection_type

    @classmethod
    def get_edge_type(cls):
        return cls.edge_type

graphql_relay/node/node.py have a from_global_id() indeed

def from_global_id(global_id):
    '''
    Takes the "global ID" created by toGlobalID, and retuns the type name and ID
    used to create it.
    '''
    unbased_global_id = unbase64(global_id)
    _type, _id = unbased_global_id.split(':', 1)
    return ResolvedGlobalId(_type, _id)

Is there a nice method to handle this ?

@syrusakbary
Copy link
Member

Graphene is using from_global_id from graphql_relay right now.

However you can customize the id_fetcher function in NodeField (https://github.com/graphql-python/graphene/blob/master/graphene/relay/fields.py#L68)

@gwind
Copy link

gwind commented Jan 26, 2016

I see, thanks 😄

@syrusakbary
Copy link
Member

SQLAlchemy support is now merged into master, closing the issue 😎

ronachong pushed a commit to ronachong/graphene that referenced this issue Nov 3, 2017
…-name

graphql-python#63 Get name of reverse_fields from model.__dict__
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

3 participants