Package: graphene-django-flufy
Graphene-Django-FlufyThis package builds on top ofGraphene-Django-Extraand it adds some extra functionalities to graphene-django to facilitate the graphql use without Relay:Allow pagination and filtering on Queries.Allow defining DjangoRestFramework serializers based on Mutations.Allow using Directives on Queries and Fragments.NOTE:Subscription support still sits in moved tographene-django-subscriptions. It may be moved laterInstallationFor installing graphene-django-flufy, just run this command in your shell:pip install graphene-django-flufyDocumentation:Extra functionalities:Fields:DjangoObjectFieldDjangoFilterListFieldDjangoFilterPaginateListFieldDjangoListObjectField (Recommended for Queries definition)Mutations:DjangoSerializerMutation (Recommended for Mutations definition)Types:DjangoListObjectType  (Recommended for Types definition)DjangoInputObjectTypeDjangoSerializerType  (Recommended for quick queries and mutations definitions)Paginations:LimitOffsetGraphqlPaginationPageGraphqlPaginationQueries 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_FLUFY={'DEFAULT_PAGINATION_CLASS':'graphene_django_flufy.paginations.LimitOffsetGraphqlPagination','DEFAULT_PAGE_SIZE':20,'MAX_PAGE_SIZE':50,'CACHE_ACTIVE':True,'CACHE_TIMEOUT':300# seconds}1- Types Definition:fromdjango.contrib.auth.modelsimportUserfromgraphene_django_flufyimportDjangoListObjectType,DjangoSerializerType,DjangoObjectTypefromgraphene_django_flufy.paginationsimportLimitOffsetGraphqlPaginationfrom.serializersimportUserSerializerclassUserType(DjangoObjectType):classMeta:model=Userdescription=" 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",),}classUserListType(DjangoListObjectType):classMeta:description=" Type definition for user list "model=Userpagination=LimitOffsetGraphqlPagination(default_limit=25,ordering="-username")# ordering can be: string, tuple or listclassUserModelType(DjangoSerializerType):""" With this type definition it't necessary a mutation definition for user's model """classMeta:description=" User model type definition "serializer_class=UserSerializerpagination=LimitOffsetGraphqlPagination(default_limit=25,ordering="-username")# ordering can be: string, tuple or listfilter_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:fromgraphene_django_flufyimportDjangoInputObjectTypeclassUserInput(DjangoInputObjectType):classMeta:description=" User InputType definition to use as input on an Arguments class on traditional Mutations "model=User3- You can define traditional mutations that use InputTypes or Mutations based on DRF serializers:importgraphenefromgraphene_django_flufyimportDjangoSerializerMutationfrom.serializersimportUserSerializerfrom.typesimportUserTypefrom.input_typesimportUserInputTypeclassUserSerializerMutation(DjangoSerializerMutation):"""DjangoSerializerMutation auto implement Create, Delete and Update functions"""classMeta:description=" DRF serializer based Mutation for Users "serializer_class=UserSerializerclassUserMutation(graphene.Mutation):"""On traditional mutation classes definition you must implement the mutate function"""user=graphene.Field(UserType,required=False)classArguments:new_user=graphene.Argument(UserInput)classMeta:description=" Graphene traditional mutation for Users "@classmethoddefmutate(cls,root,info,*args,**kwargs):...4- Defining the Schema file:importgraphenefromgraphene_django_flufyimportDjangoObjectField,DjangoListObjectField,DjangoFilterPaginateListField,DjangoFilterListField,LimitOffsetGraphqlPaginationfrom.typesimportUserType,UserListType,UserModelTypefrom.mutationsimportUserMutation,UserSerializerMutationclassQueries(graphene.ObjectType):# Possible User list queries definitionsusers=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 functionuser=DjangoObjectField(UserType,description='Single User query')# Another way to define a query to single useruser1=UserListType.RetrieveField(description='User List with pagination and filtering')# Exist two ways to define single or list user queries with DjangoSerializerTypeuser_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')classMutations(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 DjangoSerializerTypeuser_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:You must add'graphene_django_flufy.ExtraGraphQLDirectiveMiddleware'to your GRAPHENE dict
config on your settings.py:# settings.pyGRAPHENE={'SCHEMA_INDENT':4,'MIDDLEWARE':['graphene_django_flufy.ExtraGraphQLDirectiveMiddleware']}You must add thedirectivesparam 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.pyfromgraphene_django_flufyimportall_directivesschema=graphene.Schema(query=RootQuery,mutation=RootMutation,directives=all_directives)NOTE: Date directive depends ondateutilmodule, so if you do not have installed it, this directive will not be
available. You can installdateutilmodule manually:pip install python-dateutilor like this:pip install graphene_django_flufy[date]That's all !!!6- Complete Directive list:FOR NUMBERS:FloorGraphQLDirective: Floors value. Supports both String and Float fields.CeilGraphQLDirective: Ceils value. Supports both String and Float fields.FOR LIST:ShuffleGraphQLDirective: Shuffle the list in place.SampleGraphQLDirective: Take a 'k' int argument and return a k length list of unique elements chosen from the
taken listFOR DATE: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:DefaultGraphQLDirective: Take a 'to' string argument. Default to given value if None or ""Base64GraphQLDirective: Take a optional ("encode" or "decode") 'op' string argument(default='encode').
Encode or decode the string taken.NumberGraphQLDirective: Take a 'as' string argument. String formatting like a specify Python number formatting.CurrencyGraphQLDirective: Take a optional 'symbol' string argument(default="$").
Prepend thesymbolargument to taken string and format it like a currency.LowercaseGraphQLDirective: Lowercase the taken string.UppercaseGraphQLDirective: Uppercase the taken string.CapitalizeGraphQLDirective: Return the taken string with its first character capitalized and the rest lowered.CamelCaseGraphQLDirective: CamelCase the taken string.SnakeCaseGraphQLDirective: SnakeCase the taken string.KebabCaseGraphQLDirective: SnakeCase the taken string.SwapCaseGraphQLDirective: Return the taken string with uppercase characters converted to lowercase and vice
versa.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.TitleCaseGraphQLDirective: Return the taken string title-cased, where words start with an uppercase character
and the remaining characters are lowercase.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.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){idusernamefirstNamelastName}totalCount}allUsers1(lastName_Iexact:"Doe",limit:5,offset:0){idusernamefirstNamelastName}allUsers2(firstName_Icontains:"J"){idusernamefirstNamelastName}user(id:2){idusernamefirstName}user1(id:2){idusernamefirstName}}8- Mutations's examples:mutation{userCreate(newUser:{username:"test",password:"test*123"}){user{idusernamefirstNamelastName}okerrors{fieldmessages}}userDelete(id:1){okerrors{fieldmessages}}userUpdate(newUser:{id:1,username:"John"}){user{idusername}okerrors{fieldmessages}}}9- Directives's examples:Let's suppose that we have this query:query{allUsers{result{idfirstNamelastNamedateJoinedlastLogin}}}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{idfirstName@capitalizelastName@default(to:"Doe")@title_casedateJoined@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:"time ago""iso": "YYYY-MMM-DDTHH:mm:ss""js" or "javascript": "ddd MMM DD YYYY HH:mm:ss"Change Log:v0.1.0:1. Update dependencies
2. Upgrade graphene-django dependency to version > 3.