graphene-django icon indicating copy to clipboard operation
graphene-django copied to clipboard

Merge fields used in meta attribute passed on DjangoObjectType

Open sebsasto opened this issue 5 years ago • 3 comments

Currently I'm working on an integration with Django Parler that require to subclass DjangoObjectType to pass the translated fields into the original model. However I cannot do this because the _meta.fields gets overwritten in the __init_subclass_with_meta__.

This PR allows to pass fields in the _meta attribute and merge them with the other fields. This is the same behaviour of the __init_subclass_with_meta__ inside ObjectType

sebsasto avatar Feb 22 '21 19:02 sebsasto

@zbyte64 I added the test cases. Please let me know if it is ok

sebsasto avatar Feb 23 '21 12:02 sebsasto

If anyone is curious to see an example on real case scenario, let me exemplify with Parler. Django Parler have a functionality that adds all the translated fields into the main Model. In this case we can access directly the translated fields from the main model. However, those fields are not part of the main Meta class and therefore DjangoObjectType does not recognise them.

In the example below I'm injecting Django Parler fields in a custom DjangoObjectType using the feature introduced in this PR.

class DjangoParlerObjectType(DjangoObjectType):

    class Meta:
            abstract = True

    @classmethod
    def __init_subclass_with_meta__(cls, model=None, _meta=None, registry=None, convert_choices_to_enum=True, **options):
        assert is_valid_django_model(model), (
            'You need to pass a valid Django Model in {}.Meta, received "{}".'
        ).format(cls.__name__, model)

        if hasattr(model, '_parler_meta'):
            if not _meta:
                _meta = DjangoObjectTypeOptions(cls)

            if not registry:
                registry = get_global_registry()

            fields = OrderedDict()
            for name, parler_model in model._parler_meta.get_fields_with_model():
                field = parler_model._meta.get_field(name)
                field.null = field.null or field.blank

                _convert_choices_to_enum = convert_choices_to_enum
                if not isinstance(_convert_choices_to_enum, bool):
                    if name in _convert_choices_to_enum:
                        _convert_choices_to_enum = True
                    else:
                        _convert_choices_to_enum = False

                converted = convert_django_field_with_choices(
                    field, registry, convert_choices_to_enum=_convert_choices_to_enum
                )
                fields[name] = converted
            
            parler_fields = yank_fields_from_attrs(
                fields,
                _as=Field,
            )

            _meta.fields = parler_fields

        super().__init_subclass_with_meta__(model=model, _meta=_meta, registry=registry, convert_choices_to_enum=convert_choices_to_enum, **options)

sebsasto avatar Feb 23 '21 18:02 sebsasto

@sebsasto updates?

firaskafri avatar Sep 23 '22 08:09 firaskafri