graphql-python / graphene

GraphQL framework for Python
http://graphene-python.org/
MIT License
8.09k stars 825 forks source link

InputObjectType.__init_sublcass_with_meta__ skips Meta.fields attribute #1429

Open lrzp opened 2 years ago

lrzp commented 2 years ago

In InputObjectType.__init_subclass_with_meta__, the fields of the Meta class (passed as an options arg) are never referenced. This in effect just skips them completely wich stands in contradiction to the docstring.

Meta class options (optional):
    ...
    fields (Dict[str, graphene.InputField]): Dictionary of field name to InputField. Not
    recommended to use (prefer class attributes).

InputObjectType has to be subclassed and __init_subclass_with_meta__ has to be extended for this feature to work, like in graphene.tests.issues.test_720.

If this is accepted I can do a PR for this along the lines:

@classmethod
+   def __init_subclass_with_meta__(cls, container=None, _meta=None, fields=None **options):
-   def __init_subclass_with_meta__(cls, container=None, _meta=None, **options):
        if not _meta:
            _meta = InputObjectTypeOptions(cls)

+       if not fields:
+           fields = {}
-       fields = {}
        for base in reversed(cls.__mro__):
            fields.update(yank_fields_from_attrs(base.__dict__, _as=InputField))

        if _meta.fields:
            _meta.fields.update(fields)
        else:
            _meta.fields = fields
        if container is None:
            container = type(cls.__name__, (InputObjectTypeContainer, cls), {})
        _meta.container = container
        super(InputObjectType, cls).__init_subclass_with_meta__(_meta=_meta, **options)
erikwrede commented 2 years ago

Do you have a case for using Meta's fields that doesn't have an alternative? If the feature is unusable right now, but no one else noticed there might be a need to re evaluate the need for this field as a whole.

lrzp commented 2 years ago

In my case, it was a creation of input fields dynamically based on a subset of model fields. I've tried to reproduce the Node behavior to keep things simple and DRY.

Node behavior:

class MyNode(DjangoObjectType):
    class Meta:
        model = MyModel
        fields = tuple_of_boolean_fields

How I want Input to work (according to docs):

class MyInput(graphene.InputObjectType):
    class Meta:
        fields = {field: graphene.Boolean() for field in tuple_of_boolean_fields}

How it has to be solved right now:

class MyInput(graphene.InputObjectType):
    field1 = graphene.Boolean()
    field2 = graphene.Boolean()
    field3 = graphene.Boolean()
    field4 = graphene.Boolean()
    field5 = graphene.Boolean()

This can be solved with some meta-programing but it leaves us with an overly complicated, hard-to-read hack.