Giter Site home page Giter Site logo

graphene-django-extras's Introduction

Graphene-Django-Extras

Travis (.org) branch Codecov PyPI - Python Version PyPI PyPI - License PyPI - Downloads Code style: black

This package adds some extra functionalities to graphene-django to facilitate the graphql use without Relay:

  1. Allow pagination and filtering on Queries
  2. Allow defining DjangoRestFramework serializers based on Mutations.
  3. Allow using Directives on Queries and Fragments.

NOTE: Subscription support was moved to graphene-django-subscriptions.

Installation

For installing graphene-django-extras, just run this command in your shell:

pip install graphene-django-extras

Documentation

Extra functionalities

Fields:

  1. DjangoObjectField
  2. DjangoFilterListField
  3. DjangoFilterPaginateListField
  4. DjangoListObjectField (Recommended for Queries definition)

Mutations:

  1. DjangoSerializerMutation (Recommended for Mutations definition)

Types:

  1. DjangoListObjectType (Recommended for Types definition)
  2. DjangoInputObjectType
  3. DjangoSerializerType (Recommended for quick queries and mutations definitions)

Paginations:

  1. LimitOffsetGraphqlPagination
  2. PageGraphqlPagination

Queries and Mutations examples

This is a basic example of graphene-django-extras package use. You can configure global params for DjangoListObjectType classes pagination definitions on settings.py like this:

    GRAPHENE_DJANGO_EXTRAS = {
        'DEFAULT_PAGINATION_CLASS': 'graphene_django_extras.paginations.LimitOffsetGraphqlPagination',
        'DEFAULT_PAGE_SIZE': 20,
        'MAX_PAGE_SIZE': 50,
        'CACHE_ACTIVE': True,
        'CACHE_TIMEOUT': 300    # seconds
    }

1- Types Definition

from django.contrib.auth.models import User
from graphene_django_extras import DjangoListObjectType, DjangoSerializerType, DjangoObjectType
from graphene_django_extras.paginations import LimitOffsetGraphqlPagination

from .serializers import UserSerializer


class UserType(DjangoObjectType):
    class Meta:
        model = User
        description = " Type definition for a single user "
        filter_fields = {
            "id": ("exact", ),
            "first_name": ("icontains", "iexact"),
            "last_name": ("icontains", "iexact"),
            "username": ("icontains", "iexact"),
            "email": ("icontains", "iexact"),
            "is_staff": ("exact", ),
        }


class UserListType(DjangoListObjectType):
    class Meta:
        description = " Type definition for user list "
        model = User
        pagination = LimitOffsetGraphqlPagination(default_limit=25, ordering="-username") # ordering can be: string, tuple or list


class UserModelType(DjangoSerializerType):
    """ With this type definition it't necessary a mutation definition for user's model """

    class Meta:
        description = " User model type definition "
        serializer_class = UserSerializer
        pagination = LimitOffsetGraphqlPagination(default_limit=25, ordering="-username") # ordering can be: string, tuple or list
        filter_fields = {
            "id": ("exact", ),
            "first_name": ("icontains", "iexact"),
            "last_name": ("icontains", "iexact"),
            "username": ("icontains", "iexact"),
            "email": ("icontains", "iexact"),
            "is_staff": ("exact", ),
        }

2- You can to define InputTypes for use on mutations

from graphene_django_extras import DjangoInputObjectType


class UserInput(DjangoInputObjectType):
    class Meta:
        description = " User InputType definition to use as input on an Arguments class on traditional Mutations "
        model = User

3- You can define traditional mutations that use InputTypes or Mutations based on DRF serializers

import graphene
from graphene_django_extras import DjangoSerializerMutation

from .serializers import UserSerializer
from .types import UserType
from .input_types import UserInputType


class UserSerializerMutation(DjangoSerializerMutation):
    """
        DjangoSerializerMutation auto implement Create, Delete and Update functions
    """
    class Meta:
        description = " DRF serializer based Mutation for Users "
        serializer_class = UserSerializer


class UserMutation(graphene.Mutation):
    """
         On traditional mutation classes definition you must implement the mutate function

    """

    user = graphene.Field(UserType, required=False)

    class Arguments:
        new_user = graphene.Argument(UserInput)

    class Meta:
        description = " Graphene traditional mutation for Users "

    @classmethod
    def mutate(cls, root, info, *args, **kwargs):
        ...

4- Defining the Schema file

import graphene
from graphene_django_extras import DjangoObjectField, DjangoListObjectField, DjangoFilterPaginateListField,
DjangoFilterListField, LimitOffsetGraphqlPagination
from .types import UserType, UserListType, UserModelType
from .mutations import UserMutation, UserSerializerMutation


class Queries(graphene.ObjectType):
    # Possible User list queries definitions
    users = DjangoListObjectField(UserListType, description='All Users query')
    users1 = DjangoFilterPaginateListField(UserType, pagination=LimitOffsetGraphqlPagination())
    users2 = DjangoFilterListField(UserType)
    users3 = DjangoListObjectField(UserListType, filterset_class=UserFilter, description='All Users query')

    # Defining a query for a single user
    # The DjangoObjectField have a ID type input field, that allow filter by id and is't necessary to define resolve function
    user = DjangoObjectField(UserType, description='Single User query')

    # Another way to define a query to single user
    user1 = UserListType.RetrieveField(description='User List with pagination and filtering')

    # Exist two ways to define single or list user queries with DjangoSerializerType
    user_retrieve1, user_list1 = UserModelType.QueryFields(
        description='Some description message for both queries',
        deprecation_reason='Some deprecation message for both queries'
    )
    user_retrieve2 = UserModelType.RetrieveField(
        description='Some description message for retrieve query',
        deprecation_reason='Some deprecation message for retrieve query'
    )
    user_list2 = UserModelType.ListField(
        description='Some description message for list query',
        deprecation_reason='Some deprecation message for list query'
    )

class Mutations(graphene.ObjectType):
    user_create = UserSerializerMutation.CreateField(deprecation_reason='Some one deprecation message')
    user_delete = UserSerializerMutation.DeleteField()
    user_update = UserSerializerMutation.UpdateField()

    # Exist two ways to define mutations with DjangoSerializerType
    user_create1, user_delete1, user_update1 = UserModelType.MutationFields(
        description='Some description message for create, delete and update mutations',
        deprecation_reason='Some deprecation message for create, delete and update mutations'
    )

    user_create2 = UserModelType.CreateField(description='Description message for create')
    user_delete2 = UserModelType.DeleteField(description='Description message for delete')
    user_update2 = UserModelType.UpdateField(description='Description message for update')

    traditional_user_mutation = UserMutation.Field()

5- Directives settings

For use Directives you must follow two simple steps:

  1. You must add 'graphene_django_extras.ExtraGraphQLDirectiveMiddleware' to your GRAPHENE dict config on your settings.py:
# settings.py

