mirror of
https://github.com/graphql-python/graphene-django.git
synced 2024-11-29 13:03:44 +03:00
127 lines
4.2 KiB
Python
127 lines
4.2 KiB
Python
from collections import OrderedDict
|
|
|
|
import six
|
|
|
|
from django.utils.functional import SimpleLazyObject
|
|
from graphene import Field, ObjectType
|
|
from graphene.types.objecttype import ObjectTypeMeta
|
|
from graphene.types.options import Options
|
|
from graphene.types.utils import merge, yank_fields_from_attrs
|
|
from graphene.utils.is_base_type import is_base_type
|
|
|
|
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):
|
|
_model_fields = get_model_fields(options.model)
|
|
only_fields = options.only_fields
|
|
exclude_fields = options.exclude_fields
|
|
|
|
fields = OrderedDict()
|
|
for field in _model_fields:
|
|
name = field.name
|
|
is_not_in_only = only_fields and name not in options.only_fields
|
|
is_already_created = name in options.fields
|
|
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
|
|
is_no_backref = str(name).endswith('+')
|
|
if is_not_in_only or is_excluded or is_no_backref:
|
|
# We skip this field if we specify only_fields and is not
|
|
# in there. Or when we exclude this field in exclude_fields.
|
|
# Or when there is no back reference.
|
|
continue
|
|
converted = convert_django_field_with_choices(field, options.registry)
|
|
if not converted:
|
|
continue
|
|
fields[name] = converted
|
|
|
|
return fields
|
|
|
|
|
|
class DjangoObjectTypeMeta(ObjectTypeMeta):
|
|
|
|
@staticmethod
|
|
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=(),
|
|
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(
|
|
attrs.pop('Meta', None),
|
|
**defaults
|
|
)
|
|
if not options.registry:
|
|
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 "{}".'
|
|
).format(name, options.model)
|
|
|
|
cls = ObjectTypeMeta.__new__(cls, name, bases, dict(attrs, _meta=options))
|
|
|
|
options.registry.register(cls)
|
|
|
|
options.django_fields = yank_fields_from_attrs(
|
|
construct_fields(options),
|
|
_as=Field,
|
|
)
|
|
options.fields = merge(
|
|
options.interface_fields,
|
|
options.django_fields,
|
|
options.base_fields,
|
|
options.local_fields
|
|
)
|
|
|
|
return cls
|
|
|
|
|
|
class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)):
|
|
|
|
def resolve_id(self, args, context, info):
|
|
return self.pk
|
|
|
|
@classmethod
|
|
def is_type_of(cls, root, context, info):
|
|
if isinstance(root, SimpleLazyObject):
|
|
root._setup()
|
|
root = root._wrapped
|
|
if isinstance(root, cls):
|
|
return True
|
|
if not is_valid_django_model(type(root)):
|
|
raise Exception((
|
|
'Received incompatible instance "{}".'
|
|
).format(root))
|
|
model = root._meta.model
|
|
return model == cls._meta.model
|
|
|
|
@classmethod
|
|
def get_node(cls, id, context, info):
|
|
try:
|
|
return cls._meta.model.objects.get(pk=id)
|
|
except cls._meta.model.DoesNotExist:
|
|
return None
|