Giter Club home page Giter Club logo

graphene's People

Contributors

adamcharnock avatar alecaivazis avatar alecrosenbaum avatar amitsaha avatar aryaniyaps avatar bossgrand avatar cclauss avatar cito avatar dan98765 avatar danpalmer avatar defrex avatar dvndrsn avatar ekampf avatar erikwrede avatar globegitter avatar hung-phan avatar jdugan1024 avatar jhgg avatar jkimbo avatar justinrmiller avatar mvanlonden avatar pidelport avatar pizzapanther avatar rgroothuijsen avatar sjhewitt avatar syrusakbary avatar tcleonard avatar ulgens avatar varundey avatar yen223 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphene's Issues

Cannot use ListField as mutation input

I'm porting a graphql server that was created with your graphql-relay js port. I have a mutation that has a list field as an input and works fine, however when I try to create a list field with graphene I get the following error:

AssertionError: NewCellInnerInput.newCells field type must be Input Type but got: [Cell].

Below is a snippet from my ported schema:

class Cell(graphene.ObjectType):
    title = graphene.StringField()
    item = graphene.StringField()


class NewCell(relay.ClientIDMutation):
    class Input:
        parent = graphene.IDField(required=True)
        newCells = graphene.ListField(Cell)

    parent = graphene.Field(Tree)

Add GeoJSON types

Add GeoJSON types (graphene.geo) and the mapping from Django Geo fields to it.

TypeError: __init__() got an unexpected keyword argument 'subscription

I am trying to introspect a schema that worked fine with graphene 0.4.0.1 but now breaks with 0.4.2 (unchanged), getting the following error:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "third_party/python/graphene/core/schema.py", line 126, in introspect
    return self.execute(introspection_query).data
  File "third_party/python/graphene/core/schema.py", line 117, in execute
    self.schema,
  File "third_party/python/graphene/core/schema.py", line 77, in schema
    subscription=self.T(self.subscription))
  File "third_party/python/graphene/core/schema.py", line 21, in __init__
    super(GraphQLSchema, self).__init__(*args, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'subscription'

I am not using subscriptions right now in any way.

Multiple `ConnectionField` fields do not function as expected

I am finding that once I have multiple relay.ConnectionField fields attached to a Query, all requests will be validated against the second field

I have the following Query class:

class Query(ObjectType):
    # Accounts have a 'name' field
    all_accounts = relay.ConnectionField(nodes.Account, description='All the accounts')
    # Transactions do not have a 'name' field
    all_transactions = relay.ConnectionField(nodes.Transaction, description='All the transactions')

    @resolve_only_args
    def resolve_all_accounts(self, **kwargs):
        return [nodes.Account(obj) for obj in models.Account.objects.all()]

    @resolve_only_args
    def resolve_all_transactions(self, **kwargs):
        return [nodes.Transaction(obj) for obj in models.Transaction.objects.all()]

Upon running this query:

query {
  allAccounts {
    edges {
      node {
        name
      }
    }
  }
}

I receive:

{
  "errors": [
    {
      "locations": [
        {
          "column": 9,
          "line": 5
        }
      ],
      "message": "Cannot query field \"name\" on \"Transaction\"."
    }
  ]
}

Note that the above error seems to think I am querying Transaction which, after tripple checking my code, I'm pretty sure I'm not. Also, if I switch the order of the fields on the Query class then the query will be successfully processed and data returned.

Caveat: I'm still pretty new to this, and the whole edges { nodes { part of that query looks a little suspect to me. However, there are little to no examples I can find on anywhere online regarding how one should query for more than a single object. I'm almost entirely running off GraphiQL's autocomplete!

Share fields between mutation input and relay Node

I want to share fields between a relay Node and a mutation input as shown in this example: http://graphene-python.org/playground/?schema=import%2520collections%250A%250Aimport%2520graphene%250Afrom%2520graphene%2520import%2520relay%250A%250Aclass%2520SharedUserFields(graphene.ObjectType)%253A%250A%2520%2520%2520%2520first_name%2520%253D%2520graphene.String()%250A%2520%2520%2520%2520last_name%2520%253D%2520graphene.String()%250A%250Aclass%2520User(relay.Node)%253A%250A%2520%2520%2520%2520user_id%2520%253D%2520graphene.ID()%250A%250A%2520%2520%2520%2520%2540classmethod%250A%2520%2520%2520%2520def%2520get_node(cls%252C%2520user_id)%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520user%2520%253D%2520%257B%27user_id%27%253A%2520%271%27%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520User(**user)%250A%250AUser.extend_fields(%255BSharedUserFields%255D)%250A%250Aclass%2520UpdateCustomer(relay.ClientIDMutation)%253A%250A%2520%2520%2520%2520class%2520Input(SharedUserFields)%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520pass%250A%2509%250A%2520%2520%2520%2520user%2520%253D%2520graphene.Field(User)%250A%2520%2520%2520%2520%250A%2520%2520%2520%2520def%2520mutate_and_get_payload(cls%252C%2520mutation_input%252C%2520info)%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2523%2520update%2520stuff%2520here%250A%2520%2520%2520%2520%2520%2520%2520%2520return(User(first_name%253D%27Mutated%2520first_name%27))%250A%2520%2520%2520%2520%250Aclass%2520Query(graphene.ObjectType)%253A%250A%2520%2520%2520%2520user%2520%253D%2520graphene.Field(User)%250A%2520%2520%2520%2520%250A%2520%2520%2520%2520def%2520resolve_user(self%252C%2520args%252C%2520info)%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520user%2520%253D%2520%257B%27user_id%27%253A%2520%271%27%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520User(**user)%250A%250Aschema%2520%253D%2520graphene.Schema(query%253DQuery)%250A&query=query%2520testQuery%2520%257B%250A%2520%2520user%2520%257B%250A%2520%2520%2520%2520firstName%250A%2520%2520%2520%2520%257D%250A%257D

For some reason extend_fields does not work on the playground but maybe the graphene version is outdated.

But how would it be possible to share graphene fields between the User relay Node and the UpdateCustomerInput to reduce duplication?

Improvements next stable package

Based on a new implementation, is needed to improve the following things for next stable package:

  • Schema lazy type registering: Register types as schema access it.
  • Simplify schema base types. Avoid @memoize hacks. EDIT: Partially resolved in fields. Improve schema type getter
  • Add field description based on resolver function __doc__
  • Add python class enums.Enum mapping to a GraphQL Enum
  • Map received arguments keys by resolvers to be snake_case
  • Add Mutations
  • Add Relay Mutations
  • Generalize use of instance/types instantiation
  • Add field definition in types using only resolvers and decorators

One way for deal with most of this improvements is using a TypeRegister as epoxy is doing, however for new types behavior is not as easy to scale.

Research on TypeRegister

Having a Type Register means it have to create a different object each time to have to access, for example, an interface (without doing any _add_interface_impl hacks).
Also, it means the graphql-core type is fixed for each defined class.

The advantages of having a fixed type your_class.T is that could be shared across not only this graphene projects, but any that uses graphql-core.
However, the cost of having a fixed type for each defined class are more than the advantages.

Disadvantages

Why is not good to have a fixed type for each class? Some classes, like a relay Node depends a lot on the context where is being executed (or schema/registry). Why create a new Node class for just changing the context?
Also, it requires to add manually the type into the registry (via mixins, or hacking the registry for automatically adding my type).

Practical solution

Create a schema.T(object_type) that will return the type for an object_type associated for this schema.

Field "myTests" argument "id" of type "ID!" is required but not provided.

First of all great work on these sets of python libraries they really seem great so far :)

