|
3 | 3 | import six
|
4 | 4 |
|
5 | 5 | from django.utils.functional import SimpleLazyObject
|
6 |
| -from graphene import Field, ObjectType |
7 |
| -from graphene.types.objecttype import ObjectTypeMeta |
8 |
| -from graphene.types.options import Options |
9 |
| -from graphene.types.utils import merge, yank_fields_from_attrs |
10 |
| -from graphene.utils.is_base_type import is_base_type |
| 6 | +from graphene import Field |
| 7 | +from graphene.relay import Connection, Node |
| 8 | +from graphene.types.objecttype import ObjectType, ObjectTypeOptions |
| 9 | +from graphene.types.utils import yank_fields_from_attrs |
11 | 10 |
|
12 | 11 | from .converter import convert_django_field_with_choices
|
13 | 12 | from .registry import Registry, get_global_registry
|
14 | 13 | from .utils import (DJANGO_FILTER_INSTALLED, get_model_fields,
|
15 | 14 | is_valid_django_model)
|
16 | 15 |
|
17 | 16 |
|
18 |
| -def construct_fields(options): |
19 |
| - _model_fields = get_model_fields(options.model) |
20 |
| - only_fields = options.only_fields |
21 |
| - exclude_fields = options.exclude_fields |
| 17 | +def construct_fields(model, registry, only_fields, exclude_fields): |
| 18 | + _model_fields = get_model_fields(model) |
22 | 19 |
|
23 | 20 | fields = OrderedDict()
|
24 | 21 | for name, field in _model_fields:
|
25 |
| - is_not_in_only = only_fields and name not in options.only_fields |
26 |
| - is_already_created = name in options.fields |
27 |
| - is_excluded = name in exclude_fields or is_already_created |
| 22 | + is_not_in_only = only_fields and name not in only_fields |
| 23 | + # is_already_created = name in options.fields |
| 24 | + is_excluded = name in exclude_fields # or is_already_created |
28 | 25 | # https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_query_name
|
29 | 26 | is_no_backref = str(name).endswith('+')
|
30 | 27 | if is_not_in_only or is_excluded or is_no_backref:
|
31 | 28 | # We skip this field if we specify only_fields and is not
|
32 | 29 | # in there. Or when we exclude this field in exclude_fields.
|
33 | 30 | # Or when there is no back reference.
|
34 | 31 | continue
|
35 |
| - converted = convert_django_field_with_choices(field, options.registry) |
| 32 | + converted = convert_django_field_with_choices(field, registry) |
36 | 33 | fields[name] = converted
|
37 | 34 |
|
38 | 35 | return fields
|
39 | 36 |
|
40 | 37 |
|
41 |
| -class DjangoObjectTypeMeta(ObjectTypeMeta): |
42 |
| - |
43 |
| - @staticmethod |
44 |
| - def __new__(cls, name, bases, attrs): |
45 |
| - # Also ensure initialization is only performed for subclasses of |
46 |
| - # DjangoObjectType |
47 |
| - if not is_base_type(bases, DjangoObjectTypeMeta): |
48 |
| - return type.__new__(cls, name, bases, attrs) |
49 |
| - |
50 |
| - defaults = dict( |
51 |
| - name=name, |
52 |
| - description=attrs.pop('__doc__', None), |
53 |
| - model=None, |
54 |
| - local_fields=None, |
55 |
| - only_fields=(), |
56 |
| - exclude_fields=(), |
57 |
| - interfaces=(), |
58 |
| - skip_registry=False, |
59 |
| - registry=None |
60 |
| - ) |
61 |
| - if DJANGO_FILTER_INSTALLED: |
62 |
| - # In case Django filter is available, then |
63 |
| - # we allow more attributes in Meta |
64 |
| - defaults.update( |
65 |
| - filter_fields=(), |
66 |
| - ) |
67 |
| - |
68 |
| - options = Options( |
69 |
| - attrs.pop('Meta', None), |
70 |
| - **defaults |
71 |
| - ) |
72 |
| - if not options.registry: |
73 |
| - options.registry = get_global_registry() |
74 |
| - assert isinstance(options.registry, Registry), ( |
75 |
| - 'The attribute registry in {}.Meta needs to be an instance of ' |
76 |
| - 'Registry, received "{}".' |
77 |
| - ).format(name, options.registry) |
78 |
| - assert is_valid_django_model(options.model), ( |
| 38 | +class DjangoObjectTypeOptions(ObjectTypeOptions): |
| 39 | + model = None # type: Model |
| 40 | + registry = None # type: Registry |
| 41 | + connection = None # type: Type[Connection] |
| 42 | + |
| 43 | + filter_fields = () |
| 44 | + |
| 45 | + |
| 46 | +class DjangoObjectType(ObjectType): |
| 47 | + @classmethod |
| 48 | + def __init_subclass_with_meta__(cls, model=None, registry=None, skip_registry=False, |
| 49 | + only_fields=(), exclude_fields=(), filter_fields=None, connection=None, use_connection=None, interfaces=(), **options): |
| 50 | + assert is_valid_django_model(model), ( |
79 | 51 | 'You need to pass a valid Django Model in {}.Meta, received "{}".'
|
80 |
| - ).format(name, options.model) |
| 52 | + ).format(cls.__name__, model) |
| 53 | + |
| 54 | + if not registry: |
| 55 | + registry = get_global_registry() |
81 | 56 |
|
82 |
| - cls = ObjectTypeMeta.__new__(cls, name, bases, dict(attrs, _meta=options)) |
| 57 | + assert isinstance(registry, Registry), ( |
| 58 | + 'The attribute registry in {} needs to be an instance of ' |
| 59 | + 'Registry, received "{}".' |
| 60 | + ).format(cls.__name__, registry) |
83 | 61 |
|
84 |
| - options.registry.register(cls) |
| 62 | + if not DJANGO_FILTER_INSTALLED and filter_fields: |
| 63 | + raise Exception("Can only set filter_fields if Django-Filter is installed") |
85 | 64 |
|
86 |
| - options.django_fields = yank_fields_from_attrs( |
87 |
| - construct_fields(options), |
| 65 | + django_fields = yank_fields_from_attrs( |
| 66 | + construct_fields(model, registry, only_fields, exclude_fields), |
88 | 67 | _as=Field,
|
89 | 68 | )
|
90 |
| - options.fields = merge( |
91 |
| - options.interface_fields, |
92 |
| - options.django_fields, |
93 |
| - options.base_fields, |
94 |
| - options.local_fields |
95 |
| - ) |
96 | 69 |
|
97 |
| - return cls |
| 70 | + if use_connection is None and interfaces: |
| 71 | + use_connection = any((issubclass(interface, Node) for interface in interfaces)) |
| 72 | + |
| 73 | + if use_connection and not connection: |
| 74 | + # We create the connection automatically |
| 75 | + connection = Connection.create_type('{}Connection'.format(cls.__name__), node=cls) |
| 76 | + |
| 77 | + if connection is not None: |
| 78 | + assert issubclass(connection, Connection), "The connection must be a Connection. Received {}".format(connection.__name__) |
98 | 79 |
|
| 80 | + _meta = DjangoObjectTypeOptions(cls) |
| 81 | + _meta.model = model |
| 82 | + _meta.registry = registry |
| 83 | + _meta.filter_fields = filter_fields |
| 84 | + _meta.fields = django_fields |
| 85 | + _meta.connection = connection |
99 | 86 |
|
100 |
| -class DjangoObjectType(six.with_metaclass(DjangoObjectTypeMeta, ObjectType)): |
| 87 | + super(DjangoObjectType, cls).__init_subclass_with_meta__(_meta=_meta, interfaces=interfaces, **options) |
| 88 | + |
| 89 | + if not skip_registry: |
| 90 | + registry.register(cls) |
101 | 91 |
|
102 |
| - def resolve_id(self, args, context, info): |
| 92 | + def resolve_id(self): |
103 | 93 | return self.pk
|
104 | 94 |
|
105 | 95 | @classmethod
|
|
0 commit comments