Improved python syntax and sorts (pep8). Improved Readme

This commit is contained in:
Syrus Akbary 2016-09-17 17:09:56 -07:00
parent 0434899b4e
commit 4e23c3ccf6
27 changed files with 132 additions and 69 deletions

2
.coveragerc Normal file
View File

@ -0,0 +1,2 @@
[run]
omit = */tests/*,graphene_django/debug/sql/*

View File

@ -3,20 +3,10 @@ Please read [UPGRADE-v1.0.md](https://github.com/graphql-python/graphene/blob/ma
--- ---
# ![Graphene Logo](http://graphene-python.org/favicon.png) [Graphene-Django](http://graphene-python.org) [![Build Status](https://travis-ci.org/graphql-python/graphene-django.svg?branch=master)](https://travis-ci.org/graphql-python/graphene-django) [![PyPI version](https://badge.fury.io/py/graphene-django.svg)](https://badge.fury.io/py/graphene-django) [![Coverage Status](https://coveralls.io/repos/graphql-python/graphene-django/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene-django?branch=master) # ![Graphene Logo](http://graphene-python.org/favicon.png) Graphene-Django [![Build Status](https://travis-ci.org/graphql-python/graphene-django.svg?branch=master)](https://travis-ci.org/graphql-python/graphene-django) [![PyPI version](https://badge.fury.io/py/graphene-django.svg)](https://badge.fury.io/py/graphene-django) [![Coverage Status](https://coveralls.io/repos/graphql-python/graphene-django/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene-django?branch=master)
[Graphene](http://graphene-python.org) is a Python library for building GraphQL schemas/types fast and easily. A [Django](https://www.djangoproject.com/) integration for [Graphene](http://graphene-python.org/).
- **Easy to use:** Graphene helps you use GraphQL in Python without effort.
- **Relay:** Graphene has builtin support for Relay
- **Django:** Automatic *Django model* mapping to Graphene Types. Check a fully working [Django](http://github.com/graphql-python/swapi-graphene) implementation
Graphene also supports *SQLAlchemy*!
*What is supported in this Python version?* **Everything**: Interfaces, ObjectTypes, Scalars, Unions and Relay (Nodes, Connections), in addition to queries, mutations and subscriptions.
**NEW**!: [Try graphene online](http://graphene-python.org/playground/)
## Installation ## Installation
@ -28,30 +18,50 @@ pip install "graphene-django>=1.0.dev"
## Examples ## Examples
Here is one example for get you started: Here is a simple Django model:
```python ```python
from django.db import models from django.db import models
from graphene_django import DjangoObjectType
class UserModel(models.Model): class UserModel(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100)
```
To create a GraphQL schema for it you simply have to write the following:
```python
from graphene_django import DjangoObjectType
class User(DjangoObjectType): class User(DjangoObjectType):
class Meta: class Meta:
# This type will transform all the UserModel fields
# into Graphene fields automatically
model = UserModel model = UserModel
# An extra field in the User Type class Query(graphene.ObjectType):
full_name = graphene.String() users = graphene.List(User)
def resolve_full_name(self, args, context, info): @graphene.resolve_only_args
return "{} {}".format(self.name, self.last_name) def resolve_users(self):
return UserModel.objects.all()
schema = graphene.Schema(query=QueryRoot)
``` ```
If you want to learn even more, you can also check the following [examples](examples/): Then you can simply query the schema:
```python
query = '''
query {
users {
name,
lastName
}
}
'''
result = schema.execute(query)
```
To learn more check out the following [examples](examples/):
* **Schema with Filtering**: [Cookbook example](examples/cookbook) * **Schema with Filtering**: [Cookbook example](examples/cookbook)
* **Relay Schema**: [Starwars Relay example](examples/starwars) * **Relay Schema**: [Starwars Relay example](examples/starwars)

View File

@ -1,4 +1,6 @@
import sys, os import sys
import os
ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, ROOT_PATH + '/examples/') sys.path.insert(0, ROOT_PATH + '/examples/')

View File

@ -1,5 +1,5 @@
from cookbook.ingredients.models import Category, Ingredient from cookbook.ingredients.models import Category, Ingredient
from graphene import ObjectType, Field, AbstractType, Node from graphene import AbstractType, Field, Node
from graphene_django.filter import DjangoFilterConnectionField from graphene_django.filter import DjangoFilterConnectionField
from graphene_django.types import DjangoObjectType from graphene_django.types import DjangoObjectType

View File

@ -1,8 +1,10 @@
import graphene
import cookbook.ingredients.schema import cookbook.ingredients.schema
import graphene
# print cookbook.ingredients.schema.Query._meta.graphql_type.get_fields()['allIngredients'].args # print cookbook.ingredients.schema.Query._meta.graphql_type.get_fields()['allIngredients'].args
class Query(cookbook.ingredients.schema.Query, graphene.ObjectType): class Query(cookbook.ingredients.schema.Query, graphene.ObjectType):
pass pass

View File

@ -1,14 +1,12 @@
import graphene import graphene
from graphene import relay, resolve_only_args, Schema from graphene import Schema, relay, resolve_only_args
from graphene_django import DjangoObjectType from graphene_django import DjangoObjectType
from .data import (create_ship, get_empire, get_faction, get_rebels, get_ship, from .data import (create_ship, get_empire, get_faction, get_rebels, get_ship,
get_ships) get_ships)
from .models import ( from .models import Character as CharacterModel
Character as CharacterModel, from .models import Faction as FactionModel
Faction as FactionModel, from .models import Ship as ShipModel
Ship as ShipModel
)
class Ship(DjangoObjectType): class Ship(DjangoObjectType):

View File

@ -1,16 +1,17 @@
from django.db import models from django.db import models
from django.utils.encoding import force_text from django.utils.encoding import force_text
from graphene import Enum, List, ID, Boolean, Float, Int, String, Field, NonNull, Field, Dynamic from graphene import (ID, Boolean, Dynamic, Enum, Field, Float, Int, List,
from graphene.types.json import JSONString NonNull, String)
from graphene.types.datetime import DateTime
from graphene.utils.str_converters import to_const
from graphene.relay import is_node from graphene.relay import is_node
from graphene.types.datetime import DateTime
from graphene.types.json import JSONString
from graphene.utils.str_converters import to_const
from .compat import (ArrayField, HStoreField, JSONField, RangeField, from .compat import (ArrayField, HStoreField, JSONField, RangeField,
RelatedObject, UUIDField) RelatedObject, UUIDField)
from .utils import get_related_model, import_single_dispatch
from .fields import get_connection_field from .fields import get_connection_field
from .utils import get_related_model, import_single_dispatch
singledispatch = import_single_dispatch() singledispatch = import_single_dispatch()
@ -37,9 +38,10 @@ def convert_django_field_with_choices(field, registry=None):
name = '{}{}'.format(meta.object_name, field.name.capitalize()) name = '{}{}'.format(meta.object_name, field.name.capitalize())
choices = list(get_choices(choices)) choices = list(get_choices(choices))
named_choices = [(c[0], c[1]) for c in choices] named_choices = [(c[0], c[1]) for c in choices]
named_choices_descriptions = {c[0]:c[2] for c in choices} named_choices_descriptions = {c[0]: c[2] for c in choices}
class EnumWithDescriptionsType(object): class EnumWithDescriptionsType(object):
@property @property
def description(self): def description(self):
return named_choices_descriptions[self.name] return named_choices_descriptions[self.name]

View File

@ -1,6 +1,7 @@
from promise import Promise
from django.db import connections from django.db import connections
from promise import Promise
from .sql.tracking import unwrap_cursor, wrap_cursor from .sql.tracking import unwrap_cursor, wrap_cursor
from .types import DjangoDebug from .types import DjangoDebug

View File

@ -1,4 +1,5 @@
from graphene import ObjectType, List from graphene import List, ObjectType
from .sql.types import DjangoDebugSQL from .sql.types import DjangoDebugSQL

View File

@ -1,9 +1,11 @@
from functools import partial from functools import partial
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from graphene.relay import ConnectionField, PageInfo from graphene.relay import ConnectionField, PageInfo
from graphql_relay.connection.arrayconnection import connection_from_list_slice from graphql_relay.connection.arrayconnection import connection_from_list_slice
from .utils import maybe_queryset, DJANGO_FILTER_INSTALLED
from .utils import DJANGO_FILTER_INSTALLED, maybe_queryset
class DjangoConnectionField(ConnectionField): class DjangoConnectionField(ConnectionField):

View File

@ -1,4 +1,5 @@
from functools import partial from functools import partial
from ..fields import DjangoConnectionField from ..fields import DjangoConnectionField
from .utils import get_filtering_args_from_filterset, get_filterset_class from .utils import get_filtering_args_from_filterset, get_filterset_class

View File

@ -5,9 +5,10 @@ from django.utils.text import capfirst
from django_filters import Filter, MultipleChoiceFilter from django_filters import Filter, MultipleChoiceFilter
from django_filters.filterset import FilterSet, FilterSetMetaclass from django_filters.filterset import FilterSet, FilterSetMetaclass
from ..forms import GlobalIDFormField, GlobalIDMultipleChoiceField
from graphql_relay.node.node import from_global_id from graphql_relay.node.node import from_global_id
from ..forms import GlobalIDFormField, GlobalIDMultipleChoiceField
class GlobalIDFilter(Filter): class GlobalIDFilter(Filter):
field_class = GlobalIDFormField field_class = GlobalIDFormField

View File

@ -2,7 +2,7 @@ from datetime import datetime
import pytest import pytest
from graphene import ObjectType, Schema, Field from graphene import Field, ObjectType, Schema
from graphene.relay import Node from graphene.relay import Node
from graphene_django import DjangoObjectType from graphene_django import DjangoObjectType
from graphene_django.forms import (GlobalIDFormField, from graphene_django.forms import (GlobalIDFormField,

View File

@ -1,6 +1,7 @@
import six import six
from graphene import Argument, String from graphene import String
from .filterset import custom_filterset_factory, setup_filterset from .filterset import custom_filterset_factory, setup_filterset

View File

@ -1,7 +1,8 @@
from django import forms from django import forms
from django.forms.fields import BaseTemporalField from django.forms.fields import BaseTemporalField
from graphene import ID, Boolean, Float, Int, String, List from graphene import ID, Boolean, Float, Int, List, String
from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField
from .utils import import_single_dispatch from .utils import import_single_dispatch

View File

@ -67,6 +67,6 @@ class Command(CommandArguments):
self.save_file(out, schema_dict) self.save_file(out, schema_dict)
style = getattr(self, 'style', None) style = getattr(self, 'style', None)
SUCCESS = getattr(style, 'SUCCESS', lambda x: x) success = getattr(style, 'SUCCESS', lambda x: x)
self.stdout.write(SUCCESS('Successfully dumped GraphQL schema to %s' % out)) self.stdout.write(success('Successfully dumped GraphQL schema to %s' % out))

View File

@ -1,13 +1,18 @@
class Registry(object): class Registry(object):
def __init__(self): def __init__(self):
self._registry = {} self._registry = {}
self._registry_models = {} self._registry_models = {}
def register(self, cls): def register(self, cls):
from .types import DjangoObjectType from .types import DjangoObjectType
assert issubclass(cls, DjangoObjectType), 'Only DjangoObjectTypes can be registered, received "{}"'.format(cls.__name__) assert issubclass(
cls, DjangoObjectType), 'Only DjangoObjectTypes can be registered, received "{}"'.format(
cls.__name__)
assert cls._meta.registry == self, 'Registry for a Model have to match.' assert cls._meta.registry == self, 'Registry for a Model have to match.'
# assert self.get_type_for_model(cls._meta.model) == cls, 'Multiple DjangoObjectTypes registered for "{}"'.format(cls._meta.model) # assert self.get_type_for_model(cls._meta.model) == cls, (
# 'Multiple DjangoObjectTypes registered for "{}"'.format(cls._meta.model)
# )
self._registry[cls._meta.model] = cls self._registry[cls._meta.model] = cls
def get_type_for_model(self, model): def get_type_for_model(self, model):

View File

@ -1,7 +1,7 @@
import graphene import graphene
from graphene import Schema, relay from graphene import Schema, relay
from ..types import DjangoObjectType
from ..types import DjangoObjectType
from .models import Article, Reporter from .models import Article, Reporter

View File

@ -4,17 +4,20 @@ from django.utils.translation import ugettext_lazy as _
from py.test import raises from py.test import raises
import graphene import graphene
from graphene.relay import Node, ConnectionField from graphene.relay import ConnectionField, Node
from graphene.types.datetime import DateTime from graphene.types.datetime import DateTime
from graphene.types.json import JSONString from graphene.types.json import JSONString
# from graphene.core.types.custom_scalars import DateTime, JSONString
from ..compat import (ArrayField, HStoreField, JSONField, MissingType, from ..compat import (ArrayField, HStoreField, JSONField, MissingType,
RangeField) RangeField)
from ..converter import convert_django_field, convert_django_field_with_choices from ..converter import convert_django_field, convert_django_field_with_choices
from ..registry import Registry from ..registry import Registry
from .models import Article, Reporter, Film, FilmDetails, Pet
from ..types import DjangoObjectType from ..types import DjangoObjectType
from .models import Article, Film, FilmDetails, Reporter
# from graphene.core.types.custom_scalars import DateTime, JSONString
def assert_conversion(django_field, graphene_field, *args, **kwargs): def assert_conversion(django_field, graphene_field, *args, **kwargs):
@ -166,6 +169,7 @@ def test_should_manytomany_convert_connectionorlist():
def test_should_manytomany_convert_connectionorlist_list(): def test_should_manytomany_convert_connectionorlist_list():
class A(DjangoObjectType): class A(DjangoObjectType):
class Meta: class Meta:
model = Reporter model = Reporter
@ -179,6 +183,7 @@ def test_should_manytomany_convert_connectionorlist_list():
def test_should_manytomany_convert_connectionorlist_connection(): def test_should_manytomany_convert_connectionorlist_connection():
class A(DjangoObjectType): class A(DjangoObjectType):
class Meta: class Meta:
model = Reporter model = Reporter
interfaces = (Node, ) interfaces = (Node, )
@ -196,6 +201,7 @@ def test_should_manytoone_convert_connectionorlist():
getattr(Reporter.articles, 'related') getattr(Reporter.articles, 'related')
class A(DjangoObjectType): class A(DjangoObjectType):
class Meta: class Meta:
model = Article model = Article
@ -213,6 +219,7 @@ def test_should_onetoone_reverse_convert_model():
getattr(Film.details, 'related') getattr(Film.details, 'related')
class A(DjangoObjectType): class A(DjangoObjectType):
class Meta: class Meta:
model = FilmDetails model = FilmDetails

View File

@ -2,9 +2,9 @@ from django import forms
from py.test import raises from py.test import raises
import graphene import graphene
from ..form_converter import convert_form_field
from graphene import ID, List, NonNull from graphene import ID, List, NonNull
from ..form_converter import convert_form_field
from .models import Reporter from .models import Reporter

View File

@ -8,9 +8,8 @@ import graphene
from graphene.relay import Node from graphene.relay import Node
from ..compat import MissingType, RangeField from ..compat import MissingType, RangeField
from ..types import DjangoObjectType
from ..fields import DjangoConnectionField from ..fields import DjangoConnectionField
from ..registry import reset_global_registry, get_global_registry from ..types import DjangoObjectType
from .models import Article, Reporter from .models import Article, Reporter
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db

View File

@ -1,8 +1,7 @@
from py.test import raises from py.test import raises
from ..types import DjangoObjectType
from ..registry import Registry from ..registry import Registry
from ..types import DjangoObjectType
from .models import Reporter from .models import Reporter
@ -24,10 +23,20 @@ def test_should_raise_if_model_is_invalid():
def test_should_map_fields_correctly(): def test_should_map_fields_correctly():
class ReporterType2(DjangoObjectType): class ReporterType2(DjangoObjectType):
class Meta: class Meta:
model = Reporter model = Reporter
registry = Registry() registry = Registry()
assert list(ReporterType2._meta.fields.keys()) == ['id', 'first_name', 'last_name', 'email', 'pets', 'a_choice', 'articles', 'films'] assert list(
ReporterType2._meta.fields.keys()) == [
'id',
'first_name',
'last_name',
'email',
'pets',
'a_choice',
'articles',
'films']
def test_should_map_only_few_fields(): def test_should_map_only_few_fields():

View File

@ -1,12 +1,12 @@
from graphql.type import GraphQLObjectType
from mock import patch from mock import patch
from graphene import ObjectType, Field, Int, ID, Schema, Interface from graphene import Interface, ObjectType, Schema
from graphene.relay import Node, ConnectionField from graphene.relay import Node
from ..types import DjangoObjectType
from .models import Article as ArticleModel, Reporter as ReporterModel from ..registry import reset_global_registry
from ..registry import reset_global_registry, Registry from ..types import DjangoObjectType
from .models import Article as ArticleModel
from .models import Reporter as ReporterModel
reset_global_registry() reset_global_registry()

View File

@ -2,14 +2,16 @@ from collections import OrderedDict
import six import six
from graphene import ObjectType, Field from graphene import Field, ObjectType
from graphene.types.objecttype import ObjectTypeMeta from graphene.types.objecttype import ObjectTypeMeta
from .converter import convert_django_field_with_choices
from graphene.types.options import Options from graphene.types.options import Options
from .utils import get_model_fields, is_valid_django_model, DJANGO_FILTER_INSTALLED from graphene.types.utils import merge, yank_fields_from_attrs
from .registry import Registry, get_global_registry
from graphene.utils.is_base_type import is_base_type from graphene.utils.is_base_type import is_base_type
from graphene.types.utils import yank_fields_from_attrs, merge
from .converter import convert_django_field_with_choices
from .registry import Registry, get_global_registry
from .utils import (DJANGO_FILTER_INSTALLED, get_model_fields,
is_valid_django_model)
def construct_fields(options): def construct_fields(options):
@ -95,6 +97,7 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)): class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)):
@classmethod @classmethod
def is_type_of(cls, root, context, info): def is_type_of(cls, root, context, info):
if isinstance(root, cls): if isinstance(root, cls):

View File

@ -1,12 +1,17 @@
import inspect import inspect
from django.db import models from django.db import models
from django.db.models.manager import Manager from django.db.models.manager import Manager
from .compat import RelatedObject
# from graphene.utils import LazyList # from graphene.utils import LazyList
class LazyList(object): class LazyList(object):
pass pass
from .compat import RelatedObject
try: try:
import django_filters # noqa import django_filters # noqa

View File

@ -2,6 +2,7 @@ from graphql_django_view import GraphQLView as BaseGraphQLView
class GraphQLView(BaseGraphQLView): class GraphQLView(BaseGraphQLView):
def __init__(self, schema, **kwargs): def __init__(self, schema, **kwargs):
super(GraphQLView, self).__init__( super(GraphQLView, self).__init__(
schema=schema, schema=schema,

View File

@ -1,2 +1,12 @@
[tool:pytest] [tool:pytest]
DJANGO_SETTINGS_MODULE = django_test_settings DJANGO_SETTINGS_MODULE = django_test_settings
[flake8]
exclude = setup.py,docs/*,examples/*,tests,graphene_django/debug/sql/*
max-line-length = 120
[coverage:run]
omit = */tests/*
[isort]
known_first_party=graphene,graphene_django