CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-graphene-django

Django integration for Graphene enabling GraphQL APIs in Django applications

Pending
Overview
Eval results
Files

forms.mddocs/

Forms Integration

Form-based GraphQL mutations with Django form validation, error handling, and automatic input type generation. Provides seamless integration between Django forms and GraphQL mutations with comprehensive validation and error reporting.

Capabilities

BaseDjangoFormMutation

Abstract base class for Django form-based mutations with customizable form handling and validation logic.

class BaseDjangoFormMutation(graphene.relay.ClientIDMutation):
    """
    Base class for Django form-based mutations.
    
    Provides foundation for form-based mutations with form instantiation,
    validation, and error handling. Designed to be subclassed for specific
    form types and validation requirements.
    """
    
    @classmethod
    def mutate_and_get_payload(cls, root, info, **input):
        """
        Main mutation logic with form processing.
        
        Parameters:
        - root: GraphQL root object
        - info: GraphQL execution info
        - **input: Mutation input arguments
        
        Returns:
        Mutation result with form data or errors
        """
    
    @classmethod
    def get_form(cls, root, info, **input):
        """
        Get form instance for validation.
        
        Parameters:
        - root: GraphQL root object  
        - info: GraphQL execution info
        - **input: Form input data
        
        Returns:
        django.forms.Form: Form instance
        """
    
    @classmethod
    def get_form_kwargs(cls, root, info, **input):
        """
        Get form constructor arguments.
        
        Parameters:
        - root: GraphQL root object
        - info: GraphQL execution info  
        - **input: Form input data
        
        Returns:
        dict: Form constructor keyword arguments
        """
    
    @classmethod
    def perform_mutate(cls, form, info):
        """
        Perform mutation with validated form.
        
        Parameters:
        - form: Validated Django form
        - info: GraphQL execution info
        
        Returns:
        Mutation result object
        """

DjangoFormMutation

Concrete form mutation with automatic error handling and input type generation from Django forms.

class DjangoFormMutation(BaseDjangoFormMutation):
    """
    Concrete form mutation with error handling.
    
    Automatically generates GraphQL input types from Django forms and
    provides standardized error reporting through ErrorType objects.
    """
    errors = graphene.List(ErrorType)
    
    class Meta:
        """
        Meta options for form mutations.
        
        Attributes:
        - form_class: Django form class to use
        - only_fields: Fields to include in GraphQL input (list or tuple)
        - exclude_fields: Fields to exclude from GraphQL input (list or tuple)
        - return_field_name: Name for successful result field
        """
        form_class = None
        only_fields = None
        exclude_fields = None  
        return_field_name = None
    
    @classmethod
    def __init_subclass_with_meta__(cls, form_class=None, only_fields=None,
                                   exclude_fields=None, return_field_name=None,
                                   **options):
        """
        Configure form mutation subclass.
        
        Parameters:
        - form_class: Django form class
        - only_fields: Fields to include  
        - exclude_fields: Fields to exclude
        - return_field_name: Result field name
        - **options: Additional mutation options
        """
    
    @classmethod
    def perform_mutate(cls, form, info):
        """
        Perform mutation with validated form data.
        
        Parameters:
        - form: Validated Django form
        - info: GraphQL execution info
        
        Returns:
        Mutation result with form data
        """

DjangoModelFormMutation

Model form-based mutations with CRUD operations and automatic model instance handling.

