Basic Proxy Model Support in Graphene-Django

in #utopian-io7 years ago (edited)

Pull Request Link

Summary

This PR adds support for querying proxy models in Graphene-Django. Graphene-Django is a Python library that enables developers to create GraphQL services on top of the Django web framework.

Proxy models are an interesting feature of Django. In short, they let you modify the runtime behavior of a model while maintaining the same database table as the superclass. This is contrasted with the typical inheritance model in Django, which will create a separate table for each model.

Motivation

At Quorum, we use proxy models in many places. While experimenting with GraphQL, I ran into an issue where some of my queries were failing, and suspected there was an issue with types. It turned out that our proxy models defined their own __init__ methods which set their class to be the proxy model class at runtime. Graphene did not yet handle this situation.

Details and Example

Previously, while you were still able to query for all objects of a particular type, of which some may be proxy models, an error would be thrown if at runtime the Python class were changed. In graphene_django_types.py there is a line that verifies that each node in the resulting GraphQL matches the type of the declared type in the schema. I'll use a test case from my PR to illustrate this.

Let's say you have a Reporter Django model like so:

class Reporter(models.Model):
    first_name = ...
    last_name
    ...

Your GraphQL schema might look like:

class ReporterType(DjangoObjectType):
    class Meta:
        model = Reporter # this is your Django model
        interfaces = (Node, )
        use_connection = True

then, you might want to query for all Reporters:

class Query(graphene.ObjectType):
    all_reporters = DjangoConnectionFilterField(ReporterType)

The line model == cls._meta.model (here) would make sure that the model of each reporter type matches the declared model in the ReporterType meta. If you had just reporters, this would be totally fine.

Let's say you also had another Django model called CNNReporter that is a proxy model to of Reporter:

class CNNReporter(Reporter):
    class Meta:
        proxy = True

Now, if we override the __init__ method of the Reporter class to modify its class (link to code), our proxy model instances will have a different type at runtime than instances of Reporter. The line would compare Reporter == CNNReporter, which would fail.

If we instead check the concrete_model field in the _meta of the instance’s model, we will always get the same type for all objects in the query, regardless of proxy model inheritance. In this case, CNNReporter's concrete_model is Reporter. The change is here.



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Hey @jmares I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • This is your first accepted contribution here in Utopian. Welcome!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]