Package: graphene-django-plus
graphene-django-plusDEPRECATION WARNINGGraphene itself is abandoned and most users are migrating to other better alternatives, likestrawberry.For that reason this lib is being deprecated and new features will no longer be developed for it.
Maintenance is still going to happen and PRs are still welcomed though.For anyone looking for alternatives, I createdstrawberry-django-plusto use not
only as a migration path to the projects I maintain, but also to add even more awesome features.
Be sure to check it out!Tools to easily create permissioned CRUD endpoints ingraphene-django.Installpipinstallgraphene-django-plusTo make use of everything this lib has to offer, it is recommended to install
bothgraphene-django-optimizeranddjango-guardian.pipinstallgraphene-django-optimizerdjango-guardianWhat it doesProvides some base types for Django Models to improve querying them with:Unauthenticated user handlingAutomatic optimization usinggraphene-django-optimizerPermission handling for queries using the defaultdjango permission systemObject permission handling for queries usingdjango guardianRelay id conversion so querying can use the global id instead of the model's idProvides a set of complete and simple CRUD mutations with:Unauthenticated user handlingPermission handling using the defaultdjango permission systemObject permission handling usingdjango guardianAutomatic input generation based on the model (no need to write your own input type or usedjango formsanddrf serializers)Automatic model validation based on the model's validatorsVery simple to create some quick CRUD endpoints for your modelsEasy to extend and override functionalitiesFile upload handlingWhat is includedCheck thedocsfor a complete
api documentation.Modelsgraphene_django_plus.models.GuardedModel: A django model that can be used
either directly or as a mixin. It will provide a.has_permmethod and a.objects.for_userthat will be used byModelTypedescribed below to
check for object permissions.Types and Queriesgraphene_django_plus.types.ModelType: This enchancesgraphene_django_plus.DjangoModelTypeby doing some automaticprefetchoptimization on setup and also checking for objects permissions on queries
when it inherits fromGuardedModel.graphene_django_plus.fields.CountableConnection: This enchancesgraphene.relay.Connectionto provide atotal_countattribute.Here is an example describing how to use those:importgraphenefromgrapheneimportrelayfromgraphene_django.fieldsimportDjangoConnectionFieldfromgraphene_django_plus.modelsimportGuardedModelfromgraphene_django_plus.typesimportModelTypefromgraphene_django_plus.fieldsimportCountableConnectionclassMyModel(GuardedModel):classMeta:# guardian permissions for this modelpermissions=[('can_read',"Can read the this object's info."),]name=models.CharField(max_length=255)classMyModelType(ModelType):classMeta:model=MyModelinterfaces=[relay.Node]# Use our CountableConnectionconnection_class=CountableConnection# When adding this to a query, only objects with a `can_read`# permission to the request's user will be allowed to return to him# Note that `can_read` was defined in the model.# If the model doesn't inherid from `GuardedModel`, `guardian` is not# installed or this list is empty, any object will be allowed.# This is empty by defaultobject_permissions=['can_read',]# If unauthenticated users should be allowed to retrieve any object# of this type. This is not dependent on `GuardedModel` and neither# `guardian` and is defined as `False` by defaultpublic=False# A list of Django model permissions to check. Different from# object_permissions, this uses the basic Django's permission system# and thus is not dependent on `GuardedModel` and neither `guardian`.# This is an empty list by default.permissions=[]classQuery(graphene.ObjectType):my_models=DjangoConnectionField(MyModelType)my_model=relay.Node.Field(MyModelType)This can be queried like:# All objects that the user has permission to seequery{myModels{totalCountedges{node{idname}}}}# Single object if the user has permission to see itquery{myModel(id:"<relay global ID>"){idname}}Mutationsgraphene_django_plus.mutations.BaseMutation: Base mutation usingrelayand some basic permission checking. Just override its.perform_mutationto
perform the mutation.graphene_django_plus.mutations.ModelMutation: Model mutation capable of
both creating and updating a model based on the existence of anidattribute in the input. All the model's fields will be automatically read
from Django, inserted in the input type and validated.graphene_django_plus.mutations.ModelCreateMutation: AModelMutationenforcing a "create only" rule by excluding theidfield from the input.graphene_django_plus.mutations.ModelUpdateMutation: AModelMutationenforcing a "update only" rule by making theidfield required in the
input.graphene_django_plus.mutations.ModelDeleteMutation: A mutation that will
receive only the model's id and will delete it (if given permission, of
course).Here is an example describing how to use those:importgraphenefromgrapheneimportrelayfromgraphene_django_plus.modelsimportGuardedModelfromgraphene_django_plus.typesimportModelTypefromgraphene_django_plus.mutationsimport(ModelCreateMutation,ModelUpdateMutation,ModelDeleteMutation,)classMyModel(GuardedModel):classMeta:# guardian permissions for this modelpermissions=[('can_write',"Can update this object's info."),]name=models.CharField(max_length=255)classMyModelType(ModelType):classMeta:model=MyModelinterfaces=[relay.Node]classMyModelUpdateMutation(ModelUpdateMutation):classMeta:model=MyModel# Make sure only users with the given permissions can modify the# object.# If the model doesn't inherid from `GuardedModel`, `guardian` is not# installed on this list is empty, any object will be allowed.# This is empty by default.object_permissions=['can_write',]# If unauthenticated users should be allowed to retrieve any object# of this type. This is not dependent on `GuardedModel` and neither# `guardian` and is defined as `False` by defaultpublic=False# A list of Django model permissions to check. Different from# object_permissions, this uses the basic Django's permission system# and thus is not dependent on `GuardedModel` and neither `guardian`.# This is an empty list by default.permissions=[]classMyModelDeleteMutation(ModelDeleteMutation):classMeta:model=MyModelobject_permissions=['can_write',]classMyModelCreateMutation(ModelCreateMutation):classMeta:model=MyModel@classmethoddefafter_save(cls,info,instance,cleaned_input=None):# If the user created the object, allow him to modify itassign_perm('can_write',info.context.user,instance)classMutation(graphene.ObjectType):my_model_create=MyModelCreateMutation.Field()my_model_update=MyModelUpdateMutation.Field()my_model_delete=MyModelDeleteMutation.Field()This can be used to create/update/delete like:# Create mutationmutation{myModelCreate(input:{name:"foobar"}){myModel{name}errors{fieldmessage}}}# Update mutationmutation{myModelUpdate(input:{id:"<relay global ID>",name:"foobar"}){myModel{name}errors{fieldmessage}}}# Delete mutationmutation{myModelDelete(input:{id:"<relay global ID>"}){myModel{name}errors{fieldmessage}}}Any validation errors will be presented in theerrorsreturn value.To turn off auto related relations addition to the mutation input - set globalMUTATIONS_INCLUDE_REVERSE_RELATIONSparameter toFalsein yoursettings.py:GRAPHENE_DJANGO_PLUS = {
    'MUTATIONS_INCLUDE_REVERSE_RELATIONS': False
}Note: in case reverse relation does not haverelated_nameattribute set -
mutation input will be generated as Django itself is generating by appending_setto the lower cased model name -modelname_setLicenseThis project is licensed under MIT licence (seeLICENSEfor more info)ContributingMake sure to havepoetryinstalled.Install dependencies with:poetryinstallRun the testsuite with:poetryrunpytestFeel free to fork the project and send me pull requests with new features,
corrections and translations. We'll gladly merge them and release new versions
ASAP.