GRAPHENE = {
    'SCHEMA_INDENT': 4,
    'MIDDLEWARE': [
        'graphene_django_extras.ExtraGraphQLDirectiveMiddleware'
    ]
}
  1. You must add the directives param with your custom directives to your schema definition. This module comes with some common directives for you, these directives allow to you format strings, numbers, lists, and dates (optional), and you can load like this:
# schema.py
from graphene_django_extras import all_directives

schema = graphene.Schema(
    query=RootQuery,
    mutation=RootMutation,
    directives=all_directives
)

NOTE: Date directive depends on dateutil module, so if you do not have installed it, this directive will not be available. You can install dateutil module manually:

pip install python-dateutil

or like this:

pip install graphene-django-extras[date]

That's all !!!

6- Complete Directive list

FOR NUMBERS
  1. FloorGraphQLDirective: Floors value. Supports both String and Float fields.
  2. CeilGraphQLDirective: Ceils value. Supports both String and Float fields.
FOR LIST
  1. ShuffleGraphQLDirective: Shuffle the list in place.
  2. SampleGraphQLDirective: Take a 'k' int argument and return a k length list of unique elements chosen from the taken list
FOR DATE
  1. DateGraphQLDirective: Take a optional 'format' string argument and format the date from resolving the field by dateutil module with the 'format' format. Default format is: 'DD MMM YYYY HH:mm:SS' equivalent to '%d %b %Y %H:%M:%S' python format.
FOR STRING
  1. DefaultGraphQLDirective: Take a 'to' string argument. Default to given value if None or ""
  2. Base64GraphQLDirective: Take a optional ("encode" or "decode") 'op' string argument(default='encode'). Encode or decode the string taken.
  3. NumberGraphQLDirective: Take a 'as' string argument. String formatting like a specify Python number formatting.
  4. CurrencyGraphQLDirective: Take a optional 'symbol' string argument(default="$"). Prepend the symbol argument to taken string and format it like a currency.
  5. LowercaseGraphQLDirective: Lowercase the taken string.
  6. UppercaseGraphQLDirective: Uppercase the taken string.
  7. CapitalizeGraphQLDirective: Return the taken string with its first character capitalized and the rest lowered.
  8. CamelCaseGraphQLDirective: CamelCase the taken string.
  9. SnakeCaseGraphQLDirective: SnakeCase the taken string.
  10. KebabCaseGraphQLDirective: SnakeCase the taken string.
  11. SwapCaseGraphQLDirective: Return the taken string with uppercase characters converted to lowercase and vice versa.
  12. StripGraphQLDirective: Take a optional 'chars' string argument(default=" "). Return the taken string with the leading and trailing characters removed. The 'chars' argument is not a prefix or suffix; rather, all combinations of its values are stripped.
  13. TitleCaseGraphQLDirective: Return the taken string title-cased, where words start with an uppercase character and the remaining characters are lowercase.
  14. CenterGraphQLDirective: Take a 'width' string argument and a optional 'fillchar' string argument(default=" "). Return the taken string centered with the 'width' argument as new length. Padding is done using the specified 'fillchar' argument. The original string is returned if 'width' argument is less than or equal to taken string length.
  15. ReplaceGraphQLDirective: Take two strings arguments 'old' and 'new', and a optional integer argument 'count'. Return the taken string with all occurrences of substring 'old' argument replaced by 'new' argument value. If the optional argument 'count' is given, only the first 'count' occurrences are replaced.

7- Queries's examples

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

  allUsers1(lastName_Iexact:"Doe", limit:5, offset:0){
    id
    username
    firstName
    lastName
  }

  allUsers2(firstName_Icontains: "J"){
    id
    username
    firstName
    lastName
  }

  user(id:2){
    id
    username
    firstName
  }

  user1(id:2){
    id
    username
    firstName
  }
}

8- Mutations's examples

mutation{
  userCreate(newUser:{username:"test", password:"test*123"}){
    user{
      id
      username
      firstName
      lastName
    }
    ok
    errors{
      field
      messages
    }
  }

  userDelete(id:1){
    ok
    errors{
      field
      messages
    }
  }

  userUpdate(newUser:{id:1, username:"John"}){
    user{
      id
      username
    }
    ok
    errors{
      field
      messages
    }
  }
}

9- Directives's examples

Let's suppose that we have this query:

query{
    allUsers{
        result{
            id
            firstName
            lastName
            dateJoined
            lastLogin
        }
    }
}

And return this data:

{
  "data": {
    "allUsers": {
      "results": [
        {
            "id": "1",
            "firstName": "JOHN",
            "lastName": "",
            "dateJoined": "2017-06-20 09:40:30",
            "lastLogin": "2017-08-05 21:05:02"
        },
        {
            "id": "2",
            "firstName": "Golden",
            "lastName": "GATE",
            "dateJoined": "2017-01-02 20:36:45",
            "lastLogin": "2017-06-20 10:15:31"
        },
        {
            "id": "3",
            "firstName": "Nike",
            "lastName": "just do it!",
            "dateJoined": "2017-08-30 16:05:20",
            "lastLogin": "2017-12-05 09:23:09"
        }
      ]
    }
  }
}

As we see, some data it's missing or just not have the format that we like it, so let's go to format the output data that we desired:

query{
    allUsers{
        result{
            id
            firstName @capitalize
            lastName @default(to: "Doe") @title_case
            dateJoined @date(format: "DD MMM YYYY HH:mm:SS")
            lastLogin @date(format: "time ago")
        }
    }
}

And we get this output data:

{
  "data": {
    "allUsers": {
      "results": [
        {
            "id": "1",
            "firstName": "John",
            "lastName": "Doe",
            "dateJoined": "20 Jun 2017 09:40:30",
            "lastLogin": "4 months, 12 days, 15 hours, 27 minutes and 58 seconds ago"
        },
        {
            "id": "2",
            "firstName": "Golden",
            "lastName": "Gate",
            "dateJoined": "02 Jan 2017 20:36:45",
            "lastLogin": "5 months, 28 days, 2 hours, 17 minutes and 53 seconds ago"
        },
        {
            "id": "3",
            "firstName": "Nike",
            "lastName": "Just Do It!",
            "dateJoined": "30 Aug 2017 16:05:20",
            "lastLogin": "13 days, 3 hours, 10 minutes and 31 seconds ago"
        }
      ]
    }
  }
}

As we see, the directives are an easy way to format output data on queries, and it's can be put together like a chain.

List of possible date's tokens: "YYYY", "YY", "WW", "W", "DD", "DDDD", "d", "ddd", "dddd", "MM", "MMM", "MMMM", "HH", "hh", "mm", "ss", "A", "ZZ", "z".

You can use this shortcuts too:

  1. "time ago"
  2. "iso": "YYYY-MMM-DDTHH:mm:ss"
  3. "js" or "javascript": "ddd MMM DD YYYY HH:mm:ss"

Change Log

v1.0.0

  1. Added support to Django 4.x
  2. Removed support for Django versions < 3.2

