{ "info": { "author": "Ambient Innovation: GmbH", "author_email": "hello@ambient-innovation.com", "bugtrack_url": null, "classifiers": [], "description": "# graphene-django-ai\nToolbox for changes to streamline graphene-django\n\n\n## Installation\n\nFor installing graphene, just run this command in your shell\n\n```bash\npip install \"graphene-django-ai>=1.0.0\"\n```\n\n## Setup\n\nRefer to the documentation of `django-graphene` base package. \n\nhttps://github.com/graphql-python/graphene-django/blob/master/README.md\n\n## Interesting to know\n\nSome internal functions of `graphene-django` are monkey-patched inside the `__init__.py`. If you want to take\na look \"under the hood\", have a look at this file.\n\n## Examples\n\n### GraphQL based on django ModelForms\n\nHere is a simple Django model in `my_app/models.py`:\n\n```python\nfrom django.db import models\n\nclass User(models.Model):\n first_name = models.CharField(max_length=100)\n last_name = models.CharField(max_length=100)\n```\n\nNow we create a `ModelForm` in `my_app/forms.py`:\n\n```python\nfrom django import forms\nfrom .models import User\n\nclass SpaceForm(forms.ModelForm):\n class Meta:\n model = User\n exclude = []\n```\n\n We need to create an `ObjectType` which we derive from our model. \n Lives in `my_apps/schemes/schematypes.py`:\n\n```python\nfrom graphene_django import DjangoObjectType\nfrom ..models import User\n\nclass UserType(DjangoObjectType):\n class Meta:\n model = User\n```\n\n Here's the mutation in `my_app/schema/mutations.py`. \n It takes a `ModelForm` (or a non-model form) to derive the validation rules from:\n\n ```python\nimport graphene\nfrom graphene_django_ai.forms.mutations import LoginRequiredDjangoModelFormMutation\nfrom .schematypes import UserType\nfrom ..forms import UserForm\n\n\nclass UserCreateUpdateMutation(LoginRequiredDjangoModelFormMutation):\n space = graphene.Field(UserType)\n\n class Meta:\n form_class = UserForm\n\n\n# Register new mutation\nclass UserMutation(graphene.ObjectType):\n spaces = UserCreateUpdateMutation.Field(description='Create and update users')\n ```\n\n If you register now your `UserMutation` in your schema you have a working model-based and DRY API \n endpoint. Congratulations!\n\n### DeleteMutation for django-model objects\n\nIf you want to delete an object you can easily use the `DeleteMutation` like this:\n\n```python\nfrom graphene_django_ai.schemes.mutations import DeleteMutation\nfrom my_app.models import MyModel\n\nclass MyModelDeleteMutation(DeleteMutation):\n class Meta:\n model = MyModel\n```\n\nIf you are using `django-graphql-jwt` authentication you can ensure only logged in access to your delete endpoint like this:\n\n```python\nfrom graphene_django_ai.schemes.mutations import LoginRequiredDeleteMutation\nfrom my_app.models import MyModel\n\nclass MyModelDeleteMutation(LoginRequiredDeleteMutation):\n class Meta:\n model = MyModel\n```\n\nIf you need to customize the **validation** or the **base queryset** you can override methods like this:\n\n```python\nfrom graphene_django_ai.schemes.mutations import LoginRequiredDeleteMutation\nfrom graphql import GraphQLError\nfrom my_app.models import MyModel\n\nclass MyModelDeleteMutation(LoginRequiredDeleteMutation):\n class Meta:\n model = MyModel\n\n def validate(self, request):\n if not request.user.is_superuser:\n raise GraphQLError(\"This is only allowed for superusers!\")\n\n def get_queryset(self, request):\n return self.model.objects.filter(created_by=request.user)\n```\n\n\n### JWT secure mutations\n\n\nIf you derive your mutation from `LoginRequiredDjangoModelFormMutation` you don't have to manually take\ncare about securing the login with the decorators.\n\n```python\nfrom graphene_django_ai.forms.mutations import LoginRequiredDjangoModelFormMutation\nclass MyMutation(LoginRequiredDjangoModelFormMutation):\n ...\n```\n\n## Testing GraphQL calls\n\nIf you want to unittest your API calls derive your test case from the class `GraphQLTestCase`.\n\nUsage:\n\n```python\nimport json\n\nfrom graphene_django.tests.base_test import GraphQLTestCase\nfrom my_project.config.schema import schema\n\nclass MyFancyTestCase(GraphQLTestCase):\n\n # Here you need to inject your test case's schema\n GRAPHQL_SCHEMA = schema\n\n def test_some_query(self):\n response = self.query(\n '''\n query {\n myModel {\n id\n name\n }\n }\n ''',\n op_name='myModel'\n )\n content = json.loads(response.content)\n # This validates the status code and if you get errors\n self.assertResponseNoErrors(response)\n\n # Add some more asserts if you like\n ...\n\n def test_some_mutation(self):\n response = self.query(\n '''\n mutation myMutation($input: MyMutationInput!) {\n myMutation(input: $input) {\n my-model {\n id\n name\n }\n }\n }\n ''',\n op_name='myMutation',\n input_data={'my_field': 'foo', 'other_field': 'bar'}\n )\n # This validates the status code and if you get errors\n self.assertResponseNoErrors(response)\n\n # Add some more asserts if you like\n ...\n\n def test_failing_call(self):\n\n response = self.query(\n '''\n mutation myMutation($input: MyMutationInput!) {\n myMutation(input: $badInput) {\n my-model {\n id\n name\n }\n }\n }\n ''',\n op_name='myMutation',\n input_data={'my_field': 'foo', 'other_field': 'bar'}\n )\n # This assert tests if the call raised some errors\n # For example if you want to test if invalid input is handled correctly by your endpoint\n self.assertResponseHasErrors(response)\n\n # Add some more asserts if you like\n ... \n\n```\n\n## Run tests locally\n\n**Still W.I.P.!**\n\n python -m unittest discover -v\n\n\n## Relase a new version\n\n- Update `Changelog` in `Readme.md`\n\n- Create pull request / merge to master\n\n- Run:\n\n * Make sure you have all the required packages installed \n `pip install twine wheel`\n * Create a file in your home directory: `~/.pypirc`\n ```\n [distutils]\n index-servers=\n pypi\n testpypi\n\n [pypi]\n repository: https://upload.pypi.org/legacy/\n username: ambient-innovation\n\n [testpypi]\n repository: https://test.pypi.org/legacy/\n username: ambient-innovation\n ```\n * Create distribution \n `python setup.py sdist bdist_wheel`\n * Upload to Test-PyPi \n `twine upload --repository testpypi dist/*`\n * Check at Test-PyPi if it looks nice \n * Upload to real PyPi \n `twine upload dist/*`\n\n## Changelog\n\n* **1.0.4** (2019-04-05)\n * Fixed a bug that a BooleanField from a django form would always be required\n\n* **1.0.3** (2019-04-05)\n * Added delete mutation for django-model objects `DeleteMutation`\n * Added delete mutation which ensures JWT authentication\n\n* **1.0.2** (2019-04-05)\n * Updated deployment documentation\n * Added markdown support to Readme file\n\n* **1.0.1** (2019-04-05)\n * Added documentation about `GraphQLTestCase` \n * Put version to variable in `__init__.py`\n\n* **1.0.0** (2019-04-04)\n * Initial package released\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/ambient-innovation/graphene-django-ai", "keywords": "", "license": "The MIT License (MIT)", "maintainer": "", "maintainer_email": "", "name": "graphene-django-ai", "package_url": "https://pypi.org/project/graphene-django-ai/", "platform": "", "project_url": "https://pypi.org/project/graphene-django-ai/", "project_urls": { "Homepage": "https://github.com/ambient-innovation/graphene-django-ai" }, "release_url": "https://pypi.org/project/graphene-django-ai/1.0.4/", "requires_dist": [ "django (>=2.0.0)", "graphene-django (==2.2.0)", "django-graphql-jwt (>=0.2.1)" ], "requires_python": "", "summary": "Toolbox for changes to streamline graphene-django.", "version": "1.0.4" }, "last_serial": 5103469, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "e477202180a497bbc141897517daaf9d", "sha256": "71aa2f7e2264741249ad6e06801d7523865bca0e0bda4d38de7bc567a1dc1572" }, "downloads": -1, "filename": "graphene-django-ai-1.0.0.tar.gz", "has_sig": false, "md5_digest": "e477202180a497bbc141897517daaf9d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6003, "upload_time": "2019-04-04T12:34:43", "url": "https://files.pythonhosted.org/packages/32/db/0cd5ca050fcc75996608dd15297a4949584bb8cf9a8dba40308ded82c7dc/graphene-django-ai-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "e20a8a699a4d9b5f933f09b5923c806f", "sha256": "251ea43fd54a224efcedc6a2f8b743d5f099f4ae512c6cc70511efc039b32188" }, "downloads": -1, "filename": "graphene-django-ai-1.0.1.tar.gz", "has_sig": false, "md5_digest": "e20a8a699a4d9b5f933f09b5923c806f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7193, "upload_time": "2019-04-05T09:17:38", "url": "https://files.pythonhosted.org/packages/71/59/94a6d145aecaefb71c93bc8805acc77d7b85469d970e1617b765d2e12198/graphene-django-ai-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "2e139ff498be0d2c63dc36c37ee411d4", "sha256": "01239e1b39a4a1a2ce6578016df63399969ab6f482ec667c70d6e33b99389e0c" }, "downloads": -1, "filename": "graphene_django_ai-1.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "2e139ff498be0d2c63dc36c37ee411d4", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 9138, "upload_time": "2019-04-05T09:31:02", "url": "https://files.pythonhosted.org/packages/d2/ab/e5f17ae6280729ce771010107ccaf0070fd1ed1d3d69430fc3405c5fe53f/graphene_django_ai-1.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bb7e2021539abb011ebdc279d78da4d3", "sha256": "1043e3e3f5a1ea4f873866f62efa3b2ad710b0c14d847c99dd3c475c04de5966" }, "downloads": -1, "filename": "graphene-django-ai-1.0.2.tar.gz", "has_sig": false, "md5_digest": "bb7e2021539abb011ebdc279d78da4d3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7389, "upload_time": "2019-04-05T09:31:05", "url": "https://files.pythonhosted.org/packages/cf/78/a1a4d835ef4977d7cb4440ae6f2ba64413d89d7a465a380712657707578d/graphene-django-ai-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "21527ca65b7d7120cd3ad89712cfcb2e", "sha256": "3e1ef0d5bd649517924d1cfe8a52cb88d242cd7293be7582bc7a912083c461bb" }, "downloads": -1, "filename": "graphene_django_ai-1.0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "21527ca65b7d7120cd3ad89712cfcb2e", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10540, "upload_time": "2019-04-05T11:44:44", "url": "https://files.pythonhosted.org/packages/4b/4a/f607035f349f5c4bd638560d26a5d5f27876631fd17344654e8d865ee71b/graphene_django_ai-1.0.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4c90b06a7564a8ba0a4820e594748e0a", "sha256": "e8f42618e99a8bc251fda546d174a9e92ca8a315520817948e8597799f45d5ef" }, "downloads": -1, "filename": "graphene-django-ai-1.0.3.tar.gz", "has_sig": false, "md5_digest": "4c90b06a7564a8ba0a4820e594748e0a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8436, "upload_time": "2019-04-05T11:44:47", "url": "https://files.pythonhosted.org/packages/26/d7/a5aa06c2acc313eb26e023009936983ff5e297f5972c47239cae4304b7d6/graphene-django-ai-1.0.3.tar.gz" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "87fafc5f736396b155eb7c31475234b1", "sha256": "0070a920679be2f746139b49b5104ac67b02d85ab6a491cc371939e66d074fa3" }, "downloads": -1, "filename": "graphene_django_ai-1.0.4-py3-none-any.whl", "has_sig": false, "md5_digest": "87fafc5f736396b155eb7c31475234b1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10621, "upload_time": "2019-04-05T12:38:08", "url": "https://files.pythonhosted.org/packages/70/1a/9b60c99f8888f8d3f81ad459ce80b38c0e70928fa295137102552d0214b6/graphene_django_ai-1.0.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e4a31ac70896a3be656ede4674eafec0", "sha256": "0c3868563f0937643d9ea82a0367f4014b924e73e202e473b67b44deea57b994" }, "downloads": -1, "filename": "graphene-django-ai-1.0.4.tar.gz", "has_sig": false, "md5_digest": "e4a31ac70896a3be656ede4674eafec0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8542, "upload_time": "2019-04-05T12:38:12", "url": "https://files.pythonhosted.org/packages/92/ec/9aac547702037a2a271a94c1a8aba86fb916db6f828abf9f0ecbceb23926/graphene-django-ai-1.0.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "87fafc5f736396b155eb7c31475234b1", "sha256": "0070a920679be2f746139b49b5104ac67b02d85ab6a491cc371939e66d074fa3" }, "downloads": -1, "filename": "graphene_django_ai-1.0.4-py3-none-any.whl", "has_sig": false, "md5_digest": "87fafc5f736396b155eb7c31475234b1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 10621, "upload_time": "2019-04-05T12:38:08", "url": "https://files.pythonhosted.org/packages/70/1a/9b60c99f8888f8d3f81ad459ce80b38c0e70928fa295137102552d0214b6/graphene_django_ai-1.0.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e4a31ac70896a3be656ede4674eafec0", "sha256": "0c3868563f0937643d9ea82a0367f4014b924e73e202e473b67b44deea57b994" }, "downloads": -1, "filename": "graphene-django-ai-1.0.4.tar.gz", "has_sig": false, "md5_digest": "e4a31ac70896a3be656ede4674eafec0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8542, "upload_time": "2019-04-05T12:38:12", "url": "https://files.pythonhosted.org/packages/92/ec/9aac547702037a2a271a94c1a8aba86fb916db6f828abf9f0ecbceb23926/graphene-django-ai-1.0.4.tar.gz" } ] }