From 00d8b2273ff5dfa996d893cd0961685341eaba1d Mon Sep 17 00:00:00 2001 From: Eduard Erja Date: Fri, 19 Jul 2019 19:23:57 +0300 Subject: [PATCH 1/4] Added django_mongoengine example app --- examples/django_mongoengine/.gitignore | 1 + examples/django_mongoengine/README.md | 56 ++++++++ examples/django_mongoengine/__init__.py | 0 examples/django_mongoengine/bike/__init__.py | 0 examples/django_mongoengine/bike/apps.py | 5 + examples/django_mongoengine/bike/fixtures.py | 28 ++++ .../bike/migrations/__init__.py | 0 examples/django_mongoengine/bike/models.py | 12 ++ examples/django_mongoengine/bike/schema.py | 12 ++ examples/django_mongoengine/bike/tests.py | 87 +++++++++++ examples/django_mongoengine/bike/types.py | 9 ++ examples/django_mongoengine/bike/urls.py | 7 + .../bike_catalog/__init__.py | 0 .../bike_catalog/settings.py | 136 ++++++++++++++++++ .../bike_catalog/settings_test.py | 3 + .../django_mongoengine/bike_catalog/urls.py | 22 +++ .../django_mongoengine/bike_catalog/wsgi.py | 16 +++ examples/django_mongoengine/manage.py | 21 +++ examples/django_mongoengine/pytest.ini | 3 + examples/django_mongoengine/requirements.txt | 7 + 20 files changed, 425 insertions(+) create mode 100644 examples/django_mongoengine/.gitignore create mode 100644 examples/django_mongoengine/README.md create mode 100644 examples/django_mongoengine/__init__.py create mode 100644 examples/django_mongoengine/bike/__init__.py create mode 100644 examples/django_mongoengine/bike/apps.py create mode 100644 examples/django_mongoengine/bike/fixtures.py create mode 100644 examples/django_mongoengine/bike/migrations/__init__.py create mode 100644 examples/django_mongoengine/bike/models.py create mode 100644 examples/django_mongoengine/bike/schema.py create mode 100644 examples/django_mongoengine/bike/tests.py create mode 100644 examples/django_mongoengine/bike/types.py create mode 100644 examples/django_mongoengine/bike/urls.py create mode 100644 examples/django_mongoengine/bike_catalog/__init__.py create mode 100644 examples/django_mongoengine/bike_catalog/settings.py create mode 100644 examples/django_mongoengine/bike_catalog/settings_test.py create mode 100644 examples/django_mongoengine/bike_catalog/urls.py create mode 100644 examples/django_mongoengine/bike_catalog/wsgi.py create mode 100644 examples/django_mongoengine/manage.py create mode 100644 examples/django_mongoengine/pytest.ini create mode 100644 examples/django_mongoengine/requirements.txt diff --git a/examples/django_mongoengine/.gitignore b/examples/django_mongoengine/.gitignore new file mode 100644 index 00000000..ba520ccd --- /dev/null +++ b/examples/django_mongoengine/.gitignore @@ -0,0 +1 @@ +db.sqlite3 \ No newline at end of file diff --git a/examples/django_mongoengine/README.md b/examples/django_mongoengine/README.md new file mode 100644 index 00000000..32964231 --- /dev/null +++ b/examples/django_mongoengine/README.md @@ -0,0 +1,56 @@ + +Example Django+MongoEngine Project +================================ + +This example project demos integration between Graphene, Django and MongoEngine. + +Getting started +--------------- + +First you'll need to get the source of the project. Do this by cloning the +whole Graphene repository: + +```bash +# Get the example project code +git clone git@github.com:abawchen/graphene-mongo.git +cd graphene-mongo/examples/django_mongoengine +``` + +Create a virtual environment. + +```bash +# Create a virtualenv in which we can install the dependencies +virtualenv env +source env/bin/activate +``` + +Now we can install our dependencies: + +```bash +pip install -r requirements.txt +``` + +Run the following command: + +```python +python manage.py migrate +``` + +Setup a mongodb connection and create a database. +See the mongoengine connection details in the *settings.py* file + +Start the server: + +```python +python manage.py runserver +``` + +Now head on over to +[http://127.0.0.1:8000/graphql](http://127.0.0.1:8000/graphql) +and run some queries! + +For tests run: + +```python +pytest -v +``` diff --git a/examples/django_mongoengine/__init__.py b/examples/django_mongoengine/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/django_mongoengine/bike/__init__.py b/examples/django_mongoengine/bike/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/django_mongoengine/bike/apps.py b/examples/django_mongoengine/bike/apps.py new file mode 100644 index 00000000..c6b5601a --- /dev/null +++ b/examples/django_mongoengine/bike/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class BikeConfig(AppConfig): + name = 'bike' diff --git a/examples/django_mongoengine/bike/fixtures.py b/examples/django_mongoengine/bike/fixtures.py new file mode 100644 index 00000000..ad66318f --- /dev/null +++ b/examples/django_mongoengine/bike/fixtures.py @@ -0,0 +1,28 @@ +import pytest +from .models import Bike + + +@pytest.fixture(scope='module') +def fixtures_data(): + Bike.drop_collection() + bike_one = Bike( + name='Level R', + brand='Mondraker', + year='2020', + size=['S', 'M', 'L', 'XL'], + wheel_size=27.5, + type='MTB' + ) + bike_one.save() + + bike_two = Bike( + name='CAADX ULTEGRA', + brand='Cannondale', + year='2019', + size=['46', '51', '54', '58'], + wheel_size=28, + type='Gravel' + ) + bike_two.save() + + return True diff --git a/examples/django_mongoengine/bike/migrations/__init__.py b/examples/django_mongoengine/bike/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/django_mongoengine/bike/models.py b/examples/django_mongoengine/bike/models.py new file mode 100644 index 00000000..85ba00f0 --- /dev/null +++ b/examples/django_mongoengine/bike/models.py @@ -0,0 +1,12 @@ +from mongoengine import Document +from mongoengine.fields import FloatField, StringField, ListField + + +class Bike(Document): + meta = {'collection': 'bike'} + name = StringField() + brand = StringField() + year = StringField() + size = ListField(StringField()) + wheel_size = FloatField() + type = StringField() diff --git a/examples/django_mongoengine/bike/schema.py b/examples/django_mongoengine/bike/schema.py new file mode 100644 index 00000000..535dfd6a --- /dev/null +++ b/examples/django_mongoengine/bike/schema.py @@ -0,0 +1,12 @@ +import graphene +from graphene.relay import Node +from graphene_mongo.fields import MongoengineConnectionField +from .types import BikeType + + +class Query(graphene.ObjectType): + node = Node.Field() + bikes = MongoengineConnectionField(BikeType) + + +schema = graphene.Schema(query=Query, types=[BikeType, ]) diff --git a/examples/django_mongoengine/bike/tests.py b/examples/django_mongoengine/bike/tests.py new file mode 100644 index 00000000..60ccff8c --- /dev/null +++ b/examples/django_mongoengine/bike/tests.py @@ -0,0 +1,87 @@ +from graphene.test import Client +from .schema import schema +from .fixtures import fixtures_data + + +def test_bikes_last_item_query(fixtures_data): + query = ''' + { + bikes(last: 1){ + edges { + node { + name + brand + year + size + wheelSize + type + } + } + } + }''' + + expected = { + "data": { + "bikes": + { + "edges": [ + { + "node": { + "name": "CAADX ULTEGRA", + "brand": "Cannondale", + "year": '2019', + "size": ['46', '51', '54', '58'], + "wheelSize": 28, + "type": "Gravel" + } + }, + ] + } + } + } + + client = Client(schema) + result = client.execute(query) + assert result == expected + + +def test_bikes_filter_by_type_item_query(fixtures_data): + query = ''' + { + bikes(first: 2, type: "MTB"){ + edges { + node { + name + brand + year + size + wheelSize + type + } + } + } + }''' + + expected = { + "data": { + "bikes": + { + "edges": [ + { + "node": { + "name": "Level R", + "brand": "Mondraker", + "year": '2020', + "size": ['S', 'M', 'L', 'XL'], + "wheelSize": 27.5, + "type": "MTB" + } + }, + ] + } + } + } + + client = Client(schema) + result = client.execute(query) + assert result == expected diff --git a/examples/django_mongoengine/bike/types.py b/examples/django_mongoengine/bike/types.py new file mode 100644 index 00000000..bc2dd4bc --- /dev/null +++ b/examples/django_mongoengine/bike/types.py @@ -0,0 +1,9 @@ +from graphene import relay +from graphene_mongo import MongoengineObjectType +from .models import Bike + + +class BikeType(MongoengineObjectType): + class Meta: + model = Bike + interfaces = (relay.Node,) diff --git a/examples/django_mongoengine/bike/urls.py b/examples/django_mongoengine/bike/urls.py new file mode 100644 index 00000000..72678001 --- /dev/null +++ b/examples/django_mongoengine/bike/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from django.views.decorators.csrf import csrf_exempt +from graphene_django.views import GraphQLView + +urlpatterns = [ + path('graphql', csrf_exempt(GraphQLView.as_view(graphiql=True))), +] diff --git a/examples/django_mongoengine/bike_catalog/__init__.py b/examples/django_mongoengine/bike_catalog/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/django_mongoengine/bike_catalog/settings.py b/examples/django_mongoengine/bike_catalog/settings.py new file mode 100644 index 00000000..19a4a85f --- /dev/null +++ b/examples/django_mongoengine/bike_catalog/settings.py @@ -0,0 +1,136 @@ +""" +Django settings for bike_catalog project. + +Generated by 'django-admin startproject' using Django 2.2.3. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.2/ref/settings/ +""" + +import os +import mongoengine + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '_@^#*p_+mm-p8+#k&8i0=dnvt03$ycqmwqs4os0-+@+u6k-f_m' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + 'graphene_django', + 'graphene_mongo', + + 'bike', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'bike_catalog.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'bike_catalog.wsgi.application' + +# Database +# https://docs.djangoproject.com/en/2.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + +# Password validation +# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +# Internationalization +# https://docs.djangoproject.com/en/2.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.2/howto/static-files/ + +STATIC_URL = '/static/' + +# MONGO DB connection\ +_MONGODB_USER = '' +_MONGODB_PASSWD = '' +_MONGODB_HOST = 'localhost' +_MONGODB_NAME = 'bike-catalog' +_MONGODB_PORT = 27017 +_MONGODB_DATABASE_HOST = \ + 'mongodb://%s:%s@%s/%s' \ + % (_MONGODB_USER, _MONGODB_PASSWD, _MONGODB_HOST, _MONGODB_NAME) + +mongoengine.connect(_MONGODB_NAME, host=_MONGODB_HOST, port=_MONGODB_PORT) + +GRAPHENE = { + 'SCHEMA': 'bike.schema.schema' # Where your Graphene schema lives +} diff --git a/examples/django_mongoengine/bike_catalog/settings_test.py b/examples/django_mongoengine/bike_catalog/settings_test.py new file mode 100644 index 00000000..9436b097 --- /dev/null +++ b/examples/django_mongoengine/bike_catalog/settings_test.py @@ -0,0 +1,3 @@ +from .settings import * # flake8: noqa + +mongoengine.connect('graphene-mongo-test', host='mongomock://localhost', alias='default') diff --git a/examples/django_mongoengine/bike_catalog/urls.py b/examples/django_mongoengine/bike_catalog/urls.py new file mode 100644 index 00000000..0c7ec5af --- /dev/null +++ b/examples/django_mongoengine/bike_catalog/urls.py @@ -0,0 +1,22 @@ +"""bike_catalog URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include('bike.urls')), +] diff --git a/examples/django_mongoengine/bike_catalog/wsgi.py b/examples/django_mongoengine/bike_catalog/wsgi.py new file mode 100644 index 00000000..8f4423a3 --- /dev/null +++ b/examples/django_mongoengine/bike_catalog/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for bike_catalog project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bike_catalog.settings') + +application = get_wsgi_application() diff --git a/examples/django_mongoengine/manage.py b/examples/django_mongoengine/manage.py new file mode 100644 index 00000000..c354299c --- /dev/null +++ b/examples/django_mongoengine/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bike_catalog.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/examples/django_mongoengine/pytest.ini b/examples/django_mongoengine/pytest.ini new file mode 100644 index 00000000..f4ef0eb6 --- /dev/null +++ b/examples/django_mongoengine/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +DJANGO_SETTINGS_MODULE = bike_catalog.settings_test +python_files = tests.py test_*.py *_tests.py \ No newline at end of file diff --git a/examples/django_mongoengine/requirements.txt b/examples/django_mongoengine/requirements.txt new file mode 100644 index 00000000..d2cfbf0f --- /dev/null +++ b/examples/django_mongoengine/requirements.txt @@ -0,0 +1,7 @@ +Django==2.2.3 +pytest==4.6.3 +pytest-django==3.5.1 +mongoengine==0.17.0 +mongomock==3.16.0 +graphene-django==2.4.0 +graphene-mongo From d8916227624f91bbdea853dddae5b3a8cd726520 Mon Sep 17 00:00:00 2001 From: Eduard Erja Date: Fri, 19 Jul 2019 19:40:08 +0300 Subject: [PATCH 2/4] Added new model class without relay --- examples/django_mongoengine/bike/fixtures.py | 27 +++++++++++++-- examples/django_mongoengine/bike/models.py | 9 ++++- examples/django_mongoengine/bike/schema.py | 7 +++- examples/django_mongoengine/bike/tests.py | 35 +++++++++++++++++++- examples/django_mongoengine/bike/types.py | 7 +++- 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/examples/django_mongoengine/bike/fixtures.py b/examples/django_mongoengine/bike/fixtures.py index ad66318f..41100262 100644 --- a/examples/django_mongoengine/bike/fixtures.py +++ b/examples/django_mongoengine/bike/fixtures.py @@ -1,9 +1,8 @@ import pytest -from .models import Bike +from .models import Bike, Shop -@pytest.fixture(scope='module') -def fixtures_data(): +def fixture_bike_data(): Bike.drop_collection() bike_one = Bike( name='Level R', @@ -25,4 +24,26 @@ def fixtures_data(): ) bike_two.save() + +def fixture_shop_data(): + Shop.drop_collection() + shop_one = Shop( + name="Big Wheel Bicycles", + address="2438 Hart Ridge Road", + website="https://www.bigwheelbike.test" + ) + shop_one.save() + shop_two = Shop( + name="Bike Tech", + address="2175 Pearl Street", + website="https://www.biketech.test" + ) + shop_two.save() + + +@pytest.fixture(scope='module') +def fixtures_data(): + fixture_bike_data() + fixture_shop_data() + return True diff --git a/examples/django_mongoengine/bike/models.py b/examples/django_mongoengine/bike/models.py index 85ba00f0..9c2c0621 100644 --- a/examples/django_mongoengine/bike/models.py +++ b/examples/django_mongoengine/bike/models.py @@ -1,5 +1,12 @@ from mongoengine import Document -from mongoengine.fields import FloatField, StringField, ListField +from mongoengine.fields import FloatField, StringField, ListField, URLField + + +class Shop(Document): + meta = {'collection': 'shop'} + name = StringField() + address = StringField() + website = URLField() class Bike(Document): diff --git a/examples/django_mongoengine/bike/schema.py b/examples/django_mongoengine/bike/schema.py index 535dfd6a..7a705981 100644 --- a/examples/django_mongoengine/bike/schema.py +++ b/examples/django_mongoengine/bike/schema.py @@ -1,12 +1,17 @@ import graphene from graphene.relay import Node from graphene_mongo.fields import MongoengineConnectionField -from .types import BikeType +from .models import Shop +from .types import BikeType, ShopType class Query(graphene.ObjectType): node = Node.Field() bikes = MongoengineConnectionField(BikeType) + shop_list = graphene.List(ShopType) + + def resolve_shop_list(self, info): + return Shop.objects.all() schema = graphene.Schema(query=Query, types=[BikeType, ]) diff --git a/examples/django_mongoengine/bike/tests.py b/examples/django_mongoengine/bike/tests.py index 60ccff8c..2849810e 100644 --- a/examples/django_mongoengine/bike/tests.py +++ b/examples/django_mongoengine/bike/tests.py @@ -76,7 +76,7 @@ def test_bikes_filter_by_type_item_query(fixtures_data): "wheelSize": 27.5, "type": "MTB" } - }, + } ] } } @@ -85,3 +85,36 @@ def test_bikes_filter_by_type_item_query(fixtures_data): client = Client(schema) result = client.execute(query) assert result == expected + + +def test_shop_data_query(fixtures_data): + query = '''{ + shopList{ + name + address + website + } + }''' + + expected = { + "data": { + "shopList": [ + { + + "name": "Big Wheel Bicycles", + "address": "2438 Hart Ridge Road", + "website": "https://www.bigwheelbike.test", + + }, + { + "name": "Bike Tech", + "address": "2175 Pearl Street", + "website": "https://www.biketech.test", + } + ] + } + } + + client = Client(schema) + result = client.execute(query) + assert result == expected diff --git a/examples/django_mongoengine/bike/types.py b/examples/django_mongoengine/bike/types.py index bc2dd4bc..625e3e34 100644 --- a/examples/django_mongoengine/bike/types.py +++ b/examples/django_mongoengine/bike/types.py @@ -1,9 +1,14 @@ from graphene import relay from graphene_mongo import MongoengineObjectType -from .models import Bike +from .models import Bike, Shop class BikeType(MongoengineObjectType): class Meta: model = Bike interfaces = (relay.Node,) + + +class ShopType(MongoengineObjectType): + class Meta: + model = Shop From adf03f43a3782cdd3a35a1536ab029d75609ac93 Mon Sep 17 00:00:00 2001 From: Eduard Erja Date: Fri, 19 Jul 2019 19:42:16 +0300 Subject: [PATCH 3/4] Added missing type to schema --- examples/django_mongoengine/bike/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/django_mongoengine/bike/schema.py b/examples/django_mongoengine/bike/schema.py index 7a705981..40f953b8 100644 --- a/examples/django_mongoengine/bike/schema.py +++ b/examples/django_mongoengine/bike/schema.py @@ -14,4 +14,4 @@ def resolve_shop_list(self, info): return Shop.objects.all() -schema = graphene.Schema(query=Query, types=[BikeType, ]) +schema = graphene.Schema(query=Query, types=[BikeType, ShopType]) From a2b27afb8535c36113ec626fff50b587a5f32913 Mon Sep 17 00:00:00 2001 From: Eduard Erja Date: Fri, 19 Jul 2019 20:41:20 +0300 Subject: [PATCH 4/4] Added crud mutations and unittesting --- examples/django_mongoengine/bike/fixtures.py | 11 ++ examples/django_mongoengine/bike/models.py | 7 +- examples/django_mongoengine/bike/mutations.py | 80 +++++++++ examples/django_mongoengine/bike/schema.py | 9 +- examples/django_mongoengine/bike/tests.py | 154 ++++++++++++++++-- examples/django_mongoengine/bike/urls.py | 2 +- 6 files changed, 244 insertions(+), 19 deletions(-) create mode 100644 examples/django_mongoengine/bike/mutations.py diff --git a/examples/django_mongoengine/bike/fixtures.py b/examples/django_mongoengine/bike/fixtures.py index 41100262..15587808 100644 --- a/examples/django_mongoengine/bike/fixtures.py +++ b/examples/django_mongoengine/bike/fixtures.py @@ -24,6 +24,17 @@ def fixture_bike_data(): ) bike_two.save() + bike_three = Bike( + id="507f1f77bcf86cd799439011", + name='Moterra Neo', + brand='Cannondale', + year='2019', + size=["M", "L", "XL"], + wheel_size=29, + type='EBike' + ) + bike_three.save() + def fixture_shop_data(): Shop.drop_collection() diff --git a/examples/django_mongoengine/bike/models.py b/examples/django_mongoengine/bike/models.py index 9c2c0621..ed2c309a 100644 --- a/examples/django_mongoengine/bike/models.py +++ b/examples/django_mongoengine/bike/models.py @@ -1,9 +1,13 @@ from mongoengine import Document -from mongoengine.fields import FloatField, StringField, ListField, URLField +from mongoengine.fields import ( + FloatField, StringField, + ListField, URLField, ObjectIdField +) class Shop(Document): meta = {'collection': 'shop'} + ID = ObjectIdField() name = StringField() address = StringField() website = URLField() @@ -11,6 +15,7 @@ class Shop(Document): class Bike(Document): meta = {'collection': 'bike'} + ID = ObjectIdField() name = StringField() brand = StringField() year = StringField() diff --git a/examples/django_mongoengine/bike/mutations.py b/examples/django_mongoengine/bike/mutations.py new file mode 100644 index 00000000..42f6eee7 --- /dev/null +++ b/examples/django_mongoengine/bike/mutations.py @@ -0,0 +1,80 @@ +import graphene +from django.core.exceptions import ObjectDoesNotExist +from .models import Bike +from .types import BikeType + + +class BikeInput(graphene.InputObjectType): + id = graphene.ID() + name = graphene.String() + brand = graphene.String() + year = graphene.String() + size = graphene.List(graphene.String) + wheel_size = graphene.Float() + type = graphene.String() + + +class CreateBikeMutation(graphene.Mutation): + bike = graphene.Field(BikeType) + + class Arguments: + bike_data = BikeInput(required=True) + + def mutate(self, info, bike_data=None): + bike = Bike( + name=bike_data.name, + brand=bike_data.brand, + year=bike_data.year, + size=bike_data.size, + wheel_size=bike_data.wheel_size, + type=bike_data.type + ) + bike.save() + + return CreateBikeMutation(bike=bike) + + +class UpdateBikeMutation(graphene.Mutation): + bike = graphene.Field(BikeType) + + class Arguments: + bike_data = BikeInput(required=True) + + @staticmethod + def get_object(id): + return Bike.objects.get(pk=id) + + def mutate(self, info, bike_data=None): + bike = UpdateBikeMutation.get_object(bike_data.id) + if bike_data.name: + bike.name = bike_data.name + if bike_data.brand: + bike.brand = bike_data.brand + if bike_data.year: + bike.year = bike_data.year + if bike_data.size: + bike.size = bike_data.size + if bike_data.wheel_size: + bike.wheel_size = bike_data.wheel_size + if bike_data.type: + bike.type = bike_data.type + + bike.save() + + return UpdateBikeMutation(bike=bike) + + +class DeleteBikeMutation(graphene.Mutation): + class Arguments: + id = graphene.ID(required=True) + + success = graphene.Boolean() + + def mutate(self, info, id): + try: + Bike.objects.get(pk=id).delete() + success = True + except ObjectDoesNotExist: + success = False + + return DeleteBikeMutation(success=success) diff --git a/examples/django_mongoengine/bike/schema.py b/examples/django_mongoengine/bike/schema.py index 40f953b8..a5e7f170 100644 --- a/examples/django_mongoengine/bike/schema.py +++ b/examples/django_mongoengine/bike/schema.py @@ -3,6 +3,13 @@ from graphene_mongo.fields import MongoengineConnectionField from .models import Shop from .types import BikeType, ShopType +from .mutations import CreateBikeMutation, UpdateBikeMutation, DeleteBikeMutation + + +class Mutations(graphene.ObjectType): + create_bike = CreateBikeMutation.Field() + update_bike = UpdateBikeMutation.Field() + delete_bike = DeleteBikeMutation.Field() class Query(graphene.ObjectType): @@ -14,4 +21,4 @@ def resolve_shop_list(self, info): return Shop.objects.all() -schema = graphene.Schema(query=Query, types=[BikeType, ShopType]) +schema = graphene.Schema(query=Query, mutation=Mutations, types=[BikeType, ShopType]) diff --git a/examples/django_mongoengine/bike/tests.py b/examples/django_mongoengine/bike/tests.py index 2849810e..877329a1 100644 --- a/examples/django_mongoengine/bike/tests.py +++ b/examples/django_mongoengine/bike/tests.py @@ -1,12 +1,15 @@ +import pytest +from django.urls import reverse +from django.test import RequestFactory from graphene.test import Client from .schema import schema from .fixtures import fixtures_data -def test_bikes_last_item_query(fixtures_data): +def test_bikes_first_item_query(fixtures_data): query = ''' { - bikes(last: 1){ + bikes(first: 1){ edges { node { name @@ -27,14 +30,14 @@ def test_bikes_last_item_query(fixtures_data): "edges": [ { "node": { - "name": "CAADX ULTEGRA", - "brand": "Cannondale", - "year": '2019', - "size": ['46', '51', '54', '58'], - "wheelSize": 28, - "type": "Gravel" + "name": "Level R", + "brand": "Mondraker", + "year": '2020', + "size": ['S', 'M', 'L', 'XL'], + "wheelSize": 27.5, + "type": "MTB" } - }, + } ] } } @@ -48,7 +51,7 @@ def test_bikes_last_item_query(fixtures_data): def test_bikes_filter_by_type_item_query(fixtures_data): query = ''' { - bikes(first: 2, type: "MTB"){ + bikes(first: 2, type: "Gravel"){ edges { node { name @@ -69,12 +72,12 @@ def test_bikes_filter_by_type_item_query(fixtures_data): "edges": [ { "node": { - "name": "Level R", - "brand": "Mondraker", - "year": '2020', - "size": ['S', 'M', 'L', 'XL'], - "wheelSize": 27.5, - "type": "MTB" + "name": "CAADX ULTEGRA", + "brand": "Cannondale", + "year": '2019', + "size": ['46', '51', '54', '58'], + "wheelSize": 28, + "type": "Gravel" } } ] @@ -118,3 +121,122 @@ def test_shop_data_query(fixtures_data): client = Client(schema) result = client.execute(query) assert result == expected + + +@pytest.mark.django_db +def test_create_bike_mutation(): + query = ''' + mutation { + createBike(bikeData:{ + name:"Bullhorn", + brand:"Pegas", + year: "2019", + size: ["56", "58" ], + wheelSize: 28, + type: "Fixie" + }) { + bike { + name + brand + year + size + wheelSize + type + } + } + } + ''' + + expected = { + "data": { + "createBike": { + "bike": + { + "name": "Bullhorn", + "brand": "Pegas", + "year": "2019", + "size": ["56", "58"], + "wheelSize": 28, + "type": "Fixie", + } + } + } + } + + factory = RequestFactory() + request = factory.post(reverse('graphql-query')) + client = Client(schema) + result = client.execute(query, context=request) + assert result == expected + + +@pytest.mark.django_db +def test_update_bike_mutation(): + query = ''' + mutation { + updateBike(bikeData:{ + id: "507f1f77bcf86cd799439011", + name:"Moterra Neo Updated", + year: "2020", + wheelSize: 27.5, + type: "EBike Updated" + }) { + bike { + name + brand + year + size + wheelSize + type + } + } + } + ''' + + expected = { + "data": { + "updateBike": { + "bike": + { + "name": "Moterra Neo Updated", + "brand": "Cannondale", + "year": "2020", + "size": ["M", "L", "XL"], + "wheelSize": 27.5, + "type": "EBike Updated" + } + } + } + } + + factory = RequestFactory() + request = factory.post(reverse('graphql-query')) + client = Client(schema) + result = client.execute(query, context=request) + print(result) + assert result == expected + + +@pytest.mark.django_db +def test_delete_bike_mutation(): + query = ''' + mutation { + deleteBike(id: "507f1f77bcf86cd799439011") { + success + } + } + ''' + + expected = { + "data": { + "deleteBike": { + "success": True + } + } + } + + factory = RequestFactory() + request = factory.post(reverse('graphql-query')) + client = Client(schema) + result = client.execute(query, context=request) + assert result == expected diff --git a/examples/django_mongoengine/bike/urls.py b/examples/django_mongoengine/bike/urls.py index 72678001..6cd6a013 100644 --- a/examples/django_mongoengine/bike/urls.py +++ b/examples/django_mongoengine/bike/urls.py @@ -3,5 +3,5 @@ from graphene_django.views import GraphQLView urlpatterns = [ - path('graphql', csrf_exempt(GraphQLView.as_view(graphiql=True))), + path('graphql', csrf_exempt(GraphQLView.as_view(graphiql=True)), name='graphql-query'), ]