class DjangoModelFormMutation(BaseDjangoFormMutation):
    """
    Model form-based mutations with CRUD operations.
    
    Automatically handles model instance creation and updates through
    Django ModelForm validation with comprehensive error handling.
    """
    errors = graphene.List(ErrorType)
    
    class Meta:
        """
        Meta options for model form mutations.
        
        Inherits from DjangoFormMutation.Meta with additional model-specific
        options for instance handling and field mapping.
        """
        form_class = None
        model = None
        only_fields = None
        exclude_fields = None
        return_field_name = None
    
    @classmethod
    def get_form_kwargs(cls, root, info, **input):
        """
        Get model form constructor arguments with instance handling.
        
        Parameters:
        - root: GraphQL root object
        - info: GraphQL execution info
        - **input: Form input data
        
        Returns:
        dict: Form constructor arguments including model instance
        """
    
    @classmethod
    def perform_mutate(cls, form, info):
        """
        Perform mutation with model form save.
        
        Parameters:
        - form: Validated Django model form
        - info: GraphQL execution info
        
        Returns:
        Mutation result with saved model instance
        """

Form Field Types

Global ID form fields for GraphQL integration with proper ID encoding and validation.

class GlobalIDFormField(forms.CharField):
    """Django form field for global IDs with automatic validation."""
    
    def to_python(self, value):
        """
        Convert global ID to Python value.
        
        Parameters:
        - value: Global ID string
        
        Returns:
        Decoded ID value
        """

class GlobalIDMultipleChoiceField(forms.MultipleChoiceField):
    """Multiple choice field for global IDs with batch processing."""
    
    def to_python(self, value):
        """
        Convert multiple global IDs to Python values.
        
        Parameters:
        - value: List of global ID strings
        
        Returns:
        List of decoded ID values
        """

Usage Examples

Basic Form Mutation

from django import forms
from graphene_django.forms.mutation import DjangoFormMutation
import graphene

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)
    
    def save(self):
        # Custom save logic
        data = self.cleaned_data
        # Send email, save to database, etc.
        return data

class ContactMutation(DjangoFormMutation):
    class Meta:
        form_class = ContactForm
    
    @classmethod
    def perform_mutate(cls, form, info):
        result = form.save()
        return cls(errors=[], **result)

class Mutation(graphene.ObjectType):
    contact_form = ContactMutation.Field()

# GraphQL mutation:
# mutation {
#   contactForm(input: {
#     name: "John Doe"
#     email: "john@example.com"  
#     message: "Hello world"
#   }) {
#     errors {
#       field
#       messages
#     }
#     name
#     email
#   }
# }

Model Form Mutation

from django.db import models
from django import forms
from graphene_django.forms.mutation import DjangoModelFormMutation

class User(models.Model):
    username = models.CharField(max_length=150, unique=True)
    email = models.EmailField()
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ['username', 'email', 'first_name', 'last_name']
    
    def clean_email(self):
        email = self.cleaned_data['email']
        if User.objects.filter(email=email).exists():
            raise forms.ValidationError("Email already exists")
        return email

class CreateUserMutation(DjangoModelFormMutation):
    user = graphene.Field('myapp.schema.UserType')
    
    class Meta:
        form_class = UserForm
        return_field_name = 'user'

class UpdateUserMutation(DjangoModelFormMutation):
    user = graphene.Field('myapp.schema.UserType')
    
    class Meta:
        form_class = UserForm
        return_field_name = 'user'
    
    @classmethod
    def get_form_kwargs(cls, root, info, **input):
        kwargs = super().get_form_kwargs(root, info, **input)
        # Get existing user for update
        user_id = input.get('id')
        if user_id:
            kwargs['instance'] = User.objects.get(pk=user_id)
        return kwargs

Custom Form with File Upload

class ProfileForm(forms.Form):
    avatar = forms.ImageField()
    bio = forms.CharField(widget=forms.Textarea, required=False)
    
    def save(self, user):
        cleaned_data = self.cleaned_data
        user.profile.avatar = cleaned_data['avatar']
        user.profile.bio = cleaned_data['bio']
        user.profile.save()
        return user.profile

class UpdateProfileMutation(DjangoFormMutation):
    profile = graphene.Field('myapp.schema.ProfileType')
    
    class Meta:
        form_class = ProfileForm
    
    @classmethod
    def get_form_kwargs(cls, root, info, **input):
        kwargs = super().get_form_kwargs(root, info, **input)
        # Add files from request
        if hasattr(info.context, 'FILES'):
            kwargs['files'] = info.context.FILES
        return kwargs
    
    @classmethod
    def perform_mutate(cls, form, info):
        profile = form.save(info.context.user)
        return cls(profile=profile, errors=[])