v0.5.1

  1. Update dependencies

v0.5.0

  1. Upgrade to graphene v3

v0.4.8

  1. Upgrade graphene-django dependency to version == 2.6.0.

v0.4.6

  1. Upgrade graphql-core dependency to version >= 2.2.1.
  2. Upgrade graphene dependency to version >= 2.1.8.
  3. Upgrade graphene-django dependency to version >= 2.5.0.
  4. Upgrade django-filter dependency to version >= 2.2.0.
  5. Fixed bug 'DjangoSerializerOptions' object has no attribute 'interfaces' after update to graphene==2.1.8.
  6. The tests were refactored and added some extra tests for DjangoSerializerType.

v0.4.5

  1. Fixed compatibilities issues to use graphene-django>=2.3.2.
  2. Improved code quality and use Black code format.
  3. Fixed minor bug with "time ago" date directive.

v0.3.7

  1. Improved DjangoListType and DjangoObjectType to share the filterset_class between the two class.

v0.3.6

  1. Improve DjangoSerializerMutation resolvers.

v0.3.5

  1. Fixed minor bug on ExtraGraphQLDirectiveMiddleware.
  2. Fixed error with DRF 3.8 Compatibility.
  3. Updated List's Fields to pass info.context to filterset as request, this allow filtering by request data.
  4. Added new feature to ordering paginated queries.

v0.3.4-alpha2

  1. Fixed minor bug on DjangoListObjectType.

v0.3.4-alpha1

  1. Added filterset_class to the listing types as default filter.
  2. Changed getOne by RetrieveField on DjangoListObjectType.

v0.3.3

  1. Added filterset_class to DjangoObjectType.
  2. Fixed minor bug on factory_types function.

v0.3.3-alpha1

  1. Fixed minor bug on queryset_factory function.

v0.3.2

  1. Updated Date directive format function for better string format combinations.
  2. Updated custom Time, Date and DateTime base types to be used with Date directive.
  3. Fixed bug with caching Introspection queries on ExtraGraphQLView.

v0.3.1

  1. Fixed bug with default Date directive format.

v0.3.0

  1. Added Binary graphql type. A BinaryArray is used to convert a Django BinaryField to the string form.
  2. Added 'CACHE_ACTIVE' and 'CACHE_TIMEOUT' config options to GRAPHENE_DJANGO_EXTRAS settings for activate cache and define a expire time. Default values are: CACHE_ACTIVE=False, CACHE_TIMEOUT=300 (seconds). Only available for Queries.
  3. Updated Date directive for use with Django TimeField, DateField, and DateTimeField.
  4. Updated ExtraGraphQLView and AuthenticatedGraphQLView to allow use subscription requests on graphene-django >=2.0
  5. Updated setup dependence to graphene-django>=2.0.

v0.2.2

  1. Fixed performance bug on some queries when request nested ManyToMany fields.

v0.2.1

  1. Fixed bug with default PaginationClass and DjangoFilterPaginateListField.

v0.2.0

  1. Added some useful directives to use on queries and fragments.
  2. Fixed error on DjangoFilterPaginateListField resolve function.

v0.1.6

  1. Fixed bug on create and update function on serializer mutation.

v0.1.3

  1. Fixed minors bugs.

v0.1.2

  1. Added ok field and errors field to DjangoSerializerType like on DjangoSerializerMutation.
  2. Added possibility of filtering in those queries fields that return a list of objects.
  3. Updated DRF compatibility.
  4. Fixed bug with filters when use global DEFAULT_PAGINATION_CLASS.

v0.1.1

  1. Fixed error with JSONField reference on Django==1.8.x installations.

v0.1.0

  1. Added DjangoSerializerType for quick Django's models types definition (See documentation).
  2. Moved support for Subscriptions to graphene-django-subscriptions packages for incompatibility with graphene-django>=2.0.
  3. Fixed bug on DjangoFilterPaginateListField's pagination.

v0.1.0-alpha12

  1. Added new settings param: MAX_PAGE_SIZE, to use on GRAPHENE_DJANGO_EXTRAS configuration dict for better customize DjangoListObjectType's pagination.
  2. Added support to Django's field: GenericRel.
  3. Improve model's fields calculation for to add all possible related and reverse fields.
  4. Improved documentation translation.

v0.1.0-alpha11

  1. Improved ordering for showed fields on graphqli's IDE.
  2. Added better descriptions for auto generated fields.

v0.1.0-alpha10

  1. Improve converter.py file to avoid create field for auto generate OneToOneField product of an inheritance.
  2. Fixed bug in Emun generation for fields with choices of model inheritance child.

v0.1.0-alpha9

  1. Fixed bug on GenericType and GenericInputType generations for Queries list Type and Mutations.

v0.1.0-alpha6

  1. Fixed with exclude fields and converter function.

v0.1.0-alpha5

  1. Updated dependencies to graphene-django>=2.0.
  2. Fixed minor bugs on queryset_builder performance.

v0.1.0-alpha4

  1. Add queryset options to DjangoListObjectType Meta class for specify wanted model queryset.
  2. Add AuthenticatedGraphQLView on graphene_django_extras.views for use 'permission', 'authorization' and 'throttle' classes based on the DRF settings. Special thanks to @jacobh for this comment

v0.1.0-alpha3

  1. Fixed bug on subscriptions when not specified any field in "data" parameter to bean return on notification message.

v0.1.0-alpha2

  1. Fixed bug when subscribing to a given action (create, update or delete).
  2. Added intuitive and simple web tool to test notifications of graphene-django-extras subscription.

v0.1.0-alpha1

  1. Added support to multiselect choices values for models.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.
  4. Fixed minors bugs.

v0.0.4

  1. Fix error on DateType encode.

v0.0.3

  1. Implement custom implementation of DateType for use converter and avoid error on Serializer Mutation.

v0.0.2

  1. Changed dependency of DRF to 3.6.4 on setup.py file, to avoid an import error produced by some changes in new version of DRF=3.7.0 and because DRF 3.7.0 dropped support to Django versions < 1.10.

v0.0.1

  1. Fixed bug on DjangoInputObjectType class that refer to unused interface attribute.
  2. Added support to create nested objects like in DRF, it's valid to SerializerMutation and DjangoInputObjectType, only is necessary to specify nested_fields=True on its Meta class definition.
  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.
  4. Fixed others minors bugs.

v0.0.1-rc.2

  1. Make queries pagination configuration is more friendly.

v0.0.1-rc.1

  1. Fixed a bug with input fields in the converter function.

v0.0.1-beta.10

  1. Fixed bug in the queryset_factory function because it did not always return a queryset.

v0.0.1-beta.9

  1. Remove hard dependence with psycopg2 module.
  2. Fixed bug that prevented use queries with fragments.
  3. Fixed bug relating to custom django_filters module and ordering fields.

v0.0.1-beta.6

  1. Optimizing imports, fix some minors bugs and working on performance.

