Graphene-django: Proposal to use graphene-django without relay

Created on 20 Sep 2017  Â·  36Comments  Â·  Source: graphql-python/graphene-django

A few weeks ago I started playing with this module and it seemed very good, congratulations to the whole team.
The truth is that after further testing, following the documentation, I was not pleased that most of the implementations of important aspects in an API (pagination, filtering, among others) were only present for Relay, so I continued testing on a small project in which I was working, and make a small module, which I call graphene-django-extras, in which the following characteristics are present:

Fields:
1. DjangoListField
2. DjangoFilterListField
3. DjangoFilterPaginateListField
4. DjangoListObjectField
Mutations:
1. DjangoSerializerMutation
Types:
1. DjangoInputObjectType
2. DjangoPaginatedObjectListType
Pagination:
1. LimitOffsetGraphqlPagination
2. PageGraphqlPagination
3. CursorGraphqlPagination (_cooming soon_)

Here a example of query with pagination and filters, without Relay:

{
  allUsers(username_Icontains:"john"){
    results(limit:5, offset:5){
      id
      username
      firstName
      lastName
    }
    totalCount
  }
}

Here I leave the link of the repository with some documentation:
graphene-django-extras

Note: If someone finds a bug please let me know. I would also appreciate any help you want to provide to improve the features.
Enjoy it!!!

Most helpful comment

I highly recommend getting graphene-django-extras as functionalities in the core library. They are basic functionalities that should be maintained by the core team and open source community at large. Tying the graphene library so closely to the Relay client needs to be undone, and that would be a good first step.

All 36 comments

Hello Ernesto!

I just recently joined the team due to seeing great potential in the
project and noticing limitations just like the ones you mentioned.

First: AWESOME! :D

Second: I will take a closer look and play with what you created by the end
of this week. I will also try to integrate into the core what makes sense
(by creating PR for others to review and comment on).

I would love to stay in touch with you.
Are you on slack? (graphene channel)

Best,
Eraldo

--
/============================================
| E r a l d o E n e r g y
| Coach: Personal Development
| Legend: www.colegend.org
| Developer: Agile Apps
| Dancer: Salsa, Bachata, Kizomba & Co.
|--------------------------------------------
| M: +49 176 97 99 44 80 <004917697994480>
| E: [email protected]
| W: www.eraldo.org
| S: www.facebook.com/Eraldo
\============================================
Be Awesome! Love with an Open Heart! And Live a Legendary Life.. Today!

On Wed, Sep 20, 2017 at 9:59 PM, Ernesto Pérez Amigo <
[email protected]> wrote:

A few weeks ago I started playing with this module and it seemed very
good, congratulations to the whole team.
The truth is that after further testing, following the documentation, I
was not pleased that most of the implementations of important aspects in an
API (pagination, filtering, among others) were only present for Relay, so I
continued testing on a small project in which I was working, and make a
small module, which I call graphene-django-extras, in which the
following characteristics are present:

Fields:

  1. DjangoListField
  2. DjangoFilterListField
  3. DjangoFilterPaginateListField
  4. DjangoListObjectField
    Mutations:
  5. DjangoSerializerMutation
    Types:
  6. DjangoInputObjectType
  7. DjangoPaginatedObjectListType
    Pagination:
  8. LimitOffsetGraphqlPagination
  9. PageGraphqlPagination
  10. CursosGraphqlPagination (cooming soon)

Here I leave the link of the repository:
Graphene-Django-Extras
https://github.com/eamigo86/graphene-django-extras

Note: If someone finds a bug please let me know. I would also
appreciate any help you want to provide to improve the features.
Enjoy it!!!

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/graphql-python/graphene-django/issues/274, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAEeotZsaCJSBLxUXcGX2Lat69Qi5ODtks5skW6sgaJpZM4PeZUe
.

Hi @Eraldo !, first of all thank you. About Slack, I'm sorry, but I live in Cuba (:cuba:) and here the Internet is slow and expensive, so I only connect sometimes when I can and I really need it

I totally understand.
I have been to Cuba a couple of years ago and internet was not my focus back then so I did not think of that challenge right away. ;)
How about email? Other preference? (Btw: I also sent you a facebook request earlier)
I would like to better understand your needs and expectations for the project.

I will review this repository this weekend, it looks quite interesting

I have been playing with this and it looks very promising. I have submitted some issues on things I have encountered.

IMHO, this should really be part of graphene-django, since it makes GraphQL so much nicer in situations where you really don't want to use Relay.

Thanks @basilfx, I fixed all the bugs that you found, thank you for your comments and for your time testing the module. I am pleased to inform you that a new version is now available: 0.0.1b10

Hello everyone, I have continued working on this small module fixing the errors detected by the users and adding new things to make the implementation of Graphql in Django more pleasant for those who do not use Relay in their projects. I would like to thank the graphene-django team and all those who have tried my module and have given their impressions.

