# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['graphene_django_plus']

package_data = \
{'': ['*']}

setup_kwargs = {
    'name': 'graphene-django-plus',
    'version': '3.1.3',
    'description': 'Tools to easily create permissioned CRUD endpoints in graphene.',
    'long_description': '# graphene-django-plus #\n\n[![build status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2F0soft%2Fgraphene-django-plus%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/0soft/graphene-django-plus/goto?ref=master)\n[![docs status](https://img.shields.io/readthedocs/graphene-django-plus.svg)](https://graphene-django-plus.readthedocs.io)\n[![coverage](https://img.shields.io/codecov/c/github/0soft/graphene-django-plus.svg)](https://codecov.io/gh/0soft/graphene-django-plus)\n[![PyPI version](https://img.shields.io/pypi/v/graphene-django-plus.svg)](https://pypi.org/project/graphene-django-plus/)\n![python version](https://img.shields.io/pypi/pyversions/graphene-django-plus.svg)\n![django version](https://img.shields.io/pypi/djversions/graphene-django-plus.svg)\n\nTools to easily create permissioned CRUD endpoints in [graphene-django](https://github.com/graphql-python/graphene-django).\n\n## Install\n\n```bash\npip install graphene-django-plus\n```\n\nTo make use of everything this lib has to offer, it is recommended to install\nboth [graphene-django-optimizer](https://github.com/tfoxy/graphene-django-optimizer)\nand [django-guardian](https://github.com/django-guardian/django-guardian).\n\n```bash\npip install graphene-django-optimizer django-guardian\n```\n\n## What it does\n\n* Provides some base types for Django Models to improve querying them with:\n    * Unauthenticated user handling\n    * Automatic optimization using [graphene-django-optimizer](https://github.com/tfoxy/graphene-django-optimizer)\n    * Permission handling for queries using the default [django permission system](https://docs.djangoproject.com/en/2.2/topics/auth/default/#topic-authorization)\n    * Object permission handling for queries using [django guardian](https://github.com/django-guardian/django-guardian)\n    * Relay id conversion so querying can use the global id instead of the model\'s id\n* Provides a set of complete and simple CRUD mutations with:\n    * Unauthenticated user handling\n    * Permission handling using the default [django permission system](https://docs.djangoproject.com/en/2.2/topics/auth/default/#topic-authorization)\n    * Object permission handling using [django guardian](https://github.com/django-guardian/django-guardian)\n    * Automatic input generation based on the model (no need to write your own input type or use `django forms` and `drf serializers`)\n    * Automatic model validation based on the model\'s validators\n* Very simple to create some quick CRUD endpoints for your models\n* Easy to extend and override functionalities\n* File upload handling\n\n## What is included\n\nCheck the [docs](https://graphene-django-plus.readthedocs.io) for a complete\napi documentation.\n\n### Models\n\n* `graphene_django_plus.models.GuardedModel`: A django model that can be used\n  either directly or as a mixin. It will provide a `.has_perm` method and a\n  `.objects.for_user` that will be used by `ModelType` described bellow to\n  check for object permissions.  some utilities to check.\n\n### Types and Queries\n\n* `graphene_django_plus.types.ModelType`: This enchances\n  `graphene_django_plus.DjangoModelType` by doing some automatic `prefetch`\n  optimization on setup and also checking for objects permissions on queries\n  when it inherits from `GuardedModel`.\n\n* `graphene_django_plus.fields.CountableConnection`: This enchances\n  `graphene.relay.Connection` to provide a `total_count` attribute.\n\nHere is an example describing how to use those:\n\n```py\nimport graphene\nfrom graphene import relay\nfrom graphene_django.fields import DjangoConnectionField\n\nfrom graphene_django_plus.models import GuardedModel\nfrom graphene_django_plus.types import ModelType\nfrom graphene_django_plus.fields import CountableConnection\n\n\nclass MyModel(GuardedModel):\n    class Meta:\n        # guardian permissions for this model\n        permissions = [\n            (\'can_read\', "Can read the this object\'s info."),\n        ]\n\n    name = models.CharField(max_length=255)\n\n\nclass MyModelType(ModelType):\n    class Meta:\n        model = MyModel\n        interfaces = [relay.Node]\n\n        # Use our CountableConnection\n        connection_class = CountableConnection\n\n        # When adding this to a query, only objects with a `can_read`\n        # permission to the request\'s user will be allowed to return to him\n        # Note that `can_read` was defined in the model.\n        # If the model doesn\'t inherid from `GuardedModel`, `guardian` is not\n        # installed or this list is empty, any object will be allowed.\n        # This is empty by default\n        object_permissions = [\n            \'can_read\',\n        ]\n\n        # If unauthenticated users should be allowed to retrieve any object\n        # of this type. This is not dependant on `GuardedModel` and neither\n        # `guardian` and is defined as `False` by default\n        public = False\n\n        # A list of Django model permissions to check. Different from\n        # object_permissions, this uses the basic Django\'s permission system\n        # and thus is not dependant on `GuardedModel` and neither `guardian`.\n        # This is an empty list by default.\n        permissions = []\n\n\nclass Query(graphene.ObjectType):\n    my_models = DjangoConnectionField(MyModelType)\n    my_model = relay.Node.Field(MyModelType)\n```\n\nThis can be queried like:\n\n```graphql\n# All objects that the user has permission to see\nquery {\n  myModels {\n    totalCount\n    edges {\n      node {\n        id\n        name\n      }\n    }\n  }\n}\n\n# Single object if the user has permission to see it\nquery {\n  myModel(id: "<relay global ID>") {\n    id\n    name\n  }\n}\n```\n\n### Mutations\n\n* `graphene_django_plus.mutations.BaseMutation`: Base mutation using `relay`\n  and some basic permission checking. Just override its `.perform_mutation` to\n  perform the mutation.\n\n* `graphene_django_plus.mutations.ModelMutation`: Model mutation capable of\n  both creating and updating a model based on the existence of an `id`\n  attribute in the input. All the model\'s fields will be automatically read\n  from Django, inserted in the input type and validated.\n\n* `graphene_django_plus.mutations.ModelCreateMutation`: A `ModelMutation`\n  enforcing a "create only" rule by excluding the `id` field from the input.\n\n* `graphene_django_plus.mutations.ModelUpdateMutation`: A `ModelMutation`\n  enforcing a "update only" rule by making the `id` field required in the\n  input.\n\n* `graphene_django_plus.mutations.ModelDeleteMutation`: A mutation that will\n  receive only the model\'s id and will delete it (if given permission, of\n  course).\n\nHere is an example describing how to use those:\n\n```py\nimport graphene\nfrom graphene import relay\n\nfrom graphene_django_plus.models import GuardedModel\nfrom graphene_django_plus.types import ModelType\nfrom graphene_django_plus.mutations import (\n    ModelCreateMutation,\n    ModelUpdateMutation,\n    ModelDeleteMutation,\n)\n\n\nclass MyModel(GuardedModel):\n    class Meta:\n        # guardian permissions for this model\n        permissions = [\n            (\'can_write\', "Can update this object\'s info."),\n        ]\n\n    name = models.CharField(max_length=255)\n\n\nclass MyModelType(ModelType):\n    class Meta:\n        model = MyModel\n        interfaces = [relay.Node]\n\n\nclass MyModelUpdateMutation(ModelUpdateMutation):\n    class Meta:\n        model = MyModel\n\n        # Make sure only users with the given permissions can modify the\n        # object.\n        # If the model doesn\'t inherid from `GuardedModel`, `guardian` is not\n        # installed ot this list is empty, any object will be allowed.\n        # This is empty by default.\n        object_permissions = [\n            \'can_write\',\n        ]\n\n        # If unauthenticated users should be allowed to retrieve any object\n        # of this type. This is not dependant on `GuardedModel` and neither\n        # `guardian` and is defined as `False` by default\n        public = False\n\n        # A list of Django model permissions to check. Different from\n        # object_permissions, this uses the basic Django\'s permission system\n        # and thus is not dependant on `GuardedModel` and neither `guardian`.\n        # This is an empty list by default.\n        permissions = []\n\n\nclass MyModelDeleteMutation(ModelDeleteMutation):\n    class Meta:\n        model = MyModel\n        object_permissions = [\n            \'can_write\',\n        ]\n\n\nclass MyModelCreateMutation(ModelCreateMutation):\n    class Meta:\n        model = MyModel\n\n    @classmethod\n    def after_save(cls, info, instance, cleaned_input=None):\n        # If the user created the object, allow him to modify it\n        assign_perm(\'can_write\', info.context.user, instance)\n\n\nclass Mutation(graphene.ObjectType):\n    my_model_create = MyModelCreateMutation.Field()\n    my_model_update = MyModelUpdateMutation.Field()\n    my_model_delete = MyModelDeleteMutation.Field()\n```\n\nThis can be used to create/update/delete like:\n\n```graphql\n# Create mutation\nmutation {\n  myModelCreate(input: {name: "foobar"}) {\n    myModel {\n      name\n    }\n    errors {\n      field\n      message\n    }\n  }\n}\n\n# Update mutation\nmutation {\n  myModelUpdate(input: {id: "<relay global ID>" name: "foobar"}) {\n    myModel {\n      name\n    }\n    errors {\n      field\n      message\n    }\n  }\n}\n\n# Delete mutation\nmutation {\n  myModelDelete(input: {id: "<relay global ID>"}) {\n    myModel {\n      name\n    }\n    errors {\n      field\n      message\n    }\n  }\n}\n```\n\nAny validation errors will be presented in the `errors` return value.\n\nTo turn off auto related relations addition to the mutation input - set global\n`MUTATIONS_INCLUDE_REVERSE_RELATIONS` parameter to `False` in your\n`settings.py`:\n\n```\nGRAPHENE_DJANGO_PLUS = {\n    \'MUTATIONS_INCLUDE_REVERSE_RELATIONS\': False\n}\n```\n\nNote: in case reverse relation does not have `related_name` attribute set -\nmutation input will be generated as Django itself is generating by appending\n`_set` to the lower cased model name - `modelname_set`\n\n## License\n\nThis project is licensed under MIT licence (see `LICENSE` for more info)\n\n## Contributing\n\nMake sure to have [poetry](https://python-poetry.org/) installed.\n\nInstall dependencies with:\n\n```bash\npoetry install\n```\n\nRun the testsuite with:\n\n```bash\npoetry run pytest\n```\n\nFeel free to fork the project and send me pull requests with new features,\ncorrections and translations. We\'ll gladly merge them and release new versions\nASAP.\n',
    'author': 'Thiago Bellini Ribeiro',
    'author_email': 'thiago@bellini.dev',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/0soft/graphene-django-plus',
    'packages': packages,
    'package_data': package_data,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
