Back to blog

Django-cms and GraphQL

Michał Dziadowicz
Michał Dziadowicz Python Developer

Read time: 5 min

Oct 06, 2021

Adding GraphQL to DjangoCMS allows exposing data through an api in a structured way. This can be used to build a cms frontend in other technologies or integrate a cms with more applications.

Installing packages

Install package ‘graphene-django’ from pip. This will also install:

  • graphene
  • graphql-core
  • graphql-relay


Package graphene enables main functionality of graphql and graphene-django allows using django models as nodes in graphql.

Configuration

In the settings.py add ‘graphene_django’ to INSTALLED_APPS and add configuration for graphene:

  
    GRAPHENE = {
    "SCHEMA": "common.schema.schema"
}
  

where SCHEMA points to a file that will contain schema definitions.

Inside urls.py add

  
    from graphene_django.views import GraphQLView
  

and to ‘urlpatterns`

  
    path("graphql", GraphQLView.as_view(graphiql=True))
  

Where option `graphiql` will enable an interactive console in the web browser.

Adding a schema

Next step is to define a schema. Create a file schema.py and import DjangoObjectType.

  
    import graphene
from graphene_django import DjangoObjectType
  

Create a class Query that will hold all of graphql queries:

  
    class Query(graphene.ObjectType):
	pass

  

Add Query class to schema:

  
    
schema = graphene.Schema(query=Query)
  

The schema variable is the one that is pointed to in the settings file.

Next step is to create an object that can be returned by query. For django models you have to inherit from DjangoObjectType. Class names in graphql usually end with Type on Node. Inside Meta class configure what django model will use. If a model uses parler for translation of fields then for every translated field you have to manually define a type and source for graphene fields.

  
    class PersonType(DjangoObjectType):
    name = graphene.String(source='name')
    description = graphene.String(source='description')

    class Meta:
        model = Person
        interfaces = (graphene.Node,)
  

Inside Query class add two new queries

  
    people = graphene.List(PersonType)
person = graphene.Field(PersonType, id=graphene.String())
  

First one is for returning collections of people and the second one for returning a single object by its ID.

Next step is to add a function that can populate return types with data. For that you have to add a `resolve_*` function inside a Query class. Because the person class defines a Node interface, therefore all object ids will be encoded with base64 in the form `model:ID`. To get id back use `from_global_id` function.

  
    from graphql_relay.node.node import from_global_id


@staticmethod
def resolve_people(root, context, **kwargs):
    return Person.objects.all()

@staticmethod
def resolve_person(root, context, id):
    return Person.objects.get(pk=from_global_id(id)[1])
  


Go to `localhost/graphql` interactive console and run:

  
    query getPeople {
  people {
    id
    name
    email
    }
 }
  

`getPeope` is a convenient query name that doesn’t matter. `people` is the actual query defined in the Query class, and `id`, `name`, `email` are fields in the django model that will be returned.

To query for specific person use:

  
    query getPersonById {
person(id: "UGVyc29uVHlwZTo2") {
    id
    name
    description
    }
 }
  

Where ID can be obtained from people's query.

To add “searching” and “pagination” to people query add two new input parameters for query:

  
    people = graphene.List(PersonType, first=graphene.Int(), search=graphene.String())
  

and inside a resolver function, use these parameters to modify a QuerySet to return only interesting entities. This will enable searching inside an email field and returning only X first matches.

  
    @staticmethod
def resolve_people(root, context, first=None, search=None, **kwargs):
    qs = Person.objects.all()

    if search:
        filter = Q(email__icontains=search)
        qs = qs.filter(filter)

    if first:
        qs = qs[:first]

    return qs
  

Go to `localhost/graphql` interactive console and run:

  
    query getPeople {
    people(search:"michal") {
    id
    name
    email
    }
 }
  

to search for everyone with email containing string michal, or

  
    query getPeople {
    people(first: 5) {
       email
     }
 }
  

to get the first 5 email addresses.

To add a mutation that can modify an object create a new class:

  
    class PersonMutation(DjangoObjectType):
    class Meta:
        model = Person

    class Arguments:
        id = graphene.ID()
        name = graphene.String(required=True)
        email = graphene.String(required=True)

    person = graphene.Field(PersonType)

    @classmethod
    def mutate(cls, root, info, id, name, email):
        person = Person.objects.get(pk=from_global_id(id)[1])
        person.name = name
        person.email = email
        person.save()
        return PersonMutation(person=person)
  

where Meta defines which django model to use, Arguments class defines a list of arguments that will be used in mutation query and a mutate method that takes arguments, updates a model and returns a mutation class.

Create a new mutations class:

  
    class Mutation(graphene.ObjectType):
    update_person = graphene.Field(PersonMutation)
  

and add it to schema:

  
    
schema = graphene.Schema(query=Query, mutation=Mutation)
  

Conclusion

Graphene is a great library that can help expose endpoints with a relatively little configuration. Adding new endpoints is simple when you get a grasp of graphene code conventions. This allows rapid development of APIs that expose data in logical and easy to follow ways.

Michał Dziadowicz
Michał Dziadowicz The high-skilled software engineer who is constantly developing his skills and competency within the challenging projects.

Read time: 5 min

Oct 06, 2021

Our mission is to be /trusted partner
to our clients/
in the field of webplatform
development & staff augmentation

Check our rank

developmentclutchdevelopment
  • development
  • clutch
Item 1 of 3

0 Professionals

0 Finished project

Kudos.

  • “Not afraid of doing even the most difficult tasks and proposing innovative solutions. They don’t avoid challenges, rely on new technologies, and - most importantly - always carry the projects through from the beginning to the end.”

    Adam Bogdan
    Adam Bogdan Python Developer
Item 1 of 9

Contact.

We're happy to help! Leave your contact data, and we will get in touch with you within one business day.

Tell us about your needs, ask about our experience, portfolio, and even partnership programs.

Contact us