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

rest-framework.mddocs/

REST Framework Integration

Django REST Framework serializer integration for GraphQL mutations with comprehensive validation, model operations, and seamless DRF feature support. Enables reuse of existing DRF serializers in GraphQL mutations with full validation and error handling.

Capabilities

SerializerMutation

Mutation class using Django REST Framework serializers with automatic input type generation and comprehensive model operations.

class SerializerMutation(graphene.relay.ClientIDMutation):
    """
    Mutation class using Django REST Framework serializers.
    
    Automatically generates GraphQL input types from DRF serializers and
    provides standardized error reporting with full DRF validation support.
    """
    errors = graphene.List(ErrorType)
    
    class Meta:
        """
        Meta options for serializer mutations.
        
        Attributes:
        - serializer_class: DRF serializer class to use
        - model_operations: Operations to support ('create', 'update', or both)
        - lookup_field: Field for object lookup in updates (default: 'id')
        - return_field_name: Name for successful result field
        """
        serializer_class = None
        model_operations = ['create', 'update']
        lookup_field = 'id'
        return_field_name = None
    
    @classmethod
    def __init_subclass_with_meta__(cls, serializer_class=None, model_operations=None,
                                   lookup_field=None, return_field_name=None, **options):
        """
        Configure serializer mutation subclass.
        
        Parameters:
        - serializer_class: DRF serializer class
        - model_operations: Supported operations list
        - lookup_field: Field for instance lookup
        - return_field_name: Result field name
        - **options: Additional mutation options
        """
    
    @classmethod
    def mutate_and_get_payload(cls, root, info, **input):
        """
        Main mutation logic with serializer processing.
        
        Parameters:
        - root: GraphQL root object
        - info: GraphQL execution info
        - **input: Mutation input arguments
        
        Returns:
        Mutation result with serializer data or errors
        """
    
    @classmethod
    def get_serializer_kwargs(cls, root, info, **input):
        """
        Get serializer constructor arguments.
        
        Parameters:
        - root: GraphQL root object
        - info: GraphQL execution info
        - **input: Serializer input data
        
        Returns:
        dict: Serializer constructor keyword arguments
        """
    
    @classmethod
    def get_serializer(cls, root, info, **input):
        """
        Get serializer instance for validation.
        
        Parameters:
        - root: GraphQL root object
        - info: GraphQL execution info
        - **input: Serializer input data
        
        Returns:
        rest_framework.serializers.Serializer: Serializer instance
        """
    
    @classmethod
    def perform_mutate(cls, serializer, info):
        """
        Perform mutation with validated serializer.
        
        Parameters:
        - serializer: Validated DRF serializer
        - info: GraphQL execution info
        
        Returns:
        Mutation result object
        """

DictType

Dictionary type for key-value pairs in GraphQL schemas with flexible data representation.

class DictType(graphene.UnmountedType):
    """
    Dictionary type for key-value pairs.
    
    Represents dictionary/mapping data structures in GraphQL with
    flexible key-value pair support for dynamic data.
    """
    key = graphene.String()
    value = graphene.String()
    
    @classmethod
    def serialize(cls, value):
        """
        Serialize dictionary to GraphQL format.
        
        Parameters:
        - value: Dictionary to serialize
        
        Returns:
        List of key-value pairs
        """

Usage Examples

Basic Serializer Mutation

from rest_framework import serializers
from graphene_django.rest_framework.mutation import SerializerMutation
import graphene

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username', 'email', 'first_name', 'last_name']
    
    def validate_email(self, value):
        if User.objects.filter(email=value).exists():
            raise serializers.ValidationError("Email already exists")
        return value

class CreateUserMutation(SerializerMutation):
    user = graphene.Field('myapp.schema.UserType')
    
    class Meta:
        serializer_class = UserSerializer
        model_operations = ['create']
        return_field_name = 'user'

class Mutation(graphene.ObjectType):
    create_user = CreateUserMutation.Field()

# GraphQL mutation:
# mutation {
#   createUser(input: {
#     username: "johndoe"
#     email: "john@example.com"
#     firstName: "John"
#     lastName: "Doe"
#   }) {
#     user {
#       id
#       username
#       email
#     }
#     errors {
#       field
#       messages
#     }
#   }
# }

Update Mutation with Instance Lookup

class UpdateUserMutation(SerializerMutation):
    user = graphene.Field('myapp.schema.UserType')
    
    class Meta:
        serializer_class = UserSerializer
        model_operations = ['update']
        lookup_field = 'id'
        return_field_name = 'user'
    
    @classmethod
    def get_serializer_kwargs(cls, root, info, **input):
        kwargs = super().get_serializer_kwargs(root, info, **input)
        
        # Get instance for update
        instance_id = input.get('id')
        if instance_id:
            try:
                kwargs['instance'] = User.objects.get(pk=instance_id)
            except User.DoesNotExist:
                raise Exception("User not found")
        
        return kwargs

# GraphQL mutation:
# mutation {
#   updateUser(input: {
#     id: "1"
#     email: "newemail@example.com"
#     firstName: "Jane"
#   }) {
#     user {
#       id
#       email
#       firstName
#     }
#     errors {
#       field
#       messages  
#     }
#   }
# }

Nested Serializer Mutation

class AddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = Address
        fields = ['street', 'city', 'postal_code', 'country']

class UserWithAddressSerializer(serializers.ModelSerializer):
    address = AddressSerializer()
    
    class Meta:
        model = User
        fields = ['username', 'email', 'address']
    
    def create(self, validated_data):
        address_data = validated_data.pop('address')
        user = User.objects.create(**validated_data)
        Address.objects.create(user=user, **address_data)
        return user
    
    def update(self, instance, validated_data):
        address_data = validated_data.pop('address', None)
        
        # Update user fields
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        
        # Update address
        if address_data:
            address, created = Address.objects.get_or_create(user=instance)
            for attr, value in address_data.items():
                setattr(address, attr, value)
            address.save()
        
        return instance