v0.0.1-beta.5

  1. Repair conflict on converter.py, by the use of get_related_model function with: OneToOneRel, ManyToManyRel and ManyToOneRel.

v0.0.1-beta.4

  1. First commit.

graphene-django-extras's People

Contributors

alecrosenbaum avatar atomboulian avatar bemcculley avatar bernardopericacho avatar cassiemeharry avatar dreglad avatar eamigo86 avatar ellimilial avatar ernesto-perez-amigo avatar fabienheureux avatar jackton1 avatar jamesmcnamara avatar jonafato avatar jozefsukovsky avatar jstacoder avatar jzelez avatar keithhackbarth avatar kennand avatar ldynia avatar leiserfg avatar mandersennano avatar ollamh avatar ouradze avatar s3rius avatar slurms avatar tadeo avatar tinyjin avatar washeck avatar zagrebelin avatar zulupro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphene-django-extras's Issues

Work together with django-hvad

Is there any good way to start working with django-hvad?
What i found: it create translations_model attribute in _meta of model. So, minimum we must include fields from this model in _meta.fields.
In my opinion, there is no obvious way to add fields from this model. Of course, we can do it manually for all required fields and resolve them from this model, but i think that it is not pretty.
Also, i tried monkeypatch get_model_fields something like this

old_get_model_fields = converter.get_model_fields
def get_model_fields(model):
    fields = old_get_model_fields(model)
    if hasattr(model._meta, 'translations_model'):
        fields += old_get_model_fields(model._meta.translations_model)
    return fields
converter.get_model_fields = get_model_fields

and then i can get fields of translation model and use it in query as normal. But i do not check something like mutations and monkeypatching also is not good way.
So, how make it more clearly?

TypeError when using GenericForeignKey

'<' not supported between instances of 'GenericForeignKey' and 'DateTimeField'.

The exception is triggered here : https://github.com/eamigo86/graphene-django-extras/blob/master/graphene_django_extras/utils.py#L22 when using a type like this :

class CommentType(DjangoObjectType):
    class Meta:
        model = Comment

For a model like this:

class Comment(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE,)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)
    body = models.TextField()

    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    updated_at = models.DateTimeField(auto_now=True, editable=False)

Need to pin packages

graphql-python/graphql-core#201

Hi, our team is experiencing issues with graphql-core 2.1 - all queries are erroring if they have a blank value in any enums in the data response. In order to stop this side effect for now, and in accordance with good practice of pinning versions until testing new versions, please pin the packages.

I have a PR with this fix here: #54
Looks like you have another PR with a similar request here: #53

Thanks! The sooner this is fixed the sooner I can help more.

Include and skip missing

When using the provided get_all_directives function, the default skip and include directives are discarded #50

Get amount of filtered objects returned by DjangoFilterPaginateListField (before pagination)

Hello, is there a direct (clear) way for accessing amount of objects returned by DjangoFilterPaginateListField?
I need amount of filtered objects before they are split to pages.
Example: if I have 10 objects in db, I filter them by some key and I get 7 objects. I will be paginating this result but I still need the number (7) of objects returned by filter.

Thank you!

Expose a way to assign a custom FilterSet to a DjangoObjectType

In django-filter you can declare custom FilterSet, and in graphene-django(-extras) you can use it in a custom resolver, but there is not a way to use it in every point the type is referred. I think that it can be resolved by simply adding a filter_set_class parameter to the DjangoObjectType and using it as default value instead of None in the call to DjangoFilterListField in converter.py.

iso8601 with django-graphene 2.2

In django-graphene 2.2 iso8601 was removed from requirements
graphql-python/graphene-django@a59f41b

But graphene-django-extras still relies on it, and dont have a requirement for it which will lead into error

Traceback (most recent call last):
File "/pipeline/source/back/venv/lib/python3.6/site-packages/graphene_django_extras/base_types.py", line 13, in
import iso8601
ModuleNotFoundError: No module named 'iso8601'

DjangoSerializerMutation update mutation with unique model fields

With the latest 0.13 update, I ran into an issue with the DjangoSerializerMutation update mutation.

I have a model that defines a unique value...e.g.

class User(models.Model):
    name = models.CharField(max_length=80, blank=False, null=False, unique=True)

When the call to update is made there is a call to full_clean() on the model with the validated serialized data.

cls._meta.model(**serializer.validated_data).full_clean()

This will fail because it will validate that the name field is unique for the model.

The quick fix is to change the line to:

cls._meta.model(**serializer.validated_data).full_clean(validate_unique=False)

or to make the call to full_clean optional for the mutation.

DjangoInputObjectType problem and nested serializers

Hi. Thanks for implementing this stuff. I'm playing around with graphene some time and get piss off on things that were easy in DRF.
I've got problem with implementing DjangoInputObjectType.
This is what i get after just copy-pasting code example:

super(InputObjectType, cls).__init_subclass_with_meta__(_meta=_meta, interfaces=interfaces, **options) TypeError: __init_subclass_with_meta__() got an unexpected keyword argument 'interfaces'

Also, i was trying to implement nested serializer like this:
http://www.django-rest-framework.org/api-guide/serializers/#writable-nested-representations
but it doesn't seem to do anything. Is it even posible?

How can I use the decorators in django-graphql-jwt ?

I am using django-graphql-jwt and it exposes a couple of decorators such as @login_required that are used to decorate the resolver functions to restrict their access.

How can I use such decorators with DjangoSerializerType, .ListField(), RetrieveField(), etc. I only want to restrict access for certain fields such as:

import graphene
from .types import UserType


class Query(graphene.ObjectType):
    users = UserType.ListField()
    user = UserType.RetrieveField()


class Mutation(graphene.ObjectType):
    update_user = UserType.UpdateField()

Resolvers are not defined in these cases. Any suggestions on how to make it work with django-graphql-jwt ?

How To Custom Field Filter?

I'm using django-unixtimestampfield, and some error is raised.

File "/Users/keyeMyria/envi/for_python/env_demoni/lib/python3.6/site-packages/graphene_django_extras/fields.py", line 226, in __init__ self.filterset_class = get_filterset_class(filterset_class, **meta) File "/Users/keyeMyria/envi/for_python/env_demoni/lib/python3.6/site-packages/graphene_django_extras/filters/filter.py", line 12, in get_filterset_class return custom_filterset_factory(**meta) File "/Users/keyeMyria/envi/for_python/env_demoni/lib/python3.6/site-packages/graphene_django_extras/filters/filter.py", line 42, in custom_filterset_factory 'Meta': meta_class File "/Users/keyeMyria/envi/for_python/env_demoni/lib/python3.6/site-packages/django_filters/filterset.py", line 71, in __new__ new_class.base_filters = new_class.get_filters() File "/Users/keyeMyria/envi/for_python/env_demoni/lib/python3.6/site-packages/django_filters/filterset.py", line 345, in get_filters filters[filter_name] = cls.filter_for_field(field, field_name, lookup_expr) File "/Users/keyeMyria/envi/for_python/env_demoni/lib/python3.6/site-packages/django_filters/filterset.py", line 377, in filter_for_field ) % (cls.__name__, field_name, lookup_expr, field.__class__.__name__) AssertionError: UserFilterSet resolved field 'created_at' with 'exact' lookup to an unrecognized field type UnixTimeStampField. Try adding an override to 'Meta.filter_overrides'. See: https://django-filter.readthedocs.io/en/master/ref/filterset.html#customise-filter-generation-with-filter-overrides

