Improved Django model conversion

This commit is contained in:
Syrus Akbary 2015-09-28 01:51:51 -07:00
parent 2e8707aee6
commit 76147d7c26
7 changed files with 77 additions and 11 deletions

View File

@ -7,34 +7,48 @@ from graphene.core.fields import (
IntField, IntField,
BooleanField, BooleanField,
FloatField, FloatField,
ListField
) )
from graphene.contrib.django.fields import DjangoModelField
@singledispatch @singledispatch
def convert_django_field(field): def convert_django_field(field, cls):
raise Exception("Don't know how to convert the Django field %s"%field) raise Exception("Don't know how to convert the Django field %s (%s)" % (field, field.__class__))
@convert_django_field.register(models.DateField)
@convert_django_field.register(models.CharField) @convert_django_field.register(models.CharField)
def _(field): @convert_django_field.register(models.TextField)
def _(field, cls):
return StringField(description=field.help_text) return StringField(description=field.help_text)
@convert_django_field.register(models.AutoField) @convert_django_field.register(models.AutoField)
def _(field): def _(field, cls):
return IDField(description=field.help_text) return IDField(description=field.help_text)
@convert_django_field.register(models.BigIntegerField) @convert_django_field.register(models.BigIntegerField)
@convert_django_field.register(models.IntegerField) @convert_django_field.register(models.IntegerField)
def _(field): def _(field, cls):
return IntField(description=field.help_text) return IntField(description=field.help_text)
@convert_django_field.register(models.BooleanField) @convert_django_field.register(models.BooleanField)
def _(field): def _(field, cls):
return BooleanField(description=field.help_text) return BooleanField(description=field.help_text)
@convert_django_field.register(models.FloatField) @convert_django_field.register(models.FloatField)
def _(field): def _(field, cls):
return FloatField(description=field.help_text) return FloatField(description=field.help_text)
@convert_django_field.register(models.ManyToOneRel)
def _(field, cls):
return ListField(DjangoModelField(field.related_model))
@convert_django_field.register(models.ForeignKey)
def _(field, cls):
return DjangoModelField(field.related_model)

View File

@ -0,0 +1,23 @@
from graphene.core.fields import Field
from graphene.utils import cached_property
from graphene.env import get_global_schema
def get_type_for_model(schema, model):
schema = schema or get_global_schema()
types = schema.types.values()
for _type in types:
type_model = getattr(_type._meta, 'model', None)
if model == type_model:
return _type._meta.type
class DjangoModelField(Field):
def __init__(self, model):
super(DjangoModelField, self).__init__(None)
self.model = model
@cached_property
def type(self):
return get_type_for_model(self.schema, self.model)

View File

@ -1,4 +1,5 @@
import six import six
from django.db import models
from graphene.core.types import ObjectTypeMeta, ObjectType from graphene.core.types import ObjectTypeMeta, ObjectType
from graphene.contrib.django.options import DjangoOptions from graphene.contrib.django.options import DjangoOptions
@ -6,6 +7,13 @@ from graphene.contrib.django.converter import convert_django_field
from graphene.relay import Node from graphene.relay import Node
def get_reverse_fields(model):
for name, attr in model.__dict__.items():
related = getattr(attr, 'related', None)
if isinstance(related, models.ManyToOneRel):
yield related
class DjangoObjectTypeMeta(ObjectTypeMeta): class DjangoObjectTypeMeta(ObjectTypeMeta):
options_cls = DjangoOptions options_cls = DjangoOptions
def add_extra_fields(cls): def add_extra_fields(cls):
@ -14,10 +22,11 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
only_fields = cls._meta.only_fields only_fields = cls._meta.only_fields
# print cls._meta.model._meta._get_fields(forward=False, reverse=True, include_hidden=True) # print cls._meta.model._meta._get_fields(forward=False, reverse=True, include_hidden=True)
for field in cls._meta.model._meta.fields: reverse_fields = tuple(get_reverse_fields(cls._meta.model))
for field in cls._meta.model._meta.fields + reverse_fields:
if only_fields and field.name not in only_fields: if only_fields and field.name not in only_fields:
continue continue
converted_field = convert_django_field(field) converted_field = convert_django_field(field, cls)
cls.add_to_class(field.name, converted_field) cls.add_to_class(field.name, converted_field)

View File

@ -76,8 +76,8 @@ class Field(object):
@cached_property @cached_property
def field(self): def field(self):
if not self.field_type: # if not self.field_type:
raise Exception('Must specify a field GraphQL type for the field %s'%self.field_name) # raise Exception('Must specify a field GraphQL type for the field %s'%self.field_name)
if not self.object_type: if not self.object_type:
raise Exception('Field could not be constructed in a non graphene.Type or graphene.Interface') raise Exception('Field could not be constructed in a non graphene.Type or graphene.Interface')

View File

@ -16,6 +16,14 @@ class Options(object):
self.parents = [] self.parents = []
self.valid_attrs = DEFAULT_NAMES self.valid_attrs = DEFAULT_NAMES
# @property
# def schema(self):
# return self._schema or get_global_schema()
# @schema.setter
# def schema(self, schema):
# self._schema = schema
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):
cls._meta = self cls._meta = self
self.parent = cls self.parent = cls

View File

@ -42,6 +42,10 @@ class Schema(object):
raise Exception('Type %s not found in %r' % (type_name, self)) raise Exception('Type %s not found in %r' % (type_name, self))
return self._types[type_name] return self._types[type_name]
@property
def types(self):
return self._types
def execute(self, request='', root=None, vars=None, operation_name=None): def execute(self, request='', root=None, vars=None, operation_name=None):
root = root or object() root = root or object()
return graphql( return graphql(

View File

@ -74,6 +74,14 @@ def test_should_node():
def get_node(cls, id): def get_node(cls, id):
return ReporterNodeType(Reporter(id=2, first_name='Cookie Monster')) return ReporterNodeType(Reporter(id=2, first_name='Cookie Monster'))
class ArticleNodeType(DjangoNode):
class Meta:
model = Article
@classmethod
def get_node(cls, id):
return ArticleNodeType(None)
class Query(graphene.ObjectType): class Query(graphene.ObjectType):
node = relay.NodeField() node = relay.NodeField()
reporter = graphene.Field(ReporterNodeType) reporter = graphene.Field(ReporterNodeType)