Anyway, just trying out graphene 0.1.6.0 (on python 2.7.x) with react/relay for the first time so might be some beginners mistake, But having

class Test(relay.Node):

    custom_id = graphene.IDField(description='The id of the test.')
    test_name = graphene.StringField(description='The name of the test.')
    description = graphene.StringField(description='The description of the test.')
    test_type = graphene.StringField(description='What type the test has.')
    test_price = graphene.StringField(description='The price of each test.')
    test_clients = graphene.StringField(description='Number of test clients.')

    @classmethod
    def get_node(cls, custom_id):
        print('in get_node', custom_id)
        return Test(getTest(custom_id))

class Query(graphene.ObjectType):
    my_tests = relay.NodeField(Test,
                               customArg=graphene.Argument(graphene.String))
   all_my_tests = relay.ConnectionField(Test,
                                      connection_type=MyConnection,
                                      customArg=graphene.Argument(graphene.String))
  node = relay.NodeField()

    @resolve_only_args
    def resolve_my_tests(self, **kwargs):
        print('we are resolving all tests!')
        for key in kwargs:
            print('kwargs received', key, kwargs[key])
        return [Test(t) for t in getTests()]

I am managing to return some data with hand-written queries, but trying a query that react passes, e.g.

result = schema.execute('''
        query TestsQuery {
            myTests (customArg:"1") {
                ...__RelayQueryFragment0pgs7aw
            }
        }
        fragment __RelayQueryFragment0pgs7aw on Test{custom_id,description,id}
        ''')

just throws the error Field "myTests" argument "id" of type "ID!" is required but not provided.. If I do provide an id field I get an error that it clashes:

class Test(relay.Node):
  File "/usr/local/lib/python2.7/dist-packages/graphene/core/types.py", line 85, in __new__
    field_names[field.name].__class__)
Exception: Local field 'id' in class 'Test' (<class 'graphene.relay.fields.GlobalIDField'>) clashes with field with similar name from Interface Node (<class 'graphene.core.fields.StringField'>)

Any idea how to get around/fix this?

Edit: I made some progress using the ConnectionField but I suppose I am just a bit unsure what the best way forward is with graphene if I want to define a schema where I can show all tests I have but also query them for certain parameters such as name, type etc.

