Removed Meta inheritance in favor of __init_subclass_with_meta__

This commit is contained in:
Syrus Akbary 2017-07-24 22:27:26 -07:00
parent cec1a84480
commit 18db46e132

View File

@ -3,11 +3,10 @@ from collections import OrderedDict
import six import six
from django.utils.functional import SimpleLazyObject from django.utils.functional import SimpleLazyObject
from graphene import Field, ObjectType from graphene import Field
from graphene.types.objecttype import ObjectTypeMeta from graphene.relay import Connection, Node
from graphene.types.options import Options from graphene.types.objecttype import ObjectType, ObjectTypeOptions
from graphene.types.utils import merge, yank_fields_from_attrs from graphene.types.utils import yank_fields_from_attrs
from graphene.utils.is_base_type import is_base_type
from .converter import convert_django_field_with_choices from .converter import convert_django_field_with_choices
from .registry import Registry, get_global_registry from .registry import Registry, get_global_registry
@ -15,16 +14,14 @@ from .utils import (DJANGO_FILTER_INSTALLED, get_model_fields,
is_valid_django_model) is_valid_django_model)
def construct_fields(options): def construct_fields(model, registry, only_fields, exclude_fields):
_model_fields = get_model_fields(options.model) _model_fields = get_model_fields(model)
only_fields = options.only_fields
exclude_fields = options.exclude_fields
fields = OrderedDict() fields = OrderedDict()
for name, field in _model_fields: for name, field in _model_fields:
is_not_in_only = only_fields and name not in options.only_fields is_not_in_only = only_fields and name not in only_fields
is_already_created = name in options.fields # is_already_created = name in options.fields
is_excluded = name in exclude_fields or is_already_created is_excluded = name in exclude_fields # or is_already_created
# https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_query_name # https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_query_name
is_no_backref = str(name).endswith('+') is_no_backref = str(name).endswith('+')
if is_not_in_only or is_excluded or is_no_backref: if is_not_in_only or is_excluded or is_no_backref:
@ -32,74 +29,67 @@ def construct_fields(options):
# in there. Or when we exclude this field in exclude_fields. # in there. Or when we exclude this field in exclude_fields.
# Or when there is no back reference. # Or when there is no back reference.
continue continue
converted = convert_django_field_with_choices(field, options.registry) converted = convert_django_field_with_choices(field, registry)
fields[name] = converted fields[name] = converted
return fields return fields
class DjangoObjectTypeMeta(ObjectTypeMeta): class DjangoObjectTypeOptions(ObjectTypeOptions):
model = None # type: Model
registry = None # type: Registry
connection = None # type: Type[Connection]
@staticmethod filter_fields = ()
def __new__(cls, name, bases, attrs):
# Also ensure initialization is only performed for subclasses of
# DjangoObjectType
if not is_base_type(bases, DjangoObjectTypeMeta):
return type.__new__(cls, name, bases, attrs)
defaults = dict(
name=name,
description=attrs.pop('__doc__', None),
model=None,
local_fields=None,
only_fields=(),
exclude_fields=(),
interfaces=(),
skip_registry=False,
registry=None
)
if DJANGO_FILTER_INSTALLED:
# In case Django filter is available, then
# we allow more attributes in Meta
defaults.update(
filter_fields=(),
)
options = Options( class DjangoObjectType(ObjectType):
attrs.pop('Meta', None), @classmethod
**defaults def __init_subclass_with_meta__(cls, model=None, registry=None, skip_registry=False,
) only_fields=(), exclude_fields=(), filter_fields=None, connection=None, use_connection=None, interfaces=(), **options):
if not options.registry: assert is_valid_django_model(model), (
options.registry = get_global_registry()
assert isinstance(options.registry, Registry), (
'The attribute registry in {}.Meta needs to be an instance of '
'Registry, received "{}".'
).format(name, options.registry)
assert is_valid_django_model(options.model), (
'You need to pass a valid Django Model in {}.Meta, received "{}".' 'You need to pass a valid Django Model in {}.Meta, received "{}".'
).format(name, options.model) ).format(cls.__name__, model)
cls = ObjectTypeMeta.__new__(cls, name, bases, dict(attrs, _meta=options)) if not registry:
registry = get_global_registry()
options.registry.register(cls) assert isinstance(registry, Registry), (
'The attribute registry in {} needs to be an instance of '
'Registry, received "{}".'
).format(cls.__name__, registry)
options.django_fields = yank_fields_from_attrs( if not DJANGO_FILTER_INSTALLED and filter_fields:
construct_fields(options), raise Exception("Can only set filter_fields if Django-Filter is installed")
django_fields = yank_fields_from_attrs(
construct_fields(model, registry, only_fields, exclude_fields),
_as=Field, _as=Field,
) )
options.fields = merge(
options.interface_fields,
options.django_fields,
options.base_fields,
options.local_fields
)
return cls if use_connection is None and interfaces:
use_connection = any((issubclass(interface, Node) for interface in interfaces))
if use_connection and not connection:
# We create the connection automatically
connection = Connection.create_type('{}Connection'.format(cls.__name__), node=cls)
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)): if connection is not None:
assert issubclass(connection, Connection), "The connection must be a Connection. Received {}".format(connection.__name__)
def resolve_id(self, args, context, info): _meta = DjangoObjectTypeOptions(cls)
_meta.model = model
_meta.registry = registry
_meta.filter_fields = filter_fields
_meta.fields = django_fields
_meta.connection = connection
super(DjangoObjectType, cls).__init_subclass_with_meta__(_meta=_meta, interfaces=interfaces, **options)
if not skip_registry:
registry.register(cls)
def resolve_id(self):
return self.pk return self.pk
@classmethod @classmethod