├── graphqlapi ├── __init__.py ├── urls.py ├── schema.py ├── asgi.py ├── wsgi.py └── settings.py ├── resources ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── views.py ├── tests.py ├── apps.py ├── admin.py ├── models.py └── schema.py ├── .gitignore ├── requirements.txt ├── manage.py ├── README.md └── queries.md /graphqlapi/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ambientev 2 | __pycache__ 3 | *.pyc 4 | db.sqlite3 5 | .DS_Store 6 | myvenv 7 | -------------------------------------------------------------------------------- /resources/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /resources/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ResourcesConfig(AppConfig): 5 | name = 'resources' 6 | -------------------------------------------------------------------------------- /resources/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from resources.models import Artista, Disco 3 | 4 | classes = [Artista, Disco] 5 | 6 | for c in classes: 7 | admin.site.register(c) -------------------------------------------------------------------------------- /graphqlapi/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path 3 | from graphene_django.views import GraphQLView 4 | 5 | urlpatterns = [ 6 | path('admin/', admin.site.urls), 7 | path('graphql/', GraphQLView.as_view(graphiql=True)), 8 | ] -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aniso8601==7.0.0 2 | asgiref==3.2.5 3 | Django==3.1.13 4 | graphene==2.1.8 5 | graphene-django==2.9.0 6 | graphql-core==2.3.1 7 | graphql-relay==2.0.1 8 | promise==2.3 9 | pytz==2019.3 10 | Rx==1.6.1 11 | singledispatch==3.4.0.3 12 | six==1.14.0 13 | sqlparse==0.3.1 14 | -------------------------------------------------------------------------------- /graphqlapi/schema.py: -------------------------------------------------------------------------------- 1 | import graphene 2 | import resources.schema 3 | 4 | class Query(resources.schema.Query, graphene.ObjectType): 5 | # Essa classe irá herdar de múltiplas Queries 6 | # Ao começarmos a adicionar mais apps ao nosso projeto 7 | pass 8 | 9 | class Mutation(resources.schema.Mutation, graphene.ObjectType): 10 | pass 11 | 12 | schema = graphene.Schema(query=Query, mutation=Mutation) -------------------------------------------------------------------------------- /graphqlapi/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for graphqlapi project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'graphqlapi.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /graphqlapi/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for graphqlapi project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'graphqlapi.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /resources/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Disco(models.Model): 4 | titulo = models.CharField(max_length=100) 5 | ano_lancamento = models.DateField(null=True, blank=True) 6 | 7 | def __str__(self): 8 | return self.titulo 9 | 10 | class Meta: 11 | ordering = ['titulo'] 12 | 13 | class Artista(models.Model): 14 | nome = models.CharField(max_length=100) 15 | origem = models.CharField(max_length=100) 16 | discos = models.ManyToManyField(Disco) 17 | 18 | def __str__(self): 19 | return self.nome 20 | 21 | class Meta: 22 | ordering = ['nome'] -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'graphqlapi.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django GraphQLAPI Tutorial 2 | 3 | Neste guia vamos desenvolver uma GraphQLAPI com o framework Django através do auxílio da biblioteca Graphene. 4 | 5 | **[Leia o Tutorial](https://akiradev.netlify.app/posts/django-graphql-api/)** 6 | 7 | --- 8 | 9 | ## Instalação 10 | 11 | ### Clone o Repositório 12 | 13 | ``` 14 | git clone https://github.com/the-akira/Django-GraphQL-Tutorial.git 15 | ``` 16 | 17 | ### Dentro do Diretório Principal 18 | 19 | Crie um Ambiente Virtual 20 | 21 | ``` 22 | python -m venv myvenv 23 | ``` 24 | 25 | Ative o Ambiente Virtual 26 | 27 | ``` 28 | source myvenv/bin/activate 29 | ``` 30 | 31 | Instale os Requeriments 32 | 33 | ``` 34 | pip install -r requirements.txt 35 | ``` 36 | 37 | Sincronize o banco de dados 38 | 39 | ``` 40 | python manage.py migrate 41 | ``` 42 | 43 | Execute a aplicação 44 | 45 | ``` 46 | python manage.py runserver 47 | ``` 48 | 49 | Navegue até `http://127.0.0.1:8000/graphql/` para executar as Queries. -------------------------------------------------------------------------------- /queries.md: -------------------------------------------------------------------------------- 1 | # Queries 2 | 3 | ## Get Disco 4 | 5 | ``` 6 | query Disco { 7 | disco(id: 2) { 8 | id 9 | titulo 10 | anoLancamento 11 | } 12 | } 13 | ``` 14 | 15 | ## Get Discos 16 | 17 | ``` 18 | query Discos { 19 | discos{ 20 | id 21 | titulo 22 | anoLancamento 23 | } 24 | } 25 | ``` 26 | 27 | ## Get Artistas 28 | 29 | ``` 30 | query Artistas { 31 | artistas { 32 | id 33 | nome 34 | } 35 | } 36 | ``` 37 | 38 | ## Get Artista 39 | 40 | ``` 41 | query Artistas { 42 | artista(id: 1) { 43 | id 44 | nome 45 | origem 46 | } 47 | } 48 | ``` 49 | 50 | ## Get Artistas and Discos 51 | 52 | ``` 53 | query Artistas { 54 | artistas { 55 | id 56 | nome 57 | origem 58 | discos { 59 | titulo 60 | anoLancamento 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | ## Mutation Artista 67 | 68 | ``` 69 | mutation createArtista { 70 | createArtista(input: { 71 | nome: "Sonata Arctica", 72 | origem: "Finlandia" 73 | }) { 74 | artista { 75 | id 76 | nome 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | ## Mutation Disco 83 | 84 | ``` 85 | mutation createDisco { 86 | createDisco(input: { 87 | titulo: "Octavarium" 88 | }) { 89 | disco { 90 | id 91 | titulo 92 | } 93 | } 94 | } 95 | ``` -------------------------------------------------------------------------------- /resources/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-18 20:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Disco', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('titulo', models.CharField(max_length=100)), 19 | ('ano_lancamento', models.DateField(blank=True, null=True)), 20 | ], 21 | options={ 22 | 'ordering': ['titulo'], 23 | }, 24 | ), 25 | migrations.CreateModel( 26 | name='Artista', 27 | fields=[ 28 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 29 | ('nome', models.CharField(max_length=100)), 30 | ('origem', models.CharField(max_length=100)), 31 | ('discos', models.ManyToManyField(to='resources.Disco')), 32 | ], 33 | options={ 34 | 'ordering': ['nome'], 35 | }, 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /resources/schema.py: -------------------------------------------------------------------------------- 1 | import graphene 2 | from graphene_django.types import DjangoObjectType 3 | from resources.models import Artista, Disco 4 | 5 | class ArtistaType(DjangoObjectType): 6 | class Meta: 7 | model = Artista 8 | 9 | class DiscoType(DjangoObjectType): 10 | class Meta: 11 | model = Disco 12 | 13 | class Query(object): 14 | artista = graphene.Field(ArtistaType, id=graphene.Int()) 15 | disco = graphene.Field(DiscoType, id=graphene.Int()) 16 | artistas = graphene.List(ArtistaType) 17 | discos = graphene.List(DiscoType) 18 | 19 | def resolve_artista(self, info, **kwargs): 20 | id = kwargs.get('id') 21 | 22 | if id is not None: 23 | return Artista.objects.get(pk=id) 24 | return None 25 | 26 | def resolve_disco(self, info, **kwargs): 27 | id = kwargs.get('id') 28 | 29 | if id is not None: 30 | return Disco.objects.get(pk=id) 31 | return None 32 | 33 | def resolve_artistas(self, info, **kwargs): 34 | return Artista.objects.all() 35 | 36 | def resolve_discos(self, info, **kwargs): 37 | return Disco.objects.all() 38 | 39 | # Mutations 40 | class DiscoInput(graphene.InputObjectType): 41 | id = graphene.ID() 42 | titulo = graphene.String() 43 | ano_lancamento = graphene.String() 44 | 45 | class ArtistaInput(graphene.InputObjectType): 46 | id = graphene.ID() 47 | nome = graphene.String() 48 | origem = graphene.String() 49 | discos = graphene.List(DiscoInput) 50 | 51 | class CreateDisco(graphene.Mutation): 52 | class Arguments: 53 | input = DiscoInput(required=True) 54 | 55 | disco = graphene.Field(DiscoType) 56 | 57 | @staticmethod 58 | def mutate(root, info, input=None): 59 | disco_instance = Disco(titulo=input.titulo) 60 | disco_instance.save() 61 | return CreateDisco(disco=disco_instance) 62 | 63 | class CreateArtista(graphene.Mutation): 64 | class Arguments: 65 | input = ArtistaInput(required=True) 66 | 67 | artista = graphene.Field(ArtistaType) 68 | 69 | @staticmethod 70 | def mutate(root, info, input=None): 71 | discos = [] 72 | if input.discos: 73 | for disco_input in input.discos: 74 | disco = Disco.objects.get(pk=disco_input.id) 75 | if disco is None: 76 | return CreateArtista(disco=None) 77 | discos.append(disco) 78 | artista_instance = Artista( 79 | nome=input.nome, 80 | origem=input.origem 81 | ) 82 | artista_instance.save() 83 | artista_instance.discos.set(discos) 84 | return CreateArtista(artista=artista_instance) 85 | 86 | class Mutation(graphene.ObjectType): 87 | create_disco = CreateDisco.Field() 88 | create_artista = CreateArtista.Field() -------------------------------------------------------------------------------- /graphqlapi/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for graphqlapi project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.0.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '0fzcc+f#svu455g8cp0w2bt+ne5a6@(z^vzftz-f=pkq##%-sv' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'resources', 41 | 'graphene_django', 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | 'django.middleware.security.SecurityMiddleware', 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | 'django.middleware.csrf.CsrfViewMiddleware', 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 | 'django.contrib.messages.middleware.MessageMiddleware', 51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 | ] 53 | 54 | ROOT_URLCONF = 'graphqlapi.urls' 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': [], 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'graphqlapi.wsgi.application' 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/3.0/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.sqlite3', 81 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 82 | } 83 | } 84 | 85 | 86 | # Password validation 87 | # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators 88 | 89 | AUTH_PASSWORD_VALIDATORS = [ 90 | { 91 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 92 | }, 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 101 | }, 102 | ] 103 | 104 | 105 | # Internationalization 106 | # https://docs.djangoproject.com/en/3.0/topics/i18n/ 107 | 108 | LANGUAGE_CODE = 'en-us' 109 | 110 | TIME_ZONE = 'UTC' 111 | 112 | USE_I18N = True 113 | 114 | USE_L10N = True 115 | 116 | USE_TZ = True 117 | 118 | 119 | # Static files (CSS, JavaScript, Images) 120 | # https://docs.djangoproject.com/en/3.0/howto/static-files/ 121 | 122 | STATIC_URL = '/static/' 123 | 124 | GRAPHENE = { 125 | 'SCHEMA': 'graphqlapi.schema.schema', 126 | } --------------------------------------------------------------------------------