- All graphql URLs/end points are exposed through the sub application
crm.api
, incrm.api.views
- We have 2 main end points
/graphql
web based console to test graphql/api
can be called using any HTTP client
-
For any sub application i.e
crm.apps.contact
, you can define Graphql APIs in a package calledcrm.apps.{app_name}.graphql
and your queries and mutations will be exposed automatically -
Each
crm.apps.{app_name}.graphql
package must contain 3 modules:types.py
- where you define
graphql
types from certain SqlAlchemy model in the same manner you define Django model forms
- where you define
arguments.py
- These are the arguments for mutations and queries and they're simply defined by defining some/all fields from certain model
mutations.py
- Where you define your mutations
queries.py
- Where you define queries
-
Add your type in
crm.apps.{app_name}.graphql.types
module- Inherit from
SQLAlchemyObjectType
- In your Meta class, define
model = your-model
- Add extra fields if needed
import graphene from graphene import relay from graphene_sqlalchemy import SQLAlchemyObjectType from crm.apps.contact.models import Contact class ContactType(SQLAlchemyObjectType): uid = graphene.String() class Meta: model = Contact interfaces = (relay.Node,) name = model.__name__
- Inherit from
Important
Graphene replaces record id
with internal representation of that id
so we use uid
in all our Graphene types to represent original id
that is why it's important to add uid = graphene.String()
in each type you define which will be mapped automatically
to object.uid
which returns object.id
value
- Add your arguments in
crm.apps.{app_name}.graphql.arguments
module - we have usually 3 types of arguments
Base Arguments
- Inherits from
graphene.types.inputobjecttype.InputObjectType
&crm.graphql.BaseArgument
- Parent for all arguments, each field in it is defined as (not required)
- We basically define same fields in a certain model
- Inherits from
Create mutation arguments
- Inherits from
Base Arguments
. - Override required fields by redefining them with
required=True
param
- Inherits from
Update mutation arguments
- Inherits from
Base Arguments
. - Add
uid = graphene.String(required=True)
because while updating a recorduid
is definitely required
- Inherits from
- Example:
crm.apps.contact.graphql.arguments
import graphene
from graphene.types.inputobjecttype import InputObjectType
from crm.apps.address.graphql.arguments import AddressArguments
from crm.apps.comment.graphql.arguments import CommentArguments
from crm.apps.contact.models import Gender
from crm.apps.link.graphql.arguments import LinkArguments
from crm.apps.message.graphql.arguments import MessageArguments
from crm.apps.passport.graphql.arguments import PassportArguments
from crm.apps.task.graphql.arguments import TaskArguments
from crm.graphql import BaseArgument
class ContactSubgroupArguments(InputObjectType):
groupname = graphene.String()
class ContactArguments(InputObjectType, BaseArgument):
uid = graphene.String()
firstname = graphene.String()
lastname = graphene.String()
gender = graphene.Enum.from_enum(Gender)()
description = graphene.String()
bio = graphene.String()
belief_statement = graphene.String()
message_channels = graphene.String()
owner = graphene.Argument('crm.apps.user.graphql.arguments.UserArguments')
ownerbackup = graphene.Argument('crm.apps.user.graphql.arguments.UserArguments')
parent = graphene.Argument('crm.apps.contact.graphql.arguments.ContactArguments')
emails = graphene.String()
telephones = graphene.String()
tf_app = graphene.Boolean()
tf_web = graphene.Boolean()
referral_code = graphene.String()
deals = graphene.List('crm.apps.deal.graphql.arguments.DealArguments')
comments = graphene.List(CommentArguments)
tasks = graphene.List(TaskArguments)
messages = graphene.List(MessageArguments)
links = graphene.List(LinkArguments)
subgroups = graphene.List(ContactSubgroupArguments)
addresses = graphene.List(AddressArguments)
passports = graphene.List(PassportArguments)
class CreateContactArguments(ContactArguments):
firstname = graphene.String(required=True)
lastname = graphene.String(required=True)
class UpdateContactArguments(ContactArguments):
uid = graphene.String(required=True)
-
Add your arguments in
crm.apps.{app_name}.graphql.queries
module -
Inherit from
crm.graphql.BaseQuery
-
Use the following example, rename necessary fields
import graphene from graphene import relay from crm.apps.contact.graphql.arguments import ContactArguments from crm.apps.contact.graphql.types import ContactType from crm.graphql import BaseQuery, CRMConnectionField class ContactQuery(BaseQuery): """ we have 2 queries here contact and contacts """ # no need for resplve_contacts function here contacts = CRMConnectionField( ContactType, **ContactArguments.fields() ) # contact query to return one contact and takes (uid) argument # uid is the original object.id in db contact = graphene.Field(ContactType, uid=graphene.String()) def resolve_contact(self, context, uid): return ContactType.get_query(context).filter_by(id=uid).first() class Meta: interfaces = (relay.Node, )
-
In previous example we define 2 queries:
-
contacts
- returning all/subset of records. Please refer to CRM API General overview & GraphQl API Query language for examples on how to query data
- may take no arguments then return all records
- may take query arguments defined by
ContactArguments
then return subset of data based on the defined query
-
contact
takesuid
field and returns one record
-
-
For each query you define, usually you define the
query_name
i.econtacts
and a resolver functionresolve_{query_name}
i.eresolve_contacts
that is called to handle that query when executed
-
Add your mutations in
crm.apps.{app_name}.graphql.mutations
module -
For each mutation you have, you create a class that extends
graphene.Mutation
and overridemutate
function and may uses one of thearguments
defined incrm.apps.{app_name}.graphql.arguments
class CreateContacts(graphene.Mutation): class Arguments: """ Mutation Arguments """ records = graphene.List(CreateContactArguments, required=True) ok = graphene.Boolean() ids = graphene.List(graphene.String) @classmethod def mutate(cls, root, context, **kwargs): """ Mutation logic is handled here """ ... class UpdateContacts(graphene.Mutation): class Arguments: """ Mutation Arguments """ records = graphene.List(UpdateContactArguments, required=True) ok = graphene.Boolean() ids = graphene.List(graphene.String) @classmethod def mutate(cls, root, context, **kwargs): """ Mutation logic is handled here """ ... class DeleteContacts(graphene.Mutation): class Arguments: """ Mutation Arguments """ uids = graphene.List(graphene.String, required=True) ok = graphene.Boolean() @classmethod def mutate(cls, root, context, **kwargs): """ Mutation logic is handled here """ ...
-
Define Mutation parent class that holds all defined mutations and that extends
crm.graphql.BaseMutation
class ContactMutation(BaseMutation): """ Put all contact mutations here """ create_contacts = CreateContacts.Field() delete_contacts = DeleteContacts.Field() update_contacts = UpdateContacts.Field()