The need for a connection class (with its own fields) with ConnectionField seems a bit like an extra step I am not sure is necessary for what I am trying to do.

So I suppose what I am imagining is something like

query testsQuery {
  allTests(customArg: "1") {
    ...__RelayQueryFragment0pgs7aw
  }
}
fragment __RelayQueryFragment0pgs7aw on Test{custom_id,description, test_name, id}

Where-as right now I am creating on the fronend

query testsQuery{allTests(customArg:"1"){...__RelayQueryFragment0pgs7aw}} 
fragment __RelayQueryFragment0pgs7aw on TestMyConnection{
  edges{node{custom_id,description, test_name, id},cursor},
  pageInfo{hasNextPage,hasPreviousPage
}}

Dealing with cursors in ConnectionField resolve functions

In the Star Wars relay example, it looks like you expect the resolve_ships method of the Faction class to return every ship. While this is feasible with a small dataset like that, it's not an assumption you can make. Does the resolve function get access to the first and after argument? Also, how do we supply pagination info?

Looking at the code, it looks like connection_from_list deals with the resolved data as a complete list. Is there a way to get around this? If the resolve function only gets the arguments, but no way to supply pagination info, should we just return 1 more object than was requested, so that connection_from_list knows there's more and can correctly create the pagination info?

GraphQLSchema does not expect kwarg 'subscription'

I suspect this is related to b91ae4b.

I am currently on version 3712542.

The exception was:

