Metadata-Version: 2.1
Name: django-model-mutations
Version: 0.1.1
Summary: Graphene Django mutations for Django models made easier
Home-page: https://github.com/topletal/django-model-mutations
Author: Tomáš Opletal
Author-email: t.opletal@gmail.com
License: MIT
Description: # Django Model Mutations
        
        This package adds Mutation classes that make creating graphene mutations with django models easier using Django Rest Framework serializers. It extends graphene Mutation class in a similar way to Django Rest Framework views or original Django views.
        
        It also provides easy way to add permissions checks or ensure logged-in user, as well as easy way to override or add funcionality similar to django forms or rest framework views - such as ```get_queryset()``` or ```save()``` functions.
        
        There is also advanced error reporting from rest framework, that returns non-valid fields and error messages.
        
        Inspired by [Saleor](https://github.com/mirumee/saleor), [graphene-django-extras](https://github.com/eamigo86/graphene-django-extras/tree/master/graphene_django_extras) and [django-rest-framework](https://github.com/encode/django-rest-framework)
        
        ## Installation
        ```
        pip install django-model-mutations
        ```
        
        
        ## Basic Usage
        Main classes that this package provides:
        
        | mutations | mixins |
        | ------ | ------ |
        |CreateModelMutation|LoginRequiredMutationMixin|
        |CreateBulkModelMutation|
        |UpdateModelMutation|
        |UpdateBulkModelMutation|
        |DeleteModelMutation|
        |DeleteBulkModelMutation|
        
        
        #### Django usage
        Input type (Arguments) is generated from serializer fields  
        Return type is retrieved by model from global graphene registry, you just have to import it as in example
        ```python
        from django_model_mutations import mutations, mixins
        from your_app.types import UserType  # important to import types to register in global registry
        from your_app.serializers import UserSerializer
        
        
        # Create Mutations
        # use mixins.LoginRequiredMutationMixin to ensure only logged-in user can perform this mutation
        # MAKE SURE this mixin is FIRST in inheritance order
        class UserCreateMutation(mixins.LoginRequiredMutationMixin, mutations.CreateModelMutation):
            class Meta:
                serializer_class = UserSerializer
                # OPTIONAL META FIELDS:
                permissions = ('your_app.user_permission',) # OPTIONAL: specify user permissions
                lookup_field = 'publicId'  # OPTIONAL: specify database lookup column, default is 'id' or 'ids'
                return_field_name = 'myUser' # OPTIONAL: specify return field name, default is model name
                input_field_name = 'myUser' # OPTIONAL: specify input field name, defauls is 'input'
                
        
        class UserBulkCreateMutation(mutations.CreateBulkModelMutation):
            class Meta:
                serializer_class = UserSerializer
        
        
        # Update Mutations
        class UserUpdateMutation(mutations.UpdateModelMutation):
            class Meta:
                serializer_class = UserSerializer
        
        # WARNING: Bulk update DOES NOT USE serializer, due to limitations of rest framework serializer. 
        # Instead specify model and argument fields by yourself.
        class UserBulkUpdateMutation(mutations.UpdateBulkModelMutation):
            class Arguments:
                is_active = graphene.Boolean()
        
            class Meta:
                model = User
        
        # Delete Mutations
        # delete mutations doesn't use serializers, as there is no need
        class UserDeleteMutation(mutations.DeleteModelMutation):
            class Meta:
                model = User
        
        class UserBulkDeleteMutation(mutations.DeleteBulkModelMutation):
            class Meta:
                model = User
        
        
        # Add to graphene schema as usual
        class Mutation(graphene.ObjectType):
            user_create = UserCreateMutation.Field()
            ....
        
        schema = graphene.Schema(mutation=Mutation)
        ```
        
        
        #### GraphQl usage
        The generated GraphQl schema can be modified with ```Meta``` fields as described above in ```UserCreateMutation```.
        
        By default all mutations have ```errors``` field with ```field``` and ```messages``` that contain validation errors from rest-framework serializer or lookup errors. For now permission denied and other exceptions will not use this error reporting, but a default one, for usage see tests.
        ```graphql
        # default argument name is input
        # default return field name is model name
        mutation userCreate (input: {username: "myUsername"}) {
            user {
                id
                username
            }
            errors {
                field
                messages
            }
        }
        
        
        # Bulk operations return 'count' and errors
        mutation userCreate (input: {username: "myUsername"}) {
            count
            errors {
                field
                messages
            }
        }
        
        # update mutations
        # update and delete mutations by default specify lookup field 'id' or 'ids' for bulk mutations
        mutation {
            userUpdate (id: 2, input: {username:"newUsername"} ) {
                user {
                    id
                    username
                }  
                errors {
                    field
                    messages
                }
            } 
        }   
        
        
        mutation {
            userBulkUpdate (ids: [2, 3], isActive: false ) {
                count
                errors {
                   field
                   messages
                }
            }
        }  
        
        
        # delete mutations
        mutation {
            userDelete (id: 1) {
                user {
                    id
                }
                errors {
                   field
                   messages
                }
            }
        }  
        
        
        mutation {
            userBulkDelete (ids: [1, 2, 3]) {
                count
                errors {
                   field
                   messages
                }
            }
        }  
        ```
        
        ### Adding funcionality
        All classes are derived from ```graphene.Mutation```. When you want to override some major functionality, the best place probabably is ```perform_mutate```, which is called after permission checks from graphene ```mutate```.  
        
        In general probably the main functions that you want to override are: ```save()``` and ```get_object()``` for single object mutations or ```get_queryset()``` for bulk mutations.  
        ```get_object``` or ```get_queryset``` you should override to add more filters for fetching the object.   
        ```save``` performs final save/update/delete to database and you can add additional fields there.
        
        Examples:
        ```python
        # lets only update users that are inactive and add some random field
        class UserUpdateInactiveMutation(mutations.UpdateModelMutation):
            class Meta:
                model = User
        
            @classmethod
            def get_object(cls, object_id, info, **input):
            # can get the object first and then check
                obj = super(UserUpdateInactiveMutation, cls).get_object(object_id, info, **input)
                if obj.is_active:
                    return None
                return obj
        
            @classmethod
            def save(cls, serializer, root, info, **input):
                saved_object = serializer.save(updated_by=info.context.user)
                return cls.return_success(saved_object)
        
        
        # same but for bulk mutation we have to override get_queryset
        class UserBulkUpdateInactiveMutation(mutations.UpdateBulkModelMutation):
            class Meta:
                model = User
        
            @classmethod
            def get_queryset(cls, object_ids, info, **input):
                qs = super(UserBulkUpdateInactiveMutation, cls).get_queryset(object_ids, info, **input)
                qs.filter(is_active=False)
                return qs
        ```
        
        For the whole function flow, please check the Base models in ```django_model_mutations\mutations.py```.
        It was inspired by rest framework, so you can find functions like ```get_serializer_kwargs```, ```get_serializer```, ```validate_instance``` (for example here you can override default ```ValidationError``` exception and return None if you don't want exception of non existing id lookup etc.)
        
        ## Contributing
        Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
        
        Please make sure to update tests as appropriate.
        
        ## License
        [MIT](https://choosealicense.com/licenses/mit/)
        
Keywords: GRAPHENE,GRAPHENE-DJANGO,GRAPHQL,DJANGO,MODELS,API
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Build Tools
Classifier: License :: OSI Approved :: MIT License
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Description-Content-Type: text/markdown