`

from unixtimestampfield.fields import UnixTimeStampField

class TimestampMixin(models.Model):
created_at = UnixTimeStampField(db_column="crt_at", use_numeric=True, auto_now_add=True, editable=False)
updated_at = UnixTimeStampField(db_column="udt_at", use_numeric=True, auto_now=True)

class Meta:
    abstract = True

`

Feature Request/Estimate - nested_fields as a tuple instead of a Boolean

Hi @eamigo86 , thanks for this great library!!!

Can I make a suggestion?

For our mutations, we have some fields that we would like to pass the Foreign Key ID as an update instead of creating a new object, and some fields that we would like to create a new object through a nested structure. In other words, for some fields we want the result of nested_fields = True (nested object creation), and other fields we want the result of nested_fields = False (IDs passed in).

Do you think it would be better instead of passing a boolean for the nested_fields, pass a list of fields, to allow only nesting the fields that you specify in DjangoSerializerMutation? Similar to only_fields?

For example, below for the Foreign Key of Project and the related_name of "products":

class RoomSerializerMutation(DjangoSerializerMutation):

    class Meta:
        serializer_class = RoomSerializer
        nested_fields = ("project", "products", )

All other Foreign Key fields (including Many to Many, One to One, etc.) would take in an ID.

If you agree, I also wanted to get a general understanding of how much time that would take as well. I forked the repo and have been experimented with it to see what I can do to provide this feature myself, but I'm not very confident that I can do it since the codebase is so large and I am just learning it.

Thanks again!

[Improvement] Partial Update with serializer_class

Hi,

First, thanks for this amazing lib !

I'm wondering if the partial update would be accessible through the class DjangoSerializerMutation ?

At the moment, I use to bypass this issue, the following code:

import functools

def partialclass(cls, *args, **kwds):
	class NewCls(cls):
		__init__ = functools.partialmethod(cls.__init__, *args, **kwds)

	return NewCls

and then

class MediaFeedMutation(DjangoSerializerMutation):
	class Meta:
		serializer_class = partialclass(MediaFeedSerializer, partial=True)

ImportError: cannot import name 'QUERY_TERMS'

After last update deps for graphene-django-extras from 0.3.8a9
to 0.3.8a10 via renovate my CI test now throwing a error on launching backend. Work fine with 0.3.8a9.



Traceback (most recent call last):
  File "manage.py", line 15, in <module>
    execute_from_command_line(sys.argv)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/management/base.py", line 316, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/management/base.py", line 350, in execute
    self.check()
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/management/base.py", line 379, in check
    include_deployment_checks=include_deployment_checks,
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/management/commands/migrate.py", line 60, in _run_checks
    issues.extend(super()._run_checks(**kwargs))
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/management/base.py", line 366, in _run_checks
    return checks.run_checks(**kwargs)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/checks/registry.py", line 71, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/checks/urls.py", line 40, in check_url_namespaces_unique
    all_namespaces = _load_all_namespaces(resolver)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/core/checks/urls.py", line 57, in _load_all_namespaces
    url_patterns = getattr(resolver, 'url_patterns', [])
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/utils/functional.py", line 37, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/urls/resolvers.py", line 533, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/utils/functional.py", line 37, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django/urls/resolvers.py", line 526, in urlconf_module
    return import_module(self.urlconf_name)
  File "/pipeline/source/back/venv/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/pipeline/source/back/hamachi_gql/urls.py", line 22, in <module>
    from core.gq.views import HamachiGraphQLView
  File "/pipeline/source/back/core/gq/views.py", line 1, in <module>
    from graphene_django_extras.views import AuthenticatedGraphQLView
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/graphene_django_extras/__init__.py", line 5, in <module>
    from .fields import DjangoObjectField, DjangoFilterListField, DjangoFilterPaginateListField, \
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/graphene_django_extras/fields.py", line 7, in <module>
    from graphene_django.filter.utils import get_filtering_args_from_filterset
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/graphene_django/filter/utils.py", line 3, in <module>
    from .filterset import custom_filterset_factory, setup_filterset
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/graphene_django/filter/filterset.py", line 5, in <module>
    from django_filters import Filter, MultipleChoiceFilter
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django_filters/__init__.py", line 7, in <module>
    from .filterset import FilterSet
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django_filters/filterset.py", line 15, in <module>
    from .filters import (
  File "/pipeline/source/back/venv/lib/python3.6/site-packages/django_filters/filters.py", line 9, in <module>
    from django.db.models.sql.constants import QUERY_TERMS
ImportError: cannot import name 'QUERY_TERMS'

My req file

beautifulsoup4==4.6.3
dj-database-url==0.5.0
Django==2.1
django-admin-interface==0.6.3
django-anymail==3.0
django-colorfield==0.1.15
django-cors-headers==2.4.0
django-extensions==2.1.0
django-fsm==2.6.0
django-graphiql-debug-toolbar==0.1.1
django-graphql-extensions==0.0.4
django-graphql-jwt==0.1.10
django-mail-templated-simple==3.2
graphene==2.1.3
graphene-django==2.1.0
graphene-django-extras==0.3.8a10
Jinja2==2.10
pinax-stripe==4.4.0
psycopg2==2.7.5
pyjade==4.0.0
python-dateutil==2.7.3
python-decouple==3.1
PyYAML==3.13
requests==2.19.1
six==1.11.0
stringcase==1.2.0
matplotlib==2.2.3
zappa==0.46.2
zappa-django-utils==0.4.0
django-storages==1.6.6
boto3==1.7.75
faker==0.9.0
raven==6.9.0
django-constance==2.2.0
django-picklefield==1.0.0
attrs==18.1.0
simplejson==3.16.0
phonenumbers==8.9.10

Dumb question for __init_subclass_with_meta__

Hi Sir,
Much appreciate for the lib you are working on! i like it very much.
I wonder if you can give me some guidance on how init_subclass_with_meta works,
i keep investigating in the source code and other examples but still can't figure it out.

in this method:

    def __init_subclass_with_meta__(cls, serializer_class=None, only_fields=(), exclude_fields=(),
                                    save_resolver=None, delete_resolver=None,
                                    input_field_name=None, output_field_name=None, description='',
                                    nested_fields=False, **options):

        if not serializer_class:
            raise Exception('serializer_class is required on all DjangoSerializerMutation')

the point confused me is I didn't find anywhere that init_subclass_with_meta get called as a classmethod in my code,
and never manually pass serializer_class into this function, wonder how does line if not serializer_class: working properly? why it can automatically detect i assigned serializer_class in my Meta subclass in which inherited from DjangoSerializerMutation class?

much appreciated!

resolve_[field_name] not working for DjangoListObjectField

I've defined a simple DjangoListObjectType with just a model and pagination attributes in the Meta and when added to Query with DjangoListObjectField I can't get the resolve_[field_name] method called.
Is this an intended behavior? How can I intervene the list/queryset? I wanted to filter out some elements.
Just as a reference of my intention:

class Product(models.Model):
    name = models.CharField(max_length=255)
    discontinued = models.BooleanField(default=False)


class ProductListType(DjangoListObjectType):
    class Meta:
        model = Product
        pagination = LimitOffsetGraphqlPagination()


class Query(graphene.ObjectType):
    all_products = DjangoListObjectField(ProductListType)

   def resolve_all_products(self, info, **kwargs):
      qs = Product.objects.all()
      some_parameter = kwargs.get('some_parameter')
      if some_parameter is not None:
          qs.exclude(discontinued=True)
      return qs

Thanks a lot!

Objects in results array should not be nullable

This a minor but annoying issue when you use a front end tool like Apollo because it results in weaker typing.
It affects graphene-django as well, but I have more confidence in graphene-django-extras to fix it ! :-)
Here it is: results should not be of the form results: [UserType] but results: [UserType!] since returned objects in the array can't be null.
I don't know if it's somehow related, but the same issues applies to related objects resulting from foreign keys or M2M.