Traceback (most recent call last):
  File "/Users/adam/Envs/swiftwind/lib/python3.4/site-packages/django/core/handlers/base.py", line 132, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/adam/Envs/swiftwind/lib/python3.4/site-packages/django/contrib/auth/decorators.py", line 22, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/Users/adam/Envs/swiftwind/lib/python3.4/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/adam/Envs/swiftwind/lib/python3.4/site-packages/django/views/generic/base.py", line 65, in view
    self = cls(**initkwargs)
  File "/Users/adam/Envs/swiftwind/src/graphene/graphene/contrib/django/views.py", line 10, in __init__
    schema=schema.schema,
  File "/Users/adam/Envs/swiftwind/src/graphene/graphene/core/schema.py", line 77, in schema
    subscription=self.T(self.subscription))
  File "/Users/adam/Envs/swiftwind/src/graphene/graphene/core/schema.py", line 21, in __init__
    super(GraphQLSchema, self).__init__(*args, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'subscription'

Help with Mutation

Hello, I am trying to experiment with a simple mutation, here is my schema:

import graphene


class Person(graphene.ObjectType):

    name = graphene.String()
    age = graphene.Float()
    id = graphene.Int()

# Database of Person(s)
p1 = Person()
p2 = Person()

p1.id = 1
p1.age = 34
p1.name = 'Jack'

p2.id = 2
p2.age = 31
p2.name = 'Adam'

persons = [p1, p2]

class Query(graphene.ObjectType):

    # Create a field on which we can query and the attributes
    # we allow to query on
    person = graphene.Field(Person, id=graphene.Int())
    def resolve_person(self, args, info):
        for p in persons:
            if p.id == args.get('id'):
                return p

class UpdatePerson(graphene.Mutation):
    # Result field
    # XXX: This doesn't work
    # person = graphene.Field(Person)

    # This does work
    person = graphene.String()

    # The input fields
    class Input:
        name = graphene.String()
        id = graphene.Int()

    @classmethod
    def mutate(cls, instance, args, info):
        name = args.get('name')
        for p in persons:
            if p.id == args.get('id'):
                p.name = name
                return UpdatePerson(person=p.name)

class UpdatePersonMutation(graphene.ObjectType):
    updatePerson = graphene.Field(UpdatePerson)

schema = graphene.Schema(query=Query, mutation=UpdatePersonMutation)

In UpdatePerson above, when I have the person field as person = graphene.Field(Person), ( and correspondingly return UpdatePerson(person=p)), upon sending a mutation query, I get: [GraphQLError('Field "person" of type "Person" must have a sub selection.',)].

Any suggestions as to what I am doing wrong?

ObjectType.resolve_type classmethod makes it impossible to have a field named 'type'

The resolve_type method on ObjectType conflicts with actually resolving a field called type. Given the importance of resolve method names, I think great care should be taken to make sure graphene's codebase does not use these method names unless necessary.

The below test fails with GraphQLError("'Location' object has no attribute 'T'",); renaming the resolve_type method to _resolve_type solves the problem.

import graphene
from graphql.core.type import GraphQLEnumValue


LOCATION_TYPES = {
    'HQ': "Headquarters",
    'BRANCH': "Branch Location",
}


LOCATIONS = {
    '1': {
        'type': 'HQ',
        'address': '123 Main St',
        'city': 'Metropolis',
    },
    '2': {
        'type': 'BRANCH',
        'address': '55 Side St',
        'city': 'Smallville',
    },
}


LocationType = graphene.Enum(
    'LocationType',
    {k: GraphQLEnumValue(value=k, description=v) for k, v in LOCATION_TYPES.items()}
)


class Location(graphene.ObjectType):
    type = graphene.NonNull(LocationType)
    address = graphene.NonNull(graphene.String())
    city = graphene.NonNull(graphene.String())


class Query(graphene.ObjectType):
    location = graphene.Field(Location, id=graphene.ID(description='location id'))

    def resolve_location(self, args, info):
        loc_id = args.get('id')
        # error checking ommitted for brevity
        return Location(**LOCATIONS[loc_id])


schema = graphene.Schema(name="Sample", query=Query)

EXPECTED_SCHEMA = """
type Location {
  type: LocationType!
  address: String!
  city: String!
}

enum LocationType {
  BRANCH
  HQ
}

type Query {
  location(id: ID): Location
}
""".lstrip()

QUERY = """
fragment locFields on Location {
  type
  address
  city
}

query SomeQuery {
  hq: location(id: 1) {
    ...locFields
  }
  branch: location(id: 2) {
    ...locFields
  }

}
""".lstrip()

EXPECTED_RESPONSE = {
    'hq': {
        'type': 'HQ',
        'address': '123 Main St',
        'city': 'Metropolis'
    },
    'branch': {
        'type': 'BRANCH',
        'address': '55 Side St',
        'city': 'Smallville'
    },
}


def test_type():
    assert str(schema) == EXPECTED_SCHEMA
    result = schema.execute(QUERY)
    assert not result.errors
    assert result.data == EXPECTED_RESPONSE

Incorrect mutate document

This page has a mutate example, but parameters of the mutate method are incorrect, follow it will get an error: "mutate() takes exactly 3 arguments (4 given)"

According to to this test, it should be like:

    @classmethod
    def mutate(cls, instance, args, info):
        person = Person(name=args.get('name'))
        ok = True
        return CreatePerson(person=person, ok=ok)

Errors upon execution

I see that when a query is executed via the execute() method, the result object has errors - a list of errors. My questions are:

  • Why do we have a list of errors?
  • If we must have a list of errors, is there any recommended way to report these to a HTTP client? a list of serialized messages?

Need to have resolution "helpers" (including in get_node)

In my preferred SQLAlchemy setup, simply having the model class is not sufficient to load a model. In addition, you must have a separate session object, which has a query method.

For example, the Django approach of :

ship = Ship.objects.get(id=1)

would instead work like this:

ship = session.query(Ship).filter_by(id=1).one()

I also require the ability to pass a notion of the current user into the query so that my resolvers can verify the user is authorized to access this data.

This means the 'session' and 'auth' objects must be provided to the schema.execute method and get passed into any resolvers. I had planned to do this using the ExecutionContext. However, the get_node classmethods don't get provided the ExecutionContext.

What do you suggest?

Queries do not support multiple inheritance

Use Case: splitting out query definition logic into smaller and more manageable files (e.g. in Django app directories)

Something like this will not currently work:

schema = graphene.Schema(name='Swiftwind Schema')


class Query1(ObjectType):
    all_accounts = relay.ConnectionField(accounts_nodes.Account)
    account = relay.NodeField(accounts_nodes.Account)


class Query2(ObjectType):
    all_transactions = relay.ConnectionField(accounts_nodes.Transaction)
    transaction = relay.NodeField(accounts_nodes.Transaction)


class Query3(ObjectType):
    all_users = relay.ConnectionField(accounts_nodes.User)
    resolve_all_users = ConnectionResolver(accounts_nodes.User)


class Query(Query1, Query2, Query3):
    pass


schema.query = Query

It dies with the following error:

  File "/Users/adam/Envs/swiftwind/src/graphql-core/graphql/core/type/schema.py", line 30, in __init__
    assert isinstance(query, GraphQLObjectType), 'Schema query must be Object Type but got: {}.'.format(query)

I have also tried:

schema = graphene.Schema(name='Swiftwind Schema')


class Query1(object):
    all_accounts = relay.ConnectionField(accounts_nodes.Account)
    account = relay.NodeField(accounts_nodes.Account)


class Query2(object):
    all_transactions = relay.ConnectionField(accounts_nodes.Transaction)
    transaction = relay.NodeField(accounts_nodes.Transaction)


class Query3(object):
    all_users = relay.ConnectionField(accounts_nodes.User)
    resolve_all_users = ConnectionResolver(accounts_nodes.User)


class Query(Query1, Query2, Query3, ObjectType):
    pass


schema.query = Query

Which dies with the error:

  File "/Users/adam/Envs/swiftwind/src/graphql-core/graphql/core/type/definition.py", line 211, in define_field_map
    ).format(type)
AssertionError: Query fields must be a mapping (dict / OrderedDict) with field names as keys or a function which returns such a mapping.

Presumably because the fields never get initialised?

Anyway, if this is possible I'd love to know how.

