Django addon to automatically configure graphql endpoints based on Django models.
Project description
Django Relay Endpoint
"Django Relay Endpoint" is a Django addon that automatically configures a customizable graphql endpoint from models. The addon is made for "graphene_django".
Table of Contents
Requirements
Python 3.8 or higher Django 4.2.7 or higher
It is possible that the addon will work with lower code setup. Keep in mind that the code uses __init_subclass__
method and format string. It was tested made with the following code setup
- Python 3.11.2
- Django >= 4.2.7
- graphene-django >= 3.3.0
- graphene-file-upload >= 1.3.0
- django_filter >= 23.3.0
This package will also install graphene-django
, graphene-file-upload
and django_filter
.
Installation
Install using pip:
pip install django-relay-endpoint
How to use
- Add
'graphene_django'
to your project'ssettings.py
:
INSTALLED_APPS = [
# ... other apps and addons
'graphene_django',
]
- Declare your NodeTypes and pass it to the SchemaConfigurator to get the schema. e.g.
# endpoint.py
from django_relay_endpoint import NodeType, SchemaConfigurator
class AuthorType(NodeType):
@staticmethod
def get_queryset(object_type, queryset, info):
return queryset.filter(age__gte=35) # filter authors with age higher than 35
class Meta:
model = 'my_app.Author'
fields = ['id', 'name', 'age', 'books']
filter_fields = {
'id': ('exact',),
'name': ('iexact', 'icontains'),
}
extra_kwargs: {
'name': {
"required": True,
},
'age': {
"required": True,
}
}
class BookType(NodeType):
class Meta:
model = 'my_app.Book'
fields = ['id', 'name', 'authors']
schema = SchemaConfigurator([
AuthorType,
BookType,
]).schema()
- In your urls.py add the endpoint
# urls.py
from graphene_file_upload.django import FileUploadGraphQLView
from my_app.endpoint import schema
from django.views.decorators.csrf import csrf_exempt
urlpatterns = [
# ... other urls
path("graphql_dashboard_v1", csrf_exempt(FileUploadGraphQLView.as_view(graphiql=True, schema=schema))),
]
It uses FileUploadGraphQLView
from graphene_file_upload.django
to support file uploads.
Adding custom query and mutation types
The SchemaConfigurator
instance has query
and mutation
properties of type List, when instantiated.
Custom object types can be appended to the query
and mutation
properties: e.g.
# let's imagine we have created a mutation that handles login
from my_app.models import Book
class AuthType(Mutation): ...
# and we have BookType configured with NodeType
class BookType(NodeType):
class Meta:
model = Book
fields = '__all__'
schema = SchemaConfigurator([
BookType,
]) # will instantiate the SchemaConfigurator with rootfields form BookType.
schema.mutations.append(AuthType) # adds AuthType to mutations.
schema = schema.schema() # overwrite schema with actual schema. This will create the actual schema by extending all types and return the schema.
Configuring custom NodeType for node root field
Per relay specification the endpoint must implement node
root field.
The configured server implements the node
root field with default configuration per graphene.
If the developer wants to provide custom NodeType
it can do so by subclassing graphene.ObjectType
and providing own resolver, e.g.
class CustomNodeType(graphene.ObjectType):
node = graphene.relay.Node.Field()
@classmethod
resolve_node(cls, root, info, id):
# implement custom type identification using from_global_id
...
schema = SchemaConfigurator([
AuthorType,
BookType,
])
# overwrite the default NodeType with CustomNodeType
schema.node_type = CustomNodeType
# Overwrite schema with actual generated schema.
schema = schema.schema()
Configuring NodeType subclasses
A subclass of NodeType can be configured via its Meta class.
Available options are as follows:
Following options can be configured on class Meta:
- model: (Union[str, Type[models.Model]]) - a string composed of 'app_name.model_name' or actual django model.
- fields: List[str] | Literal["all"] - an explicit list of field names or 'all'.
- query_root_name: str | None - a root field name. Defaults to lowered snake-case model._meta.verbose_name.
- query_root_name_plural: str | None - a root field name. Defaults to lowered snake-case model._meta.verbose_name_plural.
- filter_fields: Union[Dict[str, List[str]], List[str]] - fielter_fields configurations. see https://docs.graphene-python.org/projects/django/en/latest/filtering/#filterable-fields.
- filterset_class: FilterSet - a filterset_class. see https://docs.graphene-python.org/projects/django/en/latest/filtering/#custom-filtersets.
- object_type_name: str | None - The classname of the DjangoObjectType that will be configured. Defaults to camel-case
AppNameModelNameType
. - mutation_operations: Literal["create", "update", "delete"] - similar to query_operations, this limits the root field configuration, defaults to
["create", "update", "delete"]
. - extra_kwargs: Dict[str, Dict[str, Any]] - the mutation type fields are configured via assigned django form field; this option is similar to rest framework serializer
extra_kwargs
, which is a dictionary of field_names mapped to a dictionary of django form field kwargs. The configurator automatically maps the field to the respective form field: for field mapping see https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/#field-types. For relations, it maps the fields tographene.List(graphene.ID, **field_kwargs)
and graphene.ID(**field_kwargs)
, it will also infer therequired
parameter value from the declaredallow_blank
andallow_null
parameters of the respective model.field. - field_validators: Dict[str, List[Callable]] - a dictionary of field_names mapped to the list of validators: see Validators.
- non_field_validators: List[Callable] - list of validators: see Validators.
- success_keyword: str - a success keyword for mutation responses. by defualt it is 'success'.
- input_field_name: str - a Input field name for mutations. Defaults to 'data'.
- return_field_name: str - the field name on the response on create and update mutations, if none provided, model._meta.model_name will be used.
- permissions: List[str] - A list of permission names, defaults to empty list, i.e. no permissions will be checked.
- permission_classes: List[Type[BasePermission]] - A list of permission classes. see Permissions.
Following fields can be configured on the subclass of the NodeType:
- get_queryset: Callable - a static get_queryset method. Important! this method should be declared as staticmethod, it will be returned with the configured subclass of DjangoObjectType, queryset and info. It behaves as overwrite of get_queryset method, but is a staticmethod. See the example in How to use.
Validators
A validator passed to field_validators
or non_field_validators
is a function that takes the following arguments:
-
data: the field value for field_validators and whole data object for non_field_validators
-
not_updated_model_instance: the instance with the state before merging data with the instance
-
info: the graphene resolve info object instance.
Permissions
We have extended DjangoObjectType and ClientIDMutation to support string permissions and class based permissions for queryset and object level permission checks.
Class based permissions extend custom BasePermission
class, which implements has_permission(self, info) -> bool
and has_object_permission(self, info, obj) -> bool
methods. If the class returns False
a permission-denied error will be raised. Following default permission classes can be found in graphene_relay_endpoint:
- AllowAny: This class is intended only for explicit declaration. It does nothing similar to the same permission in REST framework
- IsAuthenticated: Checks for authentication.
- IsAdminUser: Checks for admin privilege.
- IsAuthenticatedOrReadOnly: Limits mutation operations to authenticated users.
- BasePermission: A base class to subclass for custom permission classes.
Useful subclasses and tools
The addon comes with builtin DjangoClientIDMutation abstract subclass, which implements following methods
- get_queryset: same as on graphen_django.DjangoObjectType
- get_node: same as on graphen_django.DjangoObjectType
- create_node: creates an empty instance of the given mode
- validate: validates data via 'field_validators' and 'non_field_validators' supplied with the subclass of NodeType.
- update_instance: set's the values on the instance from data. For to-many relations it uses the
add_<field_name>
andremove_<field_name>
convention. First it adds than it removes. The client can pass both, and the relations will be added and removed consecutively before being saved.
N.B. DjangoClientIDMutation does not implement a mutate_and_get_payload
classmethod, the developer must implement it on a subclass.
License
See the MIT licens in the LICENSE file in the project.
Documentation
The addon is pretty simple. The How to use and Configuring NodeType subclasses explains it all. Each piece of code is also documented with dockstrings and has respective type hints. For additional information read the respective documentation:
- graphene_django: https://docs.graphene-python.org/projects/django/
- graphene-file-upload: https://github.com/lmcgartland/graphene-file-upload
Tip the author
If this project has facilitated your job, saved time spent on boilerplate code and pain of standardizing and debugging a relay style endpoint, consider tipping (donating) the author with some crypto:
Opera browser wallet: 3N5ot3DA2vSLwEqhjTGhfVnGaAuQoWBrCf
Thank you!
Client
An ember client with ember-data style encapsulation layer is on the way.
To-Do
- Default Login configurator
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django_relay_endpoint-1.2.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | dc8d2022ab210f02361b5d8f3bcf4716817d73a207a16b7d5aa92a49174678a8 |
|
MD5 | 949b29826bc25aebcb9b9b2f9e3d04a0 |
|
BLAKE2b-256 | 2b3c4f3c746401b63fca3d00b91c68c4457a385b71506c5db768e667284fff3f |
Hashes for django_relay_endpoint-1.2.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | f5da4babb475cd034d454009e12ac666b50071723adf0c3e364d3f19c7c56c2f |
|
MD5 | 6e45583595033e91ebac9a0d9be95f0c |
|
BLAKE2b-256 | 52cf8b949c00d567c8d6987fad40d56f69a48c8d86e0bc8721918d0560f350bd |