creating nested objects like in DRF

Hello @eamigo86,

I'm having some trouble with creating nested objects like in DRF as explain in your post https://github.com/graphql-python/graphene-django/issues/274#issuecomment-335191042

Here's what I got in serializers.py:

class BookingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Booking
        fields = (
            'status'
        )

class OtherBookingSerializer(serializers.ModelSerializer):
    booking = BookingSerializer()

    class Meta:
        model = OtherBooking
        fields = (
            'id',
            'first_name',
            'last_name',
            'sex',
            'birthday',
            'language',
            'emergency',
            'booking'
        )

    def create(self, validated_data):
        booking_data = validated_data.pop('booking')
        other_booking = OtherBooking.objects.create(**validated_data)
        Booking.objects.create(otherbooking=other_booking, **booking_data)
        return other_booking`

then in GraphQL, following this example : https://user-images.githubusercontent.com/11740026/31345324-090bfc78-acdb-11e7-9194-ad2bb4f8fdaa.png, it says that field 'booking' is required; but when I put it in my query, it says "unknown field"...

for info, here is what I got on schema.py:

class BookingParticipantStatusSerializerMutation(DjangoSerializerMutation):
    """
        DjangoSerializerMutation auto implement Create, Delete and Update functions
    """
    class Meta:
        description = " DRF serializer based Mutation for Users "
        serializer_class = OtherBookingSerializer
        nested_fields = True

class Mutations(graphene.ObjectType):

    create_booking_participant_status = BookingParticipantStatusSerializerMutation.CreateField()
    update_booking_participant_status = BookingParticipantStatusSerializerMutation.UpdateField()`

I'm struggling to find where the problem comes from.
Any help would be much appreciated!

Thanks in advance!

(Custom) Django filters not applied

In fields.py in the method list_resolver (around line 227), I see that filterset_class is commented out and not used at all.

This prevents one from applying custom filters from django_filter. For instance, I have the following filter:

class ObjectFilter(django_filters.FilterSet):
    class Meta:
        model = models.Object
        fields = ["id"]

    # sort is not a field on models.Object, it's just a name I have chosen.
    sort = django_filters.OrderingFilter(fields=(
        'id', 'created',
    ))

The problem is that sort is not a field that exists on the Object model, but queryset_factory tries to apply this one in the filter(**kwargs) method of a model.

This generates errors such as:

graphql.error.located_error.GraphQLLocatedError: Cannot resolve keyword 'sort' into field. Choices are: id, created

So it should apply the filterset class, and ensure that non-model fields are not passed to the filter(**kwargs) method.

support auto_camelcase=False

I was happy to learn that graphene added support for turning off camel casing field names.

schema = graphene.Schema(query=Query, auto_camelcase=False)

It all seems to work except for DjangoListObjectType has totalCount. I tried changing it to total_count in the code and was hoping it would get auto-camel cased to totalCount or not depending on the auto_camelcase setting but it didn't.

I don't know the code well enough to provide a PR. Is this something that could be supported?

Fragments not working

First of all: thank you for contributing this! Django is great, GraphQL is great, but Relay was too much for me.

I noticed that queries with fragments are not yet supported. For instance:

query Objects {
  object {
    results(offset: 0, limit: 20) {
      ...ObjectFragment
    }
    totalCount
  }
}

fragment ObjectFragment on ObjectType {
  id
  __typename
}

I have worked around the issue by changing utils.py on line 105, since it looks like that method only optimizes the query:

...
elif hasattr(field, 'selection_set') and field.selection_set:
...

I found something that might help here. See the get_fields method.

Here is a traceback:

Traceback (most recent call last):
  File "/env/lib/python3.6/site-packages/graphql/execution/executor.py", line 191, in resolve_or_error
    return executor.execute(resolve_fn, source, info, **args)
  File "/env/lib/python3.6/site-packages/graphql/execution/executors/sync.py", line 7, in execute
    return fn(*args, **kwargs)
  File "/env/lib/python3.6/site-packages/graphene_django/debug/middleware.py", line 56, in resolve
    promise = next(root, info, **args)
  File "/env/lib/python3.6/site-packages/graphql/execution/middleware.py", line 57, in make_it_promise
    return Promise.resolve(next(*a, **b))
  File "/env/lib/python3.6/site-packages/graphene_django_extras/fields.py", line 229, in list_resolver
    qs = queryset_factory(manager, info.field_asts, filtering_args, **kwargs)
  File "/env/lib/python3.6/site-packages/graphene_django_extras/utils.py", line 131, in queryset_factory
    select_related, prefetch_related = recursive_params(fields_asts[0].selection_set, available_related_fields, select_related, prefetch_related)
  File "/env/lib/python3.6/site-packages/graphene_django_extras/utils.py", line 106, in recursive_params
    a, b = recursive_params(field.selection_set, available_related_fields, select_related, prefetch_related)
  File "/env/lib/python3.6/site-packages/graphene_django_extras/utils.py", line 105, in recursive_params
    elif field.selection_set:
AttributeError: 'FragmentSpread' object has no attribute 'selection_set'

DjangoInputObjectType doesn't allow overwriting model fields

Problem

I'm using custom logic for input validation of a model field (like, validate it as a string and then convert it into UUID v.5 before save). So, I have a model and schema defined below:

# models.py
class TestModel(models.Model):
    test_field = models.UUIDField()

# schema.py
class TestModelInput(DjangoInputObjectType):
    test_field = graphene.String(required=True)

    class Meta:
        model = TestModel