I am pleased to announce that I have just published the second stable version of graphene-django-extras with few new things:

1- Fixed bug on DjangoInputObjectType class that refer to unused interface attribute.
2- Added support to create nested objects like in DRF (http://www.django-rest-framework.org/api-guide/serializers/#writable-nested-representations), it's valid to SerializerMutation and DjangoInputObjectType, only is necessary to specify _nested_fields=True_ on its Meta class definition.
01-resp_image

3- Added support to show, only in mutations types to create objects and with _debug=True_ on settings, inputs autocomplete ordered by required fields first.
01-create_mutations_show_required_fields_first release_0 0 1

4- Fixed others minors bugs

Hi again everyone, I'm pleased to announce that I have published the 0.1.0-alpha2 version of _graphene-django-extras_ with exellent new things:

  1. Added support to multiselect choices values for Django CharField with choices attribute, on queries and mutations. Example: Integration with django-multiselectfield package.
  2. Added support to GenericForeignKey and GenericRelation fields, on queries and mutations.
  3. Added first approach to support Subscriptions with Channels, with subscribe and unsubscribe operations. Using channels-api package. :grinning:
  4. Mini web tool to test notifications of graphene-django-extras subscription. It is intuitive and simple (graphene-django-extras subscriptions tool.zip)
  5. Fixed some minors optimization's bugs.

@eamigo86 Thanks for the package, finally able to create mutations that work with DRF.
Had a hard time getting it to run because the errors on the default serializer implementation were causing the mutation to fail silently.
This package should be integrated into the core, or at least mentioned in the docs!
Anyway, keep up the good work guys, awesome packages.

@cdvv7788 can you tell me more about the problems you had with the default SerializerMutation? I'm trying to create a mutation for a serializer with a nested serializer, graphene recognizes the nested serializer as a string in the input field but if I put a string of course it can't be serialized and the mutation works returning a null object (fails silently).

Hi @esistgut, Take a look to graphene-django-extras module, it may be helpful.

@eamigo86 I'm trying it. I have a serializer with another nested serializer with many=True:

class SecondSerializer(serializers.ModelSerializer):
    class Meta:
        model = SecondModel
        fields = '__all__'

class FirstSerializer(serializers.ModelSerializer):
    class Meta:
        model = FirstModel
        fields = ('field_a', 'field_b')
    field_b = SecondSerializer(many=True)

I'm trying to create a mutation on the FirstSerializer like this:

class FirstMutation(DjangoSerializerMutation):
    class Meta:
        serializer_class = FirstSerializer

class Mutation(graphene.ObjectType):
    create_order = mutations.OrderMutation.CreateField()

But the field_b input for the auto generated InputType is fieldB: [ID]
If I set the nested_fields = True:

class FirstMutation(DjangoSerializerMutation):
    class Meta:
        nested_fields = True
        serializer_class = FirstSerializer

The field gets removed from the auto generated InputType.
What am I doing wrong?

Hi @esistgut, sorry for dalay. In this case you must define a DjangoInputObjectType for your SecondModel, example:

```python

types.py

from graphene_django_extras.types import DjangoInputObjectType

class SecondModelInputType(DjangoInputObjectType):
class Meta:
model = SecondModel
````

Please test it.
Note: Please for further questions about this module please open an issue in graphene-django-extras repo.

Hola @eamigo86, yo también soy cubano. Has pensado algo para evitar el problema de las N+1 consultas, estaba pensando en hacer algo con prefetch_related.

Oh! Mala mía, ya lo tenías hecho de antes, solo que estaba usando un mixing mío y era yo mismo quien estaba haciendo la query lazy.

A brilliant job has done @eamigo86 in his graphene-django-extras package.

IMHO, the team should seriously consider somehow integrating it into graphene-django, or at least mentioning it in the docs.

@eamigo86 you really did a great job for the extra featues, most especially the graphene-django-subscriptions. seems promising.

Hey @Eraldo , @eamigo86 !! I'm using graphene-django, and I'd like to contribute. Is there a slack channel? We should get in touch!

Hi @helpse, well I'm not sure if there is an active slack channel, if there is one please let me know. Actually I am working on a new graphene-django-extra version and any help is welcome .

@eamigo86 I'd like to invite you to my Slack channel meanwhile, would you share your email address?

@syrusakbary you seem to be the current maintainer of this project. Would you be so kind as to share your opinion on whether graphene-django-extra should be merged into graphene-django core ?

I for one am quite keen to get pagination, filtering, etc.. without using relay. I can of course depend on graphene-django-extra, but since I'm on a core, long-term company project, I'd feel more reassured to see these features in the main stream.

@ArnaudPel I've solved pagination, subclassing DjangoFilterConnectionField, and sending a list of nodes instead of an UserConnection. Check this out https://github.com/inspired-solutions/django-graphql-starter/tree/open-crud

@syrusakbary @helpse nevermind my previous comment, I should have started with the question "why did the relay guys feel it necessary to introduce edges and nodes ?". Now that I understand they believe it's the best way to do pagination, I'll trust them and embrace the relay spec as well.

How shall we take this issue forward? Should we document graphene-django-extras in the official graphene-docs?

If someone finds a bug please let me know.

@eamigo86 i have encountered following bugs, and implemented code to avoid it. Sorry, i'm too lazy to create issues.


InputObjectType Bug

class CustomFieldsMixin:
    """
    Following mixin allows to add custom fields for input type.

    That is default behaviour for graphene.InputObjectType but django extras
    ignores and overrides this behaviour.

    Code aster super() is copypasted from graphene.InputObjectType sources.
    """
    @classmethod
    def __init_subclass_with_meta__(cls, *args, **kwargs):
        super().__init_subclass_with_meta__(*args, **kwargs)

        fields = OrderedDict()
        for base in reversed(cls.__mro__):
            fields.update(yank_fields_from_attrs(base.__dict__, _as=InputField))  # noqa

        if cls._meta.fields:
            cls._meta.fields.update(fields)
        else:
            cls._meta.fields = fields


LimitOffsetGraphqlPagination bug

from functools import wraps


def paginate_queryset_monkey_patch(method):
    """
    Patch following behaviour:

    LimitOffsetGraphqlPagination.paginate_queryset generally returns
    paginated queryset, but when offset is greater than amount of objects in
    db, for some reasons, it return empty list instead of empty queryset.
    """
    @wraps(method)
    def wrapper(self, queryset, *args, **kwargs):
        result = method(self, queryset, *args, **kwargs)
        if result == []:
            return queryset.none()
        return result

    return wrapper


_pag = LimitOffsetGraphqlPagination
_pag.paginate_queryset = paginate_queryset_monkey_patch(_pag.paginate_queryset)

Also i had to implement mixins to allow specifying custom managers for list types.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@eamigo86 are you still happy to maintain graphene-django-extras as a separate library or would you like to see it merged into graphene-django? (it's a fantastic library btw!)

Hello @jkimbo, thank you very much for communicating with me. I am currently updating graphene-django-extras library, I just don't have much free time; besides I am also updating another related library graphene-django-subscriptions to work with latest Channels versions and then I plan to merge them both in just one library.

After this, I would be willing to integrate it with the main library.

Regards

I believe this should be merged into the main repo. It is essential to provide an exhaustive and atomic approach to using Django and GraphQL together. Graphene-Django should not depend on Relay, which is just one of many JavaScript client implementations of GraphQL, particularly for react. GraphQL is to be used in so many more environments, and people do not necessarily want to stick to those opinionated decisions. GraphQL has got a massive potential to be a leading API specification and major language and framework like Python and Django should be on top of it.

To put it simply: Relay sucks. Please help us avoid it.

The extension module looks pretty cool! Not sure if the suggestion of using graphene-django without Relay is an ultimately good decision, but one thing for sure is this extension module at least reduces the unnecessary efforts of defining a InpuObjectType and retyping all the fields when people try to map a Django model to an InputObjectType.

Really looking forward to seeing the extension module to integrate to graphene-django.

And thanks for the great job @eamigo86 ! :+1:

I highly recommend getting graphene-django-extras as functionalities in the core library. They are basic functionalities that should be maintained by the core team and open source community at large. Tying the graphene library so closely to the Relay client needs to be undone, and that would be a good first step.

Would be great to get the features of graphene-django-extras into graphene

I'm not seeing much activity here now so thought I'd bump this thread. To echo everyone else, this is awesome and the functionality is integral to a robust, production level graphql API. Seems like the community would really benefit from getting this integrated into the core graphene-django library.

@liliserf Good timing, I would agree

To clarify and reiterate though - what is everyone expecting to get in regards to using graphene-django without relay?

My aspirations is to be able to have id be the actual django ORM pk, not the type:id encoded via relay, though I'm getting mixed messages: Is that baked into GraphQL itself, or a relay thing?

Edit: Please correct me if I'm fundamentally misunderstanding something! Assume we have a public API - IDs that are encoded with object types out of the box. In the case of IDs, there's a huge timesaver for users to refer to real ID from the URL params in fields - without needing to btoa('ObjectType:dbid') in console.

Same needs here. I also want a nicely browsable api on top of an RDB. The node-edge interface is redundant and ugly with the RDB and without Relay, it's also easier for our front-end application to simply export the schema from the backend and set dynamically constructed types with it.

I can confirm. To this day I am looking for a solution that doesn't use relay. My major gripe is with the return fields: The ok field is totally omitted and the error field is morphed into errors. This leads to a lot of issues in consistency with regular graphene.Mutations.

But I could also use them being simpler.

Was this page helpful?
0 / 5 - 0 ratings