class CreateUserWithAddressMutation(SerializerMutation):
    user = graphene.Field('myapp.schema.UserType')
    
    class Meta:
        serializer_class = UserWithAddressSerializer
        model_operations = ['create']
        return_field_name = 'user'

Custom Serializer with Method Fields

class UserProfileSerializer(serializers.ModelSerializer):
    full_name = serializers.SerializerMethodField()
    posts_count = serializers.SerializerMethodField()
    avatar_url = serializers.SerializerMethodField()
    
    class Meta:
        model = User
        fields = ['username', 'email', 'full_name', 'posts_count', 'avatar_url']
        read_only_fields = ['full_name', 'posts_count', 'avatar_url']
    
    def get_full_name(self, obj):
        return f"{obj.first_name} {obj.last_name}".strip()
    
    def get_posts_count(self, obj):
        return obj.post_set.count()
    
    def get_avatar_url(self, obj):
        if obj.profile.avatar:
            return obj.profile.avatar.url
        return None
    
    def update(self, instance, validated_data):
        # Custom update logic
        instance.username = validated_data.get('username', instance.username)
        instance.email = validated_data.get('email', instance.email)
        instance.save()
        
        # Trigger profile update
        instance.profile.last_updated = timezone.now()
        instance.profile.save()
        
        return instance

class UpdateUserProfileMutation(SerializerMutation):
    user = graphene.Field('myapp.schema.UserType')
    
    class Meta:
        serializer_class = UserProfileSerializer
        model_operations = ['update']
        return_field_name = 'user'

Serializer with Custom Validation

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['title', 'content', 'category', 'tags', 'published']
    
    def validate_title(self, value):
        if len(value) < 5:
            raise serializers.ValidationError("Title must be at least 5 characters")
        return value
    
    def validate(self, data):
        if data.get('published') and not data.get('content'):
            raise serializers.ValidationError({
                'content': 'Content is required for published posts'
            })
        return data
    
    def create(self, validated_data):
        validated_data['author'] = self.context['request'].user
        return super().create(validated_data)

class CreatePostMutation(SerializerMutation):
    post = graphene.Field('myapp.schema.PostType')
    
    class Meta:
        serializer_class = PostSerializer
        model_operations = ['create']
        return_field_name = 'post'
    
    @classmethod
    def get_serializer_kwargs(cls, root, info, **input):
        kwargs = super().get_serializer_kwargs(root, info, **input)
        kwargs['context'] = {'request': info.context}
        return kwargs

File Upload with Serializer

class DocumentSerializer(serializers.ModelSerializer):
    file = serializers.FileField()
    
    class Meta:
        model = Document
        fields = ['title', 'description', 'file', 'category']
    
    def validate_file(self, value):
        # Validate file size (5MB limit)
        if value.size > 5 * 1024 * 1024:
            raise serializers.ValidationError("File size cannot exceed 5MB")
        
        # Validate file type
        allowed_types = ['application/pdf', 'image/jpeg', 'image/png']
        if value.content_type not in allowed_types:
            raise serializers.ValidationError("File type not allowed")
        
        return value
    
    def create(self, validated_data):
        validated_data['uploaded_by'] = self.context['request'].user
        return super().create(validated_data)

class UploadDocumentMutation(SerializerMutation):
    document = graphene.Field('myapp.schema.DocumentType')
    
    class Meta:
        serializer_class = DocumentSerializer
        model_operations = ['create']
        return_field_name = 'document'
    
    @classmethod
    def get_serializer_kwargs(cls, root, info, **input):
        kwargs = super().get_serializer_kwargs(root, info, **input)
        kwargs['context'] = {'request': info.context}
        
        # Handle file upload
        if hasattr(info.context, 'FILES'):
            kwargs['data'] = {**input, **info.context.FILES}
        
        return kwargs

Many-to-Many Relationship Handling

class ProjectSerializer(serializers.ModelSerializer):
    team_members = serializers.PrimaryKeyRelatedField(
        many=True, 
        queryset=User.objects.all()
    )
    
    class Meta:
        model = Project  
        fields = ['name', 'description', 'team_members', 'deadline']
    
    def create(self, validated_data):
        team_members = validated_data.pop('team_members', [])
        project = Project.objects.create(**validated_data)
        project.team_members.set(team_members)
        return project
    
    def update(self, instance, validated_data):
        team_members = validated_data.pop('team_members', None)
        
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        
        if team_members is not None:
            instance.team_members.set(team_members)
        
        return instance

class CreateProjectMutation(SerializerMutation):
    project = graphene.Field('myapp.schema.ProjectType')
    
    class Meta:
        serializer_class = ProjectSerializer
        model_operations = ['create', 'update']
        return_field_name = 'project'

Custom Error Handling

class CustomSerializerMutation(SerializerMutation):
    @classmethod
    def mutate_and_get_payload(cls, root, info, **input):
        try:
            return super().mutate_and_get_payload(root, info, **input)
        except serializers.ValidationError as e:
            # Custom error formatting
            errors = []
            if hasattr(e, 'detail'):
                for field, messages in e.detail.items():
                    if isinstance(messages, list):
                        for message in messages:
                            errors.append(ErrorType(field=field, messages=[str(message)]))
                    else:
                        errors.append(ErrorType(field=field, messages=[str(messages)]))
            
            return cls(errors=errors)
        except Exception as e:
            # Log unexpected errors
            logger.error(f"Mutation error: {e}")
            return cls(errors=[ErrorType(field='__all__', messages=[str(e)])])

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