class CreateTestModel(graphene.Mutation):

    class Input:
        data = TestModelInput()
    
    @staticmethod
    def mutate(root, info, data):
        # here I convert and save my model

What I expect from printing graphql schema:

input TestModelInput {
  test_field: String!
  ...

What I get:

input TestModelInput {
  test_field: UUID!
  ...

Solution

I found out that in the graphene_django_extras/types.py:182 this line

super(InputObjectType, cls).__init_subclass_with_meta__(_meta=_meta, **options)

means that the method InputObjectType.__init_subclass_with_meta__ itself is not being called and this class attributes are not being converted into fields. The line should be changed into

super(DjangoInputObjectType, cls).__init_subclass_with_meta__(_meta=_meta, **options)

to run correctly.

P.S. Thanks for the great package! It saved me lots of work =)

DRF 3.8 Compatibility

Upgrading to the recently released DRF 3.8.x series broke compatibiliy with graphene-django-extras

In DRF 3.8, rest_framework.compat._resolve_model function has been removed, but it is being imported by utils.py#L15.

I don't feel like opining on the "correct" solution, but since _resolve_model is only used when Django < 1.9, then making it not only 'conditionally used' but also 'conditionally imported' would make sense to me.

Warining: Abstract type is deprecated

I'm getting this warning when runing my server


/Users/sgaseretto/.virtualenvs/graphene/lib/python3.6/site-packages/graphene/types/abstracttype.py:9: DeprecationWarning: Abstract type is deprecated, please use normal object inheritance instead.
See more: https://github.com/graphql-python/graphene/blob/2.0/UPGRADE-v2.0.md#deprecations
  "Abstract type is deprecated, please use normal object inheritance instead.\n"
/Users/sgaseretto/.virtualenvs/graphene/lib/python3.6/site-packages/graphene/types/abstracttype.py:9: DeprecationWarning: Abstract type is deprecated, please use normal object inheritance instead.
See more: https://github.com/graphql-python/graphene/blob/2.0/UPGRADE-v2.0.md#deprecations
  "Abstract type is deprecated, please use normal object inheritance instead.\n"
[14/Mar/2018 20:02:27] "POST /graphql/ HTTP/1.1" 200 72700


Any idea of how to avoid it?

Limit the number of subresults of a list

Hi,

I'm wondering if it's possible to limit the number of subresults as on the example:
https://graphql.org/learn/pagination/
Like

query{
  allBrand(brand:"3715ad6b"){
    results{
      name,
      feeds(limit: 2){
        media{
           image @resize(width:30)          
        }
      }
    }
}

or

query{
  allBrand(brand:"3715ad6b"){
    results{
      name,
      feeds(first: 2){
        media{
           image @resize(width:30)          
        }
      }
    }
}

Default `DEFAULT_PAGINATION_CLASS` failed

Hey! Thanks for this package ๐Ÿ‘

I got exception on using DEFAULT_PAGINATION_CLASS.

settings.py

GRAPHENE_DJANGO_EXTRAS = {
    'DEFAULT_PAGINATION_CLASS': 'graphene_django_extras.paginations.LimitOffsetGraphqlPagination',
    'DEFAULT_PAGE_SIZE': 50,
    'MAX_PAGE_SIZE': 100
}

I'm using DjangoFilterPaginateListField without pagination parameter:

# types.py
class PositionType(DjangoObjectType):
    class Meta:
        model = models.Position
        filter_fields = {
            'name': ['exact', 'icontains', 'istartswith'],
            'id': ['exact']
        }
# schema.py
class Query(ObjectType):
    positions = DjangoFilterPaginateListField(types.PositionType)

Traceback:

File "/usr/local/lib/python3.6/site-packages/graphene_django_extras/fields.py", line 132, in __init__
    ).format(pagination)
AssertionError: You need to pass a valid DjangoGraphqlPagination in DjangoFilterPaginateListField, received "<class 'graphene_django_extras.paginations.pagination.LimitOffsetGraphqlPagination'>".

graphene_django_extras/fields.py

isinstance(pagination, BaseDjangoGraphqlPagination) on 142 line.

Code from sources:

pagination = pagination or graphql_api_settings.DEFAULT_PAGINATION_CLASS
if pagination is not None:
    assert isinstance(pagination, BaseDjangoGraphqlPagination), (
        'You need to pass a valid DjangoGraphqlPagination in DjangoFilterPaginateListField, received "{}".'
    ).format(pagination)

Traditional Methods for displaying calculated field not working.

Very possible that I'm missing something, but my model contains this property:

    @property
    def name_string(self):
        return '%s %s %s' % (
            self.model_year,
            self.model_name,
            self.trim
        )

Serializer:

class VehicleSerializer(serializers.ModelSerializer):
    name_string = serializers.ReadOnlyField()

    class Meta:
        model = Vehicle
        fields = '__all__'
        read_only_fields = ('name_string')

SerializerType:

class VehicleModelType(DjangoSerializerType):
    class Meta:
        description = "Vehicle Model Type Definition"
        serializer_class = VehicleSerializer
        pagination = LimitOffsetGraphqlPagination(default_limit=25)
        filter_fields = {
            'stock': ['exact', ],
            'active': ['exact', ],
        }

However, name_string or nameString isn't an option when querying. Not sure if GDE looks for them, or what I need to do?

Custom Resolver for DjangoFilterPaginateListField/DjangoFilterListField/DjangoListObjectField

Just wondering if it is possible to define custom resolver for the fields exposed by graphene-django-extras.

My project has two Apps (with separate databases) but with relationships across apps.

I would like to use a custom resolver to select related objects where the connection is not defined in the ORM but still have access to pagination and filtering.

I tried to do this as I would in Graphene-Django but the resolver function isn't called when used with any of the fields provided by this library. I tried it with graphene.field and the resolver function is run.

class SiteObjectType(DjangoObjectType):
     class Meta:
        description = "Site model type definition "
        model = models.Site
   
    vehicles = DjangoListObjectField(types.Vehicle)

    def resolve_vehicles(self, info, **kwargs):
        # something like this to select the relevant vehicle objects
        related_vehicles = Vehicles.object.filter(id__in="self.id")
        return related_vehicles

Is this expected behaviour and is there any way around it?

Thanks for the great work on this extension!

How can i override delete method

Hi,
Thanks for the lib, i like it very much,
quick question is, seems i can't override the delete method,
I want to check if the products owned by current logged in user before i delete it.
but it seems it's never calling delete and never print product in the console,
create, and update working fine

from rest_framework import serializers
from core.models import Product
class ProductSerializer(serializers.Serializer):
     class Meta:
            model = Product
    def create(self, validated_data):
           pass
    def update(self, product, validated_data):
           pass
     def delete(self, product):
            if self.logged_in() and product.user.id == self.user().id:
                print("product", product)
            raise ValidationError(message={"message": "user not logged in"})

Couldn't make filters work

Hey! Thanks for this amazing utility, definitely it makes using graphene a lot easier with Django.

I'm having this error when trying to implement a filtered query:

Traceback (most recent call last):
  File "/home/miltonln/Proyectos/Swapps/accountant-venv/lib/python3.6/site-packages/graphql/execution/executor.py", line 330, in complete_value_catching_error
    exe_context, return_type, field_asts, info, result)
  File "/home/miltonln/Proyectos/Swapps/accountant-venv/lib/python3.6/site-packages/graphql/execution/executor.py", line 383, in complete_value
    raise GraphQLLocatedError(field_asts, original_error=result)
graphql.error.located_error.GraphQLLocatedError: 'NoneType' object has no attribute '_meta'

this is my DjangoObjectType :

class ProductType(DjangoObjectType):
    class Meta:
        model = Product
        description = "Hey"
        filter_fields = {
            'name': ['icontains']
        }

and this is the query:

class Query(object):
    all_product_categories = graphene.List(ProductCategoryType)
    all_products = graphene.List(ProductType)
    filter_products = DjangoFilterListField(ProductType)

It is correctly connected to the rest of the project schema.

I made some debugging and it seems to be failing in this line, the root param is None

Am I making some mistake or is this an issue for this library?

Thanks for all the help you can provide to me.

Add a way to tag a custom resolver for prefetch

I have a type with a calculated field who depends on a model related field, can you add a way to tag it for prefetch?
My idea:

class PersonType(DjangoObjectType):
    class Meta:
        model = Person
    display = graphene.String()
    
    @dependes_on('user')
    def resolve_display(self, info):
        return str(self.user) 

This way every access to display will implay a fetch of user.

mixed prefetch_related() and select_related()

Hi @eamigo86, thanks for sharing this!

I was struggling with Django ORM errors like:

Invalid field name(s) given in select_related: 'my_field'. Choices are: my_other_field, another_one, ...

In particular, this happens when both querying AND filtering by a many-to-many or one-to-many relation field.

If i'm not missing something, there seems to be a subtle bug over utils.recursive_params() that leads to such fields being used as arguments for prefetch_related (expected) and simultaneously for select_related (unexpected) if they're "visited" more than once within the recursion.

I made a pull request #42 which fixes this issue for me, by adjusting this duplicate-preventing conditional that misidentifies already-added fields meant for prefetch_related as fields meant for select_related.

Hard dependency on `psycopg2`

In converter.py on line 4, fields from PostgreSQL are imported.

These fields are only available when you have the psycopg2 module installed. This module is not listed in setup.py, but I think it is better to not depend on this if the user does not use PostgresQL.

undocumented feature: defining a DjangoObjectType as baseType for DjangoListObjectType or DjangoSerializerType

Hi,
first of all: awesome package!
I don't really have an issue to report, but a feature that should be documented and was hard for me to find.
My problem was that I intended to use DjangoSerializerType for common queries and mutations. Everything worked fine, apart from including property functions from the django model. I had to define a baseType like this, which worked fine:

class InstrumentType(DjangoObjectType):
    is_at = graphene.Field(LocationType.object_type(), source='is_at')
    class Meta:
        model = Instrument

But then I couldn't use the nice features of DjangoSerializerType or DjangoListObjectType. Then I searched for a long time how to pass that InstrumentType to e.g. a DjangoSerializerType. Finally I found out that you use this line in the types.py to get an already defined baseType from the registry:

output_type = registry.get_type_for_model(model)
if not output_type:
     output_type = factory_type('output', DjangoObjectType, **factory_kwargs)

But this depends on the order of the type definitions in the source code and is real implicit behaviour.
After I found that out, I could use my InstrumentSerializerType with correct custom baseType just like this:

class InstrumentSerializerType(DjangoSerializerType):
    class Meta:
        serializer_class = InstrumentSerializer

I really suggest you document that behaviour, since it's a powerful feature and can also have unexpected negative effects if you don't know about.

Thanks for the nice work,
Greets,
Bene

How to add request.user to serializer?

The codes seems not passing context to serializer when it's called.
serializer = cls._meta.serializer_class(data=new_obj)

I would like to add request.user when it tries to save, I couldn't find a way to do it.
Anyone has done this before?

Changing some Fields from a DjangoInputObjectType to required=False

I'm trying to create some update mutations, where I don't want to pass some fields, because I don't want to update them.
For example I have this model:

class Pet(models.Model):
    name = models.CharField(max_length=60)
    description = models.TextField(blank=True, null=True)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)

