From f32a2439ae5532df52e7e8064137628447df1ed8 Mon Sep 17 00:00:00 2001 From: Edward Gonzalez Date: Thu, 6 Sep 2018 14:10:59 -0400 Subject: [PATCH 1/3] Initial Commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 09f306a..c3dfca1 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,5 @@ * [Experimentation and Django back end](guides/day1.md) * [Mutations](guides/day2.md) +#Initial Commit + From d1a91b541e0b69050ba26a070cdda733d75f6e3d Mon Sep 17 00:00:00 2001 From: eddygonzalez9708 Date: Thu, 6 Sep 2018 17:14:15 -0400 Subject: [PATCH 2/3] Completed day 1 tasks. --- .gitignore | 107 +++++++++++ Pipfile | 20 +++ Pipfile.lock | 190 ++++++++++++++++++++ Procfile | 1 + django1/__init__.py | 0 django1/settings.py | 181 +++++++++++++++++++ django1/urls.py | 33 ++++ django1/wsgi.py | 16 ++ manage.py | 15 ++ notes/__init__.py | 0 notes/admin.py | 8 + notes/api.py | 32 ++++ notes/apps.py | 5 + notes/migrations/0001_initial.py | 23 +++ notes/migrations/0002_auto_20180828_1611.py | 25 +++ notes/migrations/0003_personalnote.py | 24 +++ notes/migrations/__init__.py | 0 notes/models.py | 20 +++ notes/schema.py | 27 +++ notes/tests.py | 3 + notes/views.py | 3 + 21 files changed, 733 insertions(+) create mode 100644 .gitignore create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 Procfile create mode 100644 django1/__init__.py create mode 100644 django1/settings.py create mode 100644 django1/urls.py create mode 100644 django1/wsgi.py create mode 100644 manage.py create mode 100644 notes/__init__.py create mode 100644 notes/admin.py create mode 100644 notes/api.py create mode 100644 notes/apps.py create mode 100644 notes/migrations/0001_initial.py create mode 100644 notes/migrations/0002_auto_20180828_1611.py create mode 100644 notes/migrations/0003_personalnote.py create mode 100644 notes/migrations/__init__.py create mode 100644 notes/models.py create mode 100644 notes/schema.py create mode 100644 notes/tests.py create mode 100644 notes/views.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bcc5cd5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,107 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +#vs code folder +.vscode/ diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..a3e5008 --- /dev/null +++ b/Pipfile @@ -0,0 +1,20 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +django = "*" +python-decouple = "*" +djangorestframework = "*" +django-cors-headers = "*" +gunicorn = "*" +"psycopg2-binary" = "*" +dj-database-url = "*" +whitenoise = "*" +graphene-django = "*" + +[dev-packages] + +[requires] +python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..af8bebb --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,190 @@ +{ + "_meta": { + "hash": { + "sha256": "b074e0951599b2576f65723fa375bcdaeb802190d79fddc9adfdce7264244bfa" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.6" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "aniso8601": { + "hashes": [ + "sha256:7849749cf00ae0680ad2bdfe4419c7a662bef19c03691a19e008c8b9a5267802", + "sha256:94f90871fcd314a458a3d4eca1c84448efbd200e86f55fe4c733c7a40149ef50" + ], + "version": "==3.0.2" + }, + "dj-database-url": { + "hashes": [ + "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163", + "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9" + ], + "index": "pypi", + "version": "==0.5.0" + }, + "django": { + "hashes": [ + "sha256:04f2e423f2e60943c02bd2959174b844f7d1bcd19eabb7f8e4282999958021fd", + "sha256:e1cc1cd6b658aa4e052f5f2b148bfda08091d7c3558529708342e37e4e33f72c" + ], + "index": "pypi", + "version": "==2.1.1" + }, + "django-cors-headers": { + "hashes": [ + "sha256:5545009c9b233ea7e70da7dbab7cb1c12afa01279895086f98ec243d7eab46fa", + "sha256:c4c2ee97139d18541a1be7d96fe337d1694623816d83f53cb7c00da9b94acae1" + ], + "index": "pypi", + "version": "==2.4.0" + }, + "djangorestframework": { + "hashes": [ + "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9", + "sha256:c375e4f95a3a64fccac412e36fb42ba36881e52313ec021ef410b40f67cddca4" + ], + "index": "pypi", + "version": "==3.8.2" + }, + "graphene": { + "hashes": [ + "sha256:b8ec446d17fa68721636eaad3d6adc1a378cb6323e219814c8f98c9928fc9642", + "sha256:faa26573b598b22ffd274e2fd7a4c52efa405dcca96e01a62239482246248aa3" + ], + "version": "==2.1.3" + }, + "graphene-django": { + "hashes": [ + "sha256:3afd81d47c8b702650e05cc1179fac1cfceae991d241bb164d51f28bed9ec95c", + "sha256:760a18068feb5457e2ec00d2447c09b2fbac2a6b8c32cc8be2abce3752107ad3" + ], + "index": "pypi", + "version": "==2.2.0" + }, + "graphql-core": { + "hashes": [ + "sha256:889e869be5574d02af77baf1f30b5db9ca2959f1c9f5be7b2863ead5a3ec6181", + "sha256:9462e22e32c7f03b667373ec0a84d95fba10e8ce2ead08f29fbddc63b671b0c1" + ], + "version": "==2.1" + }, + "graphql-relay": { + "hashes": [ + "sha256:2716b7245d97091af21abf096fabafac576905096d21ba7118fba722596f65db" + ], + "version": "==0.4.5" + }, + "gunicorn": { + "hashes": [ + "sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471", + "sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3" + ], + "index": "pypi", + "version": "==19.9.0" + }, + "promise": { + "hashes": [ + "sha256:0bca4ed933e3d50e3d18fb54fc1432fa84b0564838cd093e824abcd718ab9304", + "sha256:95506bac89df7a495e0b8c813fd782dd1ae590decb52f95248e316c6659ca49b" + ], + "version": "==2.1" + }, + "psycopg2-binary": { + "hashes": [ + "sha256:04afb59bbbd2eab3148e6816beddc74348078b8c02a1113ea7f7822f5be4afe3", + "sha256:098b18f4d8857a8f9b206d1dc54db56c2255d5d26458917e7bcad61ebfe4338f", + "sha256:0bf855d4a7083e20ead961fda4923887094eaeace0ab2d76eb4aa300f4bbf5bd", + "sha256:197dda3ffd02057820be83fe4d84529ea70bf39a9a4daee1d20ffc74eb3d042e", + "sha256:278ef63afb4b3d842b4609f2c05ffbfb76795cf6a184deeb8707cd5ed3c981a5", + "sha256:3cbf8c4fc8f22f0817220891cf405831559f4d4c12c4f73913730a2ea6c47a47", + "sha256:4305aed922c4d9d6163ab3a41d80b5a1cfab54917467da8168552c42cad84d32", + "sha256:47ee296f704fb8b2a616dec691cdcfd5fa0f11943955e88faa98cbd1dc3b3e3d", + "sha256:4a0e38cb30457e70580903367161173d4a7d1381eb2f2cfe4e69b7806623f484", + "sha256:4d6c294c6638a71cafb82a37f182f24321f1163b08b5d5ca076e11fe838a3086", + "sha256:4f3233c366500730f839f92833194fd8f9a5c4529c8cd8040aa162c3740de8e5", + "sha256:5221f5a3f4ca2ddf0d58e8b8a32ca50948be9a43351fda797eb4e72d7a7aa34d", + "sha256:5c6ca0b507540a11eaf9e77dee4f07c131c2ec80ca0cffa146671bf690bc1c02", + "sha256:789bd89d71d704db2b3d5e67d6d518b158985d791d3b2dec5ab85457cfc9677b", + "sha256:7b94d29239efeaa6a967f3b5971bd0518d2a24edd1511edbf4a2c8b815220d07", + "sha256:89bc65ef3301c74cf32db25334421ea6adbe8f65601ea45dcaaf095abed910bb", + "sha256:89d6d3a549f405c20c9ae4dc94d7ed2de2fa77427a470674490a622070732e62", + "sha256:97521704ac7127d7d8ba22877da3c7bf4a40366587d238ec679ff38e33177498", + "sha256:a395b62d5f44ff6f633231abe568e2203b8fabf9797cd6386aa92497df912d9a", + "sha256:a6d32c37f714c3f34158f3fa659f3a8f2658d5f53c4297d45579b9677cc4d852", + "sha256:a89ee5c26f72f2d0d74b991ce49e42ddeb4ac0dc2d8c06a0f2770a1ab48f4fe0", + "sha256:b4c8b0ef3608e59317bfc501df84a61e48b5445d45f24d0391a24802de5f2d84", + "sha256:b5fcf07140219a1f71e18486b8dc28e2e1b76a441c19374805c617aa6d9a9d55", + "sha256:b86f527f00956ecebad6ab3bb30e3a75fedf1160a8716978dd8ce7adddedd86f", + "sha256:be4c4aa22ba22f70de36c98b06480e2f1697972d49eb20d525f400d204a6d272", + "sha256:c2ac7aa1a144d4e0e613ac7286dae85671e99fe7a1353954d4905629c36b811c", + "sha256:de26ef4787b5e778e8223913a3e50368b44e7480f83c76df1f51d23bd21cea16", + "sha256:e70ebcfc5372dc7b699c0110454fc4263967f30c55454397e5769eb72c0eb0ce", + "sha256:eadbd32b6bc48b67b0457fccc94c86f7ccc8178ab839f684eb285bb592dc143e", + "sha256:ecbc6dfff6db06b8b72ae8a2f25ff20fbdcb83cb543811a08f7cb555042aa729" + ], + "index": "pypi", + "version": "==2.7.5" + }, + "python-decouple": { + "hashes": [ + "sha256:1317df14b43efee4337a4aa02914bf004f010cd56d6c4bd894e6474ec8c4fe2d" + ], + "index": "pypi", + "version": "==3.1" + }, + "pytz": { + "hashes": [ + "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", + "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" + ], + "version": "==2018.5" + }, + "rx": { + "hashes": [ + "sha256:13a1d8d9e252625c173dc795471e614eadfe1cf40ffc684e08b8fff0d9748c23", + "sha256:7357592bc7e881a95e0c2013b73326f704953301ab551fbc8133a6fadab84105" + ], + "version": "==1.6.1" + }, + "singledispatch": { + "hashes": [ + "sha256:5b06af87df13818d14f08a028e42f566640aef80805c3b50c5056b086e3c2b9c", + "sha256:833b46966687b3de7f438c761ac475213e53b306740f1abfaa86e1d1aae56aa8" + ], + "version": "==3.4.0.3" + }, + "six": { + "hashes": [ + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + ], + "version": "==1.11.0" + }, + "typing": { + "hashes": [ + "sha256:4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d", + "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", + "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a" + ], + "version": "==3.6.6" + }, + "whitenoise": { + "hashes": [ + "sha256:1e206c5adfb849942ddd057e599ac472ec1a85d56ae78a5ba24f243ea46a89c5", + "sha256:a6f86b011675b9730f69fd69d4f54c5697d6c7a90ab06f83f784d243d9fccc02" + ], + "index": "pypi", + "version": "==4.0" + } + }, + "develop": {} +} diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..8489776 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn django1.wsgi \ No newline at end of file diff --git a/django1/__init__.py b/django1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django1/settings.py b/django1/settings.py new file mode 100644 index 0000000..c608581 --- /dev/null +++ b/django1/settings.py @@ -0,0 +1,181 @@ +""" +Django settings for django1 project. + +Generated by 'django-admin startproject' using Django 2.1. + +For more information on this file, see +https://docs.djangoproject.com/en/2.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.1/ref/settings/ +""" + +import os +from decouple import config +import dj_database_url + +# 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.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = config('SECRET_KEY') + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = config('DEBUG', cast=bool) + +ALLOWED_HOSTS = config('ALLOWED_HOSTS').split(',') + +# Application definition + +INSTALLED_APPS = [ + 'notes', + 'rest_framework', + 'rest_framework.authtoken', + 'graphene_django', + 'corsheaders', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.security.SecurityMiddleware', + 'whitenoise.middleware.WhiteNoiseMiddleware', + '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 = 'django1.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 = 'django1.wsgi.application' + +# Database +# https://docs.djangoproject.com/en/2.1/ref/settings/#databases + +DATABASES = { + 'default': dj_database_url.config('DATABASE_URL', default='sqlite:///db.sqlite3') +} + +# Password validation +# https://docs.djangoproject.com/en/2.1/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.1/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.1/howto/static-files/ + +STATIC_URL = '/static/' + +STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' + +STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') + +# Biolerplate to set up permissions for the rest framework + +from rest_framework.authentication import TokenAuthentication, SessionAuthentication, BasicAuthentication + +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': [ + # This will allow read/write permissions for logged in users and read only for anonymous users. + 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', + ], + + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.BasicAuthentication', + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.TokenAuthentication', + ), +} + +GRAPHENE = { + 'SCHEMA': 'notes.schema.schema' # dir.file.varname +} + +CORS_ORIGIN_ALLOW_ALL = True + +CORS_ORIGIN_WHITELIST = ( + 'google.com', + 'hostname.example.com', + 'localhost:8000', + '127.0.0.1:9000' +) + +CORS_ORIGIN_REGEX_WHITELIST = (r'^(https?://)?(\w+\.)?google\.com$', ) + +CORS_ALLOW_METHODS = ( + 'DELETE', + 'GET', + 'OPTIONS', + 'PATCH', + 'POST', + 'PUT', +) + +CORS_ALLOW_HEADERS = ( + 'accept', + 'accept-encoding', + 'authorization', + 'content-type', + 'dnt', + 'origin', + 'user-agent', + 'x-csrftoken', + 'x-requested-with', +) + +CORS_ALLOW_CREDENTIALS = True + diff --git a/django1/urls.py b/django1/urls.py new file mode 100644 index 0000000..493a76b --- /dev/null +++ b/django1/urls.py @@ -0,0 +1,33 @@ +"""django1 URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.1/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 +# router functionality for Django, and the PersonalNoteViewSet we just created +from rest_framework import routers +from notes.api import PersonalNoteViewSet +from django.urls import path, include +from rest_framework.authtoken import views +from graphene_django.views import GraphQLView + +router = routers.DefaultRouter() +router.register('notes', PersonalNoteViewSet) + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include(router.urls)), + path('api-token-auth/', views.obtain_auth_token), + path('graphql/', GraphQLView.as_view(graphiql=True)) +] diff --git a/django1/wsgi.py b/django1/wsgi.py new file mode 100644 index 0000000..c659d94 --- /dev/null +++ b/django1/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for django1 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.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django1.settings') + +application = get_wsgi_application() diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..c0eb6fb --- /dev/null +++ b/manage.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == '__main__': + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django1.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) diff --git a/notes/__init__.py b/notes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notes/admin.py b/notes/admin.py new file mode 100644 index 0000000..67bc8a1 --- /dev/null +++ b/notes/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin +from .models import Note, PersonalNote + +class NoteAdmin(admin.ModelAdmin): + readonly_fields=('created_at', 'last_modified') + +# Register your models here. +admin.site.register((Note, PersonalNote), NoteAdmin) diff --git a/notes/api.py b/notes/api.py new file mode 100644 index 0000000..ed70d1d --- /dev/null +++ b/notes/api.py @@ -0,0 +1,32 @@ +# This is a file that would use serializers and viewsets to describe which parts of the model we want to expose to the API + +from rest_framework import serializers, viewsets +from .models import PersonalNote + +class PersonalNoteSerializer(serializers.HyperlinkedModelSerializer): + + # A nested class to tell it what parts of the model we want to access: + class Meta: + model = PersonalNote + fields = ('title', 'content') + + def create(self, validated_data): + # import pdb; pdb.set_trace() -> Debugger + user = self.context['request'].user + note = PersonalNote.objects.create(user=user, **validated_data) + return note + +class PersonalNoteViewSet(viewsets.ModelViewSet): + # Link back to the serializer class + serializer_class = PersonalNoteSerializer + # Next, add which records to search for. We could use filters here, but for now, grab all of them: + queryset = PersonalNote.objects.none() + + def get_queryset(self): + user = self.request.user + + if user.is_anonymous: + return PersonalNote.objects.none() + else: + return PersonalNote.objects.filter(user=user) + diff --git a/notes/apps.py b/notes/apps.py new file mode 100644 index 0000000..b6155ac --- /dev/null +++ b/notes/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class NotesConfig(AppConfig): + name = 'notes' diff --git a/notes/migrations/0001_initial.py b/notes/migrations/0001_initial.py new file mode 100644 index 0000000..8cad648 --- /dev/null +++ b/notes/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 2.1 on 2018-08-28 02:58 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Note', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('title', models.CharField(max_length=200)), + ('content', models.TextField(blank=True)), + ], + ), + ] diff --git a/notes/migrations/0002_auto_20180828_1611.py b/notes/migrations/0002_auto_20180828_1611.py new file mode 100644 index 0000000..b600769 --- /dev/null +++ b/notes/migrations/0002_auto_20180828_1611.py @@ -0,0 +1,25 @@ +# Generated by Django 2.1 on 2018-08-28 20:11 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('notes', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='note', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='note', + name='last_modified', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/notes/migrations/0003_personalnote.py b/notes/migrations/0003_personalnote.py new file mode 100644 index 0000000..9c673f8 --- /dev/null +++ b/notes/migrations/0003_personalnote.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1 on 2018-08-28 22:00 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('notes', '0002_auto_20180828_1611'), + ] + + operations = [ + migrations.CreateModel( + name='PersonalNote', + fields=[ + ('note_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='notes.Note')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + bases=('notes.note',), + ), + ] diff --git a/notes/migrations/__init__.py b/notes/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/notes/models.py b/notes/models.py new file mode 100644 index 0000000..db22dcd --- /dev/null +++ b/notes/models.py @@ -0,0 +1,20 @@ +from django.db import models +from uuid import uuid4 +from django.contrib.auth.models import User + +class Note(models.Model): + # Create your models here. + id = models.UUIDField(primary_key=True, default=uuid4, editable=False) + title = models.CharField(max_length=200) + content = models.TextField(blank=True) + created_at = models.DateTimeField(auto_now_add=True) + last_modified = models.DateTimeField(auto_now=True) + + def __str__(self): + return f'<{self.__class__.__name__}: {self.id} {self.title}>' + +class PersonalNote(Note): # Inherits from Note! + user = models.ForeignKey(User, on_delete=models.CASCADE) + + def __str__(self): + return f'<{self.__class__.__name__}: {self.id} {self.title}>' diff --git a/notes/schema.py b/notes/schema.py new file mode 100644 index 0000000..78f1994 --- /dev/null +++ b/notes/schema.py @@ -0,0 +1,27 @@ +from django.conf import settings +from graphene_django import DjangoObjectType +import graphene +from .models import PersonalNote as PersonalNoteModel + +class PersonalNote(DjangoObjectType): + """Describe which model we want to expose through GraphQL.""" + class Meta: + model = PersonalNoteModel + # Describe the data as a node in a graph for GraphQL + interfaces = (graphene.relay.Node, ) + +class Query(graphene.ObjectType): + """Describe which records we want to show.""" + notes = graphene.List(PersonalNote) + + def resolve_notes(self, info): + """Decide which notes we want to return.""" + user = info.context.user # Find this with the debugger + + if user.is_anonymous: + return PersonalNoteModel.objects.none() + else: + return PersonalNoteModel.objects.filter(user=user) + +# Add a schema and attach to the query +schema = graphene.Schema(query=Query) \ No newline at end of file diff --git a/notes/tests.py b/notes/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/notes/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/notes/views.py b/notes/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/notes/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From f7bc73fe4d541e6697c0bf498befcada26382345 Mon Sep 17 00:00:00 2001 From: eddygonzalez9708 Date: Sun, 9 Sep 2018 21:10:30 -0400 Subject: [PATCH 3/3] Added the Mutation --- notes/schema.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/notes/schema.py b/notes/schema.py index 78f1994..4e768cc 100644 --- a/notes/schema.py +++ b/notes/schema.py @@ -23,5 +23,28 @@ def resolve_notes(self, info): else: return PersonalNoteModel.objects.filter(user=user) +class CreatePersonalNote(graphene.Mutation): + class Arguments: + """Input fields""" + title = graphene.String() + content = graphene.String() + + """Output fields""" + personalnote = graphene.Field(PersonalNote) + ok = graphene.Boolean() + status = graphene.String() + + def mutate(self, info, title, content): + user = info.context.user + if user.is_anonymous: + return CreatePersonalNote(ok=False, status="Must be logged in") + else: + new_note = PersonalNoteModel(title=title, content=content, user=user) + new_note.save() + return CreatePersonalNote(personalnote=new_note, ok=True, status="Successful") + +class Mutation(graphene.ObjectType): + create_personal_note = CreatePersonalNote.Field() + # Add a schema and attach to the query -schema = graphene.Schema(query=Query) \ No newline at end of file +schema = graphene.Schema(query=Query, mutation=Mutation) \ No newline at end of file