Form with Custom Validation

class PasswordChangeForm(forms.Form):
    old_password = forms.CharField(widget=forms.PasswordInput)
    new_password = forms.CharField(widget=forms.PasswordInput)
    confirm_password = forms.CharField(widget=forms.PasswordInput)
    
    def __init__(self, user, *args, **kwargs):
        self.user = user
        super().__init__(*args, **kwargs)
    
    def clean_old_password(self):
        old_password = self.cleaned_data['old_password']
        if not self.user.check_password(old_password):
            raise forms.ValidationError("Invalid current password")
        return old_password
    
    def clean(self):
        cleaned_data = super().clean()
        new_password = cleaned_data.get('new_password')
        confirm_password = cleaned_data.get('confirm_password')
        
        if new_password and confirm_password:
            if new_password != confirm_password:
                raise forms.ValidationError("Passwords don't match")
        
        return cleaned_data
    
    def save(self):
        self.user.set_password(self.cleaned_data['new_password'])
        self.user.save()

class ChangePasswordMutation(DjangoFormMutation):
    success = graphene.Boolean()
    
    class Meta:
        form_class = PasswordChangeForm
        exclude_fields = ('old_password',)  # Don't expose in GraphQL
    
    @classmethod
    def get_form_kwargs(cls, root, info, **input):
        kwargs = super().get_form_kwargs(root, info, **input)
        kwargs['user'] = info.context.user
        return kwargs
    
    @classmethod
    def perform_mutate(cls, form, info):
        form.save()
        return cls(success=True, errors=[])

Form with Global ID Fields

from graphene_django.forms.forms import GlobalIDFormField, GlobalIDMultipleChoiceField

class AssignTaskForm(forms.Form):
    task_id = GlobalIDFormField()
    assignee_ids = GlobalIDMultipleChoiceField()
    due_date = forms.DateTimeField()
    
    def save(self):
        task = Task.objects.get(pk=self.cleaned_data['task_id'])
        assignees = User.objects.filter(pk__in=self.cleaned_data['assignee_ids'])
        
        task.assignees.set(assignees)
        task.due_date = self.cleaned_data['due_date']
        task.save()
        return task

class AssignTaskMutation(DjangoFormMutation):
    task = graphene.Field('myapp.schema.TaskType')
    
    class Meta:
        form_class = AssignTaskForm
    
    @classmethod
    def perform_mutate(cls, form, info):
        task = form.save()
        return cls(task=task, errors=[])

Nested Form Mutation

class AddressForm(forms.Form):
    street = forms.CharField(max_length=200)
    city = forms.CharField(max_length=100)
    postal_code = forms.CharField(max_length=10)

class UserWithAddressForm(forms.Form):
    username = forms.CharField(max_length=150)
    email = forms.EmailField()
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Extract address data
        address_data = {}
        for key in list(kwargs.get('data', {}).keys()):
            if key.startswith('address_'):
                address_key = key.replace('address_', '')
                address_data[address_key] = kwargs['data'].pop(key)
        
        self.address_form = AddressForm(data=address_data)
    
    def is_valid(self):
        return super().is_valid() and self.address_form.is_valid()
    
    def save(self):
        user = User.objects.create(
            username=self.cleaned_data['username'],
            email=self.cleaned_data['email']
        )
        
        if self.address_form.is_valid():
            Address.objects.create(
                user=user,
                **self.address_form.cleaned_data
            )
        
        return user

Install with Tessl CLI

npx tessl i tessl/pypi-graphene-django

docs

core-types.md

debug.md

fields.md

filtering.md

forms.md

index.md

rest-framework.md

testing.md

views.md

tile.json