And the following InputType:

class PetInput(DjangoInputObjectType):
    class Meta:
        model = Pet
        description = 'Pet InputType'

How can I change, for example, the name and owner fields from my InputType in order to make them required=False. As a suggestion, it will be nice adding something like the exclude_fields = ('name', 'owner') that makes them required=False, for example a require_false_fields = ('name', 'owner') I just don't know how something like this could be implemented

Compatibility Django 2

Hello,

Django-filters version has been pinned to 1.1 due to its incompatibility with django 2 (I assume). As the version 2 has been released, do you know what would be needed to have it updated such that we could use graphene-django-extras with django 2 or is it compatible out of the box ?

Thanks

Idea: expose more details on pagination

This is something that can be useful for pagination:

  • Allow an option to limit the maximum number of results per offset/page query, e.g. PageGraphqlPagination(max_limit=100). This way, someone cannot query all the results.
  • Expose the actual limit that is being applied.

Consider this (dangerous) query:

allUsers() {
    results(limit:9999, offset:5) {
      ...
    }
    totalCount
    limit <-- return the actual limit that is used.
  }
}

Without this information, you cannot calculate the right number of pages, because if you assume that you can request 99,999 items, and there are 1,000 items, you would calculate 1 page. But if the server applies a limit (e.g. 100), there is no way that you can determine the number of pages (1,000).

filtering by request data

For example i want to filter articles by current user using DjangoListObjectField or DjangoFilterPaginateListField
I dont see a clean way to do so right now. In django-graphene for DjangoConnectionField i could define resolve_articles field. It doesnt work here.
Other options would be to use django-filter request filtering by defining a custom filterset_class. https://django-filter.readthedocs.io/en/latest/guide/usage.html#filtering-the-primary-qs

But request not passed to filterset at all
https://github.com/eamigo86/graphene-django-extras/blob/master/graphene_django_extras/fields.py#L188 only qs and data passed. Something like request=info.context is missed.

Am i missing something?

Is there a way to use DjangoListObjectType with related fields?

Using DjangoListObjectType to make a filterable list doesn't make any related field available on the graphql endpoint.

I have a related field called created_by (ForeignKey to User model) but I can't query it on DjangoListObjectField, only on DjangoObjectField. Is there any way I can do that?

Mutation error with DjangoSerializerType

Hi, I encountered an error calling mutations on a model implementing DjangoSerializerType
image

It works fine with DjangoSerializerMutation
image

It seems that ok and errors aren't present on the mutation output on DjangoSerializerType

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.