Document reserved characters in argument names

I tried to run the following:

import graphene


class Query(graphene.ObjectType):
    hello = graphene.String(firstName=graphene.String())

    def resolve_hello(self, args, info):
        return 'Hello ' + args.get('firstName')

schema = graphene.Schema(query=Query)

result = schema.execute("""
query foo($firstName:String)
{
        hello(firstName:$firstName)
}
""", args={"firstName": "Serkan"})

print result.data

It works. However, if I change all references to firstName to name, it stops working. Is this documented, or is it a bug?

import graphene


class Query(graphene.ObjectType):
    hello = graphene.String(name=graphene.String())

    def resolve_hello(self, args, info):
        return 'Hello ' + args.get('name')

schema = graphene.Schema(query=Query)

result = schema.execute("""
query foo($name:String)
{
        hello(name:$name)
}
""", args={"name": "Serkan"})

print result.data

The stack trace is the following:

  File "test.py", line 18, in <module>
    """, args={"name": "Serkan"})
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphene/core/schema.py", line 122, in execute
    kwargs = dict(kwargs, request=request, root=root, args=args, schema=self.schema)
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphene/core/schema.py", line 85, in schema
    subscription=self.T(self.subscription))
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphene/core/schema.py", line 22, in __init__
    super(GraphQLSchema, self).__init__(*args, **kwargs)
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphql/core/type/schema.py", line 42, in __init__
    self._type_map = self._build_type_map()
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphql/core/type/schema.py", line 85, in _build_type_map
    type_map = type_map_reducer(type_map, type)
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphql/core/type/schema.py", line 117, in type_map_reducer
    field_map = type.get_fields()
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphql/core/type/definition.py", line 193, in get_fields
    self._field_map = define_field_map(self, self._fields)
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphql/core/type/definition.py", line 218, in define_field_map
    assert_valid_name(field_name)
  File "/Users/ustun/Projects/graphene_test/.venv/lib/python2.7/site-packages/graphql/core/type/definition.py", line 758, in assert_valid_name
    assert COMPILED_NAME_PATTERN.match(name), 'Names must match /{}/ but "{}" does not.'.format(NAME_PATTERN, name)
TypeError: expected string or buffer

Query does not support snake case query names

When I create a query that has a snake case name and attempt to execute it, I get the following error:

[graphql.core.error.GraphQLError('Cannot query field "work_order" on "Query".')]

Here is what my query looks like:

 class Query(ObjectType):
    work_order = Field(WorkOrder, id=graphene.Argument(graphene.String))

    @resolve_only_args
    def resolve_work_order(self, id):
        return WorkOrder(id='1', state='test')

Here is what my GraphQL query looks like:

query = """
query test {
    work_order(id: "1") {
        state
    }
}
"""

if I change the name of the query to workorder instead of work_order, or workOrder it works just fine. Is this intended behavior?

Add Documentation for Relay Edge implementation

In the Relay mutation docs, performing RANGE_ADD requires that the new edge created by the mutation is provided in the payload.

I do not know how to provide the correct cursor/Edge construction within the relay.ClientIDMutation subclass.

The following code I wrote:

new_object_edge = graphene.Field('ObjectEdge')

I believed would solve my issue, with a proper resolve_new_object_edge(...) resolver.

However, my resolver cannot reference the relay.ConnectionField that is specified on one of the other object types in my mutation, since it only has the context of my (in this case, Django) objects.

I experimented with relay.Edge.for_node(SomeObjectType) as well but all of the solutions I have tried so far modify the schema correctly but cannot return the appropriate Edge.

The implementation of this scenario in javascript can be found here in the Relay examples directory.

Any idea of a best approach? I can take this question to stack overflow, but felt that people using Relay & Graphene would enjoy seeing an example solution in the docs.

Django integration proposal

I've been thinking a bit about Django integration for Graphene. This is in part because I think it could be really useful, and in part because this is something I'd be interested in contributing a pull request for. However, development seems to be moving quickly so I thought it best to discuss these ideas first in case there are already plans.

The main motivation here is reducing boilerplate code. My intention would be to make all automatic functionality overridable & customisable.

Nodes:

  • Automatically traverse Django relations. A resolve_FIELD() may be specified to customise this traversal, but if not then traversal will be done using the relation's manager's all() method. [already supported]
  • Automatically support query args for all/specified model fields (reduces resolve_FIELD() boilerplate code).

Query:

  • Automatically resolve ConnectionFields which connect to DjangoNodes without the need for a resolve_FIELD() method.
  • Support for pulling in schema definitions from individual Django apps, rather than being defined centrally. (I currently do this by using a top-level Query class which inherits from multiple app-specific Query classes) [related: #55]

Mutations:

  • Provide a DjangoMutation which provides standard functionality though which to update Django models.
  • Consider a class-based views approach, with CreateMutation, UpdateMutation, and DeleteMutation base classes.

All/any thoughts very welcome indeed!

django GraphQLView isn't implemented properly

https://github.com/graphql-python/graphene/blob/master/graphene/contrib/django/views.py

Isn't implemented properly. Doesn't do proper error handling & doesn't handle GraphQL variables at all.

I was thinking instead we could remove this from graphene, and make this into it's own standalone package that could be given a GraphQL Schema and create a Django View for it.

That way it could be used with bare graphql-core, graphql-epoxy and graphene.

Thoughts?

Django relations not traversed

Perhaps this is a known limitation, or perhaps I'm doing it wrong.

My I have the following (simplified) django models:

class Account(TimeStampedModel, models.Model):
    name = models.CharField(max_length=100)
    ... more fields ...

class Transaction(TimeStampedModel, models.Model):
    from_account = models.ForeignKey(Account, related_name='transactions_out')
    ... more fields ...

Desire: I want to traverse the Account.transactions_out reverse relationship.

My Query & Nodes look like this:

class Query(ObjectType):
    all_accounts = relay.ConnectionField(nodes.Account, description='All the accounts', args={
        'name': graphene.String(),
    })
    all_transactions = relay.ConnectionField(nodes.Transaction, description='All the transactions')

    account = relay.NodeField(nodes.Account)
    transaction = relay.NodeField(nodes.Transaction)

    @resolve_only_args
    def resolve_all_accounts(self, **kwargs):
        returns ...all accounts filtering for name...

    @resolve_only_args
    def resolve_all_transactions(self, **kwargs):
        returns ...all transactions...


class Account(DjangoNode):
    class Meta:
        model = models.Account

# Transaction node omitted , but is exactly the same form as the Account node

When I run the following query:

query {
  allAccounts(name: "adamcharnock") {
    edges {
      node {
        id,
        name,
        transactionsOut {
          edges {
            node {
              amount
}}}}}}}

The result comes back with "transactionsOut": null, even though transactions do exist.

Solution: After some debugging I've found the following change will produce results:

class Account(DjangoNode):
    class Meta:
        model = models.Account

    @classmethod
    def resolve_transactionsOut(cls, instance, args, attrs):
        return instance.instance.transactions_out.all()

Seems odd because: It seems like the Graphene's Django support does not:

  1. Convert transactionsOut -> transactions_out
  2. Probably more importantly, it does not navigate the relationship automatically.

However – and as always – it is quite possible I'm doing this wrong. I'm also aware this project is a work in progress. Also, if GitHub issues are not the correct place for this I'm happy to move it elsewhere.

models.UUID not supported for Django versions < 1.8

I'm currently running Django 1.6.5, and ran into an error while trying to run graphene:

File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  88.                 response = middleware_method(request)
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/django/middleware/common.py" in process_request
  71.             if (not urlresolvers.is_valid_path(request.path_info, urlconf) and
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/django/core/urlresolvers.py" in is_valid_path
  596.         resolve(path, urlconf)
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/django/core/urlresolvers.py" in resolve
  476.     return get_resolver(urlconf).resolve(path)
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/django/core/urlresolvers.py" in resolve
  337.             for pattern in self.url_patterns:
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/django/core/urlresolvers.py" in url_patterns
  365.         patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/django/core/urlresolvers.py" in urlconf_module
  360.             self._urlconf_module = import_module(self.urlconf_name)
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/django/utils/importlib.py" in import_module
  40.         __import__(name)
File "/Users/mickiebetz/Code/website/site/src/atlas/urls.py" in <module>
  8. from graphene.contrib.django.views import GraphQLView
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/graphene/contrib/django/__init__.py" in <module>
  1. from graphene.contrib.django.types import (
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/graphene/contrib/django/types.py" in <module>
  6. from graphene.contrib.django.converter import convert_django_field
File "/Users/mickiebetz/Code/website/lib/python2.7/site-packages/graphene/contrib/django/converter.py" in <module>
  27. @convert_django_field.register(models.UUIDField)


Exception Type: AttributeError at /graphiql
Exception Value: 'module' object has no attribute 'UUIDField'

The Django UUID model field was added in Django 1.8, yet the setup.py script notes support for Django versions 1.6 and above.

Would it be possible to remove that field so Graphene is compatible with those versions of Django (1.6 and higher)?

Issue with resolve_*() functions

Consider this example (adapted from simple example in getting started):

amit@yosemite work $ cat hello_graphql.py
import graphene

class Query(graphene.ObjectType):
    hello_id = graphene.String()

    def resolve_hello_id(self, args, info):
        print 'Resolve called'
        return 'World'

schema = graphene.Schema(query=Query)
schema.execute('{ hello_id }')

On executing it, resolve function is not called. I have seen this is a generic issue when the field name is *_id.

Resolver Tagging

graphql-core supports "tagging" a resolver (via core.execution.middlewares.utils) as a way to signal special handling of the resolver in an ExecutionMiddleware.

The main example is that we have GeventExecutionMiddleware. By default, this middleware does nothing when processing resolvers. But when it is given a resolver that is tagged with (@run_in_greenlet) it will spawn a greenlet to execute the resolver, and allow the executor to continue to resolve other fields concurrently.

From the tests, here's a good example (not using Graphene):

@run_in_greenlet
def resolve_a(context, *_):
    gevent.sleep(3)
    return 'resolved a'

@run_in_greenlet
def resolve_b(context, *_):
    gevent.sleep(3)
    return 'resolved b'

Type = GraphQLObjectType('Type', {
    'a': GraphQLField(GraphQLString, resolver=resolver),
    'b': GraphQLField(GraphQLString, resolver=resolver_2)
})

executor = Executor(GraphQLSchema(Type), [GeventExecutionMiddleware()])

doc = 'query Example { a, b }'
result = executor.execute(doc)
assert result.data == {'a': 'resolved a', 'b': 'resolved b'}

In this example, if each function was not tagged with @run_in_greenlet, then the execution time would be 6 seconds, as they have to run serially. However, since they are tagged, the middleware will run them concurrently, executing the query in only 3 seconds.

Refactor Graphene Class based Types

As the graphene syntax is now stable, the next focus should be internal refactor.
All class based types (ObjectType, Interface, Mutation, Union, Scalar) should have only the ClassType in common (which assure the new type have a name and a description), but each type should customize the way or the attrs the options can have.

Right now a lot of logic is repeated for handling all this cases under one, divide this logic and use when necessary.

`tox` tests not working for me

Tried to run tox but the tests wouldn't load!

(graphene)~/P/graphene git:master ❯❯❯ tox                                                                                                                                                               ✱ ◼
GLOB sdist-make: /Users/jake/PycharmProjects/graphene/setup.py
py27 inst-nodeps: /Users/jake/PycharmProjects/graphene/.tox/dist/graphene-0.1.4.zip
py27 installed: blinker==1.4,Django==1.8.5,flake8==2.4.1,graphene==0.1.4,graphql-core==0.1a3,graphql-relay==0.2.0,mccabe==0.3.1,pep8==1.5.7,py==1.4.30,pyflakes==0.8.1,pytest==2.8.2,pytest-django==2.9.1,singledispatch==3.4.0.3,six==1.10.0
py27 runtests: PYTHONHASHSEED='3684720222'
py27 runtests: commands[0] | py.test
Traceback (most recent call last):
  File ".tox/py27/bin/py.test", line 11, in <module>
    sys.exit(main())
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/config.py", line 38, in main
    config = _prepareconfig(args, plugins)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/config.py", line 117, in _prepareconfig
    pluginmanager=pluginmanager, args=args)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 724, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
    _MultiCall(methods, kwargs, hook.spec_opts).execute()
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 595, in execute
    return _wrapped_call(hook_impl.function(*args), self.execute)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 249, in _wrapped_call
    wrap_controller.send(call_outcome)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/helpconfig.py", line 28, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 279, in get_result
    _reraise(*ex)  # noqa
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 264, in __init__
    self.result = func()
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 596, in execute
    res = hook_impl.function(*args)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/config.py", line 852, in pytest_cmdline_parse
    self.parse(args)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/config.py", line 957, in parse
    self._preparse(args)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/config.py", line 928, in _preparse
    args=args, parser=self._parser)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 724, in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
    _MultiCall(methods, kwargs, hook.spec_opts).execute()
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 595, in execute
    return _wrapped_call(hook_impl.function(*args), self.execute)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 253, in _wrapped_call
    return call_outcome.get_result()
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 279, in get_result
    _reraise(*ex)  # noqa
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 264, in __init__
    self.result = func()
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 596, in execute
    res = hook_impl.function(*args)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/pytest_django/plugin.py", line 236, in pytest_load_initial_conftests
    settings.DATABASES
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 35, in __exit__
    self.gen.throw(type, value, traceback)
  File "/Users/jake/PycharmProjects/graphene/.tox/py27/lib/python2.7/site-packages/pytest_django/plugin.py", line 105, in _handle_import_error
    raise ImportError(msg)
ImportError: No module named tests.django_settings

pytest-django could not find a Django project (no manage.py file could be found). You must explicitly add your Django project to the Python path to have it picked up.
ERROR: InvocationError: '/Users/jake/PycharmProjects/graphene/.tox/py27/bin/py.test'

Snake case to camel case

I didn't find a forced conversion to camelCase in the spec. Did I miss it? Or is graphene just opinionated on style standards. Will there be any way to opt out of this in the future?

GenericIPAddressField support

It has been in Django since 1.4 and can be converted to String. I can submit a PR but I don't know how you want it. Can it be done the same way as UUIDField?

Graphene as a validation library

This is probably not the best place to discuss this as it is not a bug report, but I was wondering how we should approach data validation with graphql and graphene, especially with its Django integration.

For example, should graphene replace the django forms layer as well? I believe it should and it can, since its validation capabilities are much richer. It is possible to validate if something is a string in django, but going beyond that, for example, ensuring a value is a list of strings is not possible without custom code.

We should discuss and document the best practices here.

I see graphene as ultimately replacing the urls, views and forms parts of django.

Sharing result of one field as an input to another

I've modified the toy example on the graphene playground to express the kind of thing I would like to be able to do. I want to have fields which depend on the output of other fields (on that object as well as potentially other connected objects). Right now I can make get the value for fields on a particular class by calling resolve_*() methods from within other resolve methods, but the result is not kept and so duplicate computation is done. Is there a different way to do this that doesn't result in duplicate computation? Is there a way to do this across object boundaries? For example defining a field which depends on summing the values of a field of an object in a has_many relation?

http://graphene-python.org/playground/?schema=import%2520graphene%250A%250Aclass%2520Query(graphene.ObjectType)%253A%250A%2520%2520%2520%2520hello%2520%253D%2520graphene.String()%250A%2520%2520%2520%2520ping%2520%253D%2520graphene.String(to%253Dgraphene.String())%250A%250A%2520%2520%2520%2520def%2520resolve_hello(self%252C%2520args%252C%2520info)%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%27World%2520%257B%257D%27.format(id(self))%250A%250A%2520%2520%2520%2520def%2520resolve_ping(self%252C%2520args%252C%2520info)%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520h%2520%253D%2520self.resolve_hello(args%252C%2520info)%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%27Pinging%2520%257B%257D%2520%253A%2520%257B%257D%27.format(args.get(%27to%27)%252C%2520h)%250A%250Aschema%2520%253D%2520graphene.Schema(query%253DQuery)%250A&query=query%2520%257B%250A%2520%2520hello%250A%2520%2520ping(to%253A%2522Peter%2522)%250A%257D%250A

Executor Customization

I think that it would be beneficial to be able to pass a custom executor to a Graphene schema, so that it can use it to execute the query.

I'm thinking either:

schema = Schema(query=QueryRootType, executor=Executor(...))

OR

schema = Schema(query=QueryRootType)
schema.set_executor(Executor(...))

By default it should use the default executor instance. I am going to expose an API for this in core.graphql.execution

from core.graphql import execution
default_executor = execution.get_default_executor()

The default executor will be configured to be a synchronous executor. In addition, you will be able to set the default executor using:

execution.set_default_executor(Executor(...))

Additional attributes on custom edge_type

For a ConnectionField we can pass in a a custom edge as so: addresses=relay.ConnectionField(Address, connection_type=AddressType, edge_type=AddressTypeEdge). But when the custom edge has some additional fields it seems there is no way for these to be set, as this line: https://github.com/graphql-python/graphql-relay-py/blob/master/graphql_relay/connection/arrayconnection.py#L46 only ever tries to set node and cursor. It would be great to have support to resolve some custom fields on a custom edge.

I might look into a fix myself tomorrow since I need that for one of our implementations.

Improvements next stable package

Inspired by other GraphQL implementations, next version should be able to:

  • Simplify Fields definition (rename graphene.{type}Field to graphene.{type}).
  • Add Types dynamic instantiation into ObjectTypes, Interfaces (as field), or in Field kwargs (as Arguments)
  • Add .List , .NonNull to Types
  • Add Union Type
  • Add Scalar Type (for having, for example a DateTime Scalar type)

How do you introspect the schema?

I see that the graphiql interface is able to introspect the schema, but I am not sure how you would export the schema form python. I see the introspect function on the Schema object, but that simply returns the objects from the type_map.

Example for InputObject type

It seems that there is support for the InputObject type defined in the spec, however, none of the examples cover its usage as an argument. Is this supported? If so, is it possible to add an example?

Get requested fields in resolve function

I have seen that the info parameter in the resolve function provides a info.field_asts, which provides information about the selected fields, but when fragments are provided you get something like:

[Field(alias=None, name=Name(value=u'customer'), arguments=[], directives=[], selection_set=SelectionSet(selections=[Field(alias=None, name=Name(value=u'id'), arguments=[], directives=[], selection_set=None), FragmentSpread(name=Name(value=u'__RelayQueryFragment0wau8gf'), directives=[])]))]

which means for fragments we can't really figure out which fields are selected at runtime.

Our use-case for knowing the fields in the resolve functions is,that we only want to calculate the fields that are actually requested because some of the fields are expensive to calculate.

Edit: Or are the resolve methods for specific fields meant to be used for that? E.g. resolve_full_name on the Customer node?

Also happy to provide an example of that would make it easier.

Arguments on individual Node fields

It would be great to have a test/example that shows how args on e.g. a StringField work. Right now I am getting an error AssertionError: Client.name(test:) argument type must be Input Type but got: ....

I have tried to use InputObjectType but there are very few tests on that and very little documentation (in the source code)

Also, now that I am starting to understand graphql, relay and graphene better I would be more than up to add some more tests or documentation if that would help at all.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.