Python

Django Vue.js и GraphQL

Spread the love

Введение

Это не подробный учебник с информацией о каждой платформе. В этой статье я описал только необходимые пункты конфигурации а так же причины почему это делается.

Весь проект можно найти на странице GitHub

Создание Django проекта

Итак начнем с создания проекта на Django. Для начало создадим соотвествующе виртуальное окружение с помощью pipenv.

mkdir myproject
cd myproject
pipenv shell --three --python 3.6

Установим django

(myproject) bash-3.2$ pipenv install django

Далее создадим проект

(myproject) bash-3.2$ django-admin startproject myproject .

Инициализируем базу данных

(myproject) bash-3.2$ python manage.py migrate

Сразу создадим администратора

(myproject) bash-3.2$ python manage.py createsuperuser

Проверим работу

(myproject) bash-3.2$ python manage.py runserver

Перейдем по ссылке http://127.0.0.1:8000/

Проверим имя администратора и его пароль. Перейдем по ссылке http://127.0.0.1:8000/admin/ и залогинимся

Разделим конфигурацию на производственную и локальную для разработки

В папке проекта создадим папку myproject/settings, а так же следующие файлы:

__init__.py
dev.py
prod.py

Переместим файл settings.py в папку myproject/settings.

Изменим BASE_DIR и добавим STATIC_ROOT в файл settings.py.

BASE_DIR = os.path.dirname(os.path.dirname(
    os.path.dirname(os.path.abspath(__file__))))
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

В __init__.py добавим импорт настроек по умолчанию

from .dev import *

В dev.py импортируем базовые настройки из settings.py и добавим настройки для hosts и debug.

from .settings import *

DEBUG = True
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']

В prod.py добавим информацию о DEBUG и hosts

from .settings import *

DEBUG = False
ALLOWED_HOSTS = ['*']

Создадим скрип запуска dev и prod. Для этого создадим файл run.sh в корне проекта.
Эти 3 команды для синхронизации всех изменений на сервере перед его запуском.

  • collectsatic — перемещает все статику в папку STATIC_ROOT
  • makemigrations — создаем новую миграцию основанную на изменения в моделях
  • migrate — применяет миграцию
#!/bin/bash
# pipenv shell

case $1 in
  dev)
    python manage.py runserver --settings=myproject.settings.dev
    ;;
  prod)
    python manage.py collectstatic --noinput
    python manage.py makemigrations    
    python manage.py migrate
    python manage.py runserver --settings=myproject.settings.prod
    ;;
esac

Проверим запуск проекта

(myproject) bash-3.2$ sh run.sh dev
(myproject) bash-3.2$ sh run.sh prod

Создание домашней страницы

Создадим в корне проекта папку templates и в ней файл index.html со следующим содержимым:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  Home page
</body>

</html>

Добавим папку templates в параметр конфигурации TEMPLATES. Для этого внесем следующую строку в mypproject\settings\settings.py:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
...

Добавим index.html в список urls. Для этого внесем следующие изменения в myproject\url.py:

from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView

urlpatterns = [
    path('', TemplateView.as_view(template_name='index.html')),
    path('admin/', admin.site.urls),
]

Проверим работу в dev и prod режиме:

(myproject) bash-3.2$ sh run.sh dev
(myproject) bash-3.2$ sh run.sh prod

Добавление Django Debug Toolbar

Установим Django Debug Toolbar:

(myproject) bash-3.2$ pipenv install django-debug-toolbar

Добавим debug toolbar в настройки dev. Для этого добавим следующие изменения в myproject\settings\dev.py:

MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
INSTALLED_APPS.append('debug_toolbar')
INTERNAL_IPS = ('127.0.0.1', 'localhost')

Добавим debug toolbar в список url. Внесем следующие изменения в myproject\urls.py:

from django.conf import settings
from django.conf.urls import include, url

...

if settings.DEBUG:
    import debug_toolbar
    urlpatterns = [
        url(r'^__debug__/', include(debug_toolbar.urls)),
    ] + urlpatterns

Проверим работу toolbar

(myproject) bash-3.2$ sh run.sh dev

Добавление фронтенда на vue.js

Установите глобально vue/cli если у вас еще до сих пор не установлена эта библиотека:

npm i -g @vue/cli

Создадим новый проект на Vue. Для этого в новом окне терминала в корне проекта запустите команду:

vue create frontend

Ответьте на все вопросы скрипта по вашему усмотрению. Но если будет предложен выбор то выберите вариант с vue-router, если нет то придется до установить нужные компоненты см. ниже.

Далее перейдем в папку проекта

cd frontend

Запустим новый проект командой:

yarn serve

Так как на этом шаге возможны различные варианты генерации базового приложения то нужно убедиться что у нас в приложение есть следующие страницы views/About.vue и views/Home.vue. Если они не создались создайте их вручную (полную версию см в репозитории). Так же возможно нужно будет до установить следующие компоненты.

npm install --save vue-router
npm install --save vuex

Нужно проверить наличие router.js:

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
    }
  ]
})

И наличия store.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {

  },
  mutations: {

  },
  actions: {

  }
})

Содержимое main.js должно быть примерно таким:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

А содержимое App.js таким:

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'app',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Интеграция Vue.js с Django

Установите webpack-boundle-tracker в папку фронтенд проекта. Для этого запустите следующие команды в папке frontend:

npm install webpack-bundle-tracker --save-dev
npm install write-file-webpack-plugin --save-dev

Запустите следующую команду в папке проекта Django:

(myproject) bash-3.2$ pipenv install django-webpack-loader

В папке фронтенд проекта frontend создайте файл vue.config.js и внесите в него следующие содержимое:

var BundleTracker = require('webpack-bundle-tracker')
var WriteFilePlugin = require('write-file-webpack-plugin')


module.exports = {
  outputDir: (process.env.NODE_ENV === "production" ? 'dist' : 'static'),
  baseUrl: '/',

 devServer: {
    publicPath: "http://localhost:8080/",
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
      "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Access-Control-Request-Headers, Access-Control-Request-Method",
      "Access-Control-Allow-Credentials": "true"
    }
  },

  chainWebpack: config => {
    config.optimization.splitChunks({
      cacheGroups: {
        vendors: {
          name: 'chunk-vendors',
          test: /[\\\/]node_modules[\\\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        common: {
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    })
  },

  configureWebpack: {
    output: {
      filename: 'js/[name].js',
      chunkFilename: 'js/[name].js'
    },
    plugins: [
      new WriteFilePlugin(),
      (process.env.NODE_ENV === "production" ?
        new BundleTracker({
          filename: 'webpack-stats-prod.json',
          publicPath: '/'
        }) :
        new BundleTracker({
          filename: 'webpack-stats.json',
          publicPath: 'http://localhost:8080/'
        })
      )
    ]
  }
}

baseUrl: “./”: Это адрес на которых webpack будет генерировать все ссылки. ./ означает, что слеш не должно быть в начале адреса и все ссылки идут из текущей папки. Эта конфигурация не будет работать для приложений с router, где браузер будет загружать ссылку со страницы. (для примера img/logo2.png по адресу http://127.0.0.1:8000/about/ будет загружать файл с адреса http://127.0.0.1:8000/about/img/logo2.png который не будет верным)

если baseUrl: "/" тогда все ссылки будут загружены с URL хоста. Если ваша веб-страница находится в папке (не на главном хосте), вам нужно изменить эту конфигурацию на базовую папку, в которой вы размещаете файлы vue.js. (например, /username/mypage)

BundleTracker будет генерировать webpack-stats.json который будет использоваться в проекте. (для prod и dev)

outputDir — это адрес расположения все файлов для Djange (static или dist)

outputchainWebpack — поскольку мы не используем файлы *.html по умолчанию у нас *.vue, нам нужно иметь один и тот же список блоков для загрузки в среде prod и dev (webpack-stats.json и webpack-stats-prod.json)

WriteFilePlugin — этот плагин будет копировать файлы в папку назначения, даже когда мы запускаем webpack-dev-server. Это важно, потому что Django использует статические файлы, такие как изображения из этой папки.

Протестируем как это работает. Запустите следующие команды в папке фронтенд проекта

vue inspect > output-dev.js
vue inspect --mode production > output-prod.js

Должно создаться два файла output-dev.js и output-prod.js

Добавим WEBPACK_LOADER в проект Django. Для этого внесите следующие изменения в myproject\settings\settings.py:

 INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'webpack_loader'
]

В myproject\settings\dev.py следующие изменения:

...
WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': '',
        'STATS_FILE': os.path.join(BASE_DIR, 'frontend/webpack-stats.json'),
    }
}

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]
MEDIA_URL = '/dmedia/'
MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

VUE_ROOT = os.path.join(os.path.join(BASE_DIR, "frontend"), "static")

В myproject\settings\prod.py следующие:

WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': '',
        'STATS_FILE': os.path.join(BASE_DIR, 'frontend/webpack-stats-prod.json'),
    }
}

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

MEDIA_URL = '/dmedia/'
MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

VUE_ROOT = os.path.join(os.path.join(BASE_DIR, "frontend"), "dist")
  • BUNDLE_DIR_NAME устанавливает свой префикс . Это префикс для файлов .js и .css. Параметр должен быть пустым для нашей конфигурации и совпадать с baseUrl из vue.config.js.
  • STATS_FILE — файл для загрузки статистики webpack.
  • MEDIA_URL — Медиа файлы из Django (dmedia потому что vue.js так же имеет папку media дляmp4|webm|ogg|... файлов)
  • MEDIA_ROOT — Путь куда будут загружаться пользовательские медиа файлы
  • STATIC_URL — Url для статики в Django.
  • STATIC_ROOT — Куда все файлы будут копироваться при запуски команды python manage.py collectstatic
  • VUE_ROOT — полезный путь для всего вывода vue.js. Он должен соответствовать папке назначения проекта vue.js.

Создадим папку static в проекте Django:

(myproject) bash-3.2$ mkdir static 

Настроим все наши ulrs для vue.js, static и media. Для этого внесем следующие изменения в myproject\urls.py:

from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView
from django.conf import settings
from django.conf.urls import include, url
from django.views.static import serve
import os


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='index.html')),
    url(r'^static/(?P<path>.*)$', serve,
        {'document_root': settings.STATIC_ROOT}),
    url(r'^dmedia/(?P<path>.*)$', serve,
        {'document_root': settings.MEDIA_ROOT}),
    url(r'^media/(?P<path>.*)$', serve,
        {'document_root': os.path.join(settings.VUE_ROOT, 'media')}),
    url(r'^img/(?P<path>.*)$', serve,
        {'document_root': os.path.join(settings.VUE_ROOT, 'img')}),
    url(r'^js/(?P<path>.*)$', serve,
        {'document_root': os.path.join(settings.VUE_ROOT, 'js')}),
    url(r'^css/(?P<path>.*)$', serve,
        {'document_root': os.path.join(settings.VUE_ROOT, 'css')}),
    url(r'^fonts/(?P<path>.*)$', serve,
        {'document_root': os.path.join(settings.VUE_ROOT, 'fonts')}),
]


if settings.DEBUG:
    import debug_toolbar
    urlpatterns = [
        url(r'^__debug__/', include(debug_toolbar.urls)),
    ] + urlpatterns

все эти URL-адреса аналогичны тем, которые можно найти в проекте vue.js. Мы используем dmedia для django media, потому что vue.js использует ту же папку для медиа файлов.

Перемести ваш favicon.ico в главную папку проекта myproject и внесите соотвествующие изменения в /myproject/urls.py:

import os
from django.views.generic.base import RedirectView
favicon_view = RedirectView.as_view(url=os.path.join(settings.STATIC_URL,'favicon.ico'), permanent=True)

urlpatterns = [
    path('favicon.ico', favicon_view),
...

Внесите следующие изменения в templates\index.html:

{% load render_bundle from webpack_loader %}
{% load static from staticfiles %}

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>frontend</title>
  <meta http-equiv=X-UA-Compatible content="IE=edge">
  <meta name=viewport content="width=device-width,initial-scale=1">
  <link rel=icon href="{% static 'favicon.ico' %}"> {% render_bundle 'chunk-vendors' %}
</head>

<body>
  <noscript>
    <strong>
      We're sorry but frontend doesn't work properly without JavaScript enabled. Please enable it to
      continue.
    </strong>
  </noscript>

  {% for i in 'abc' %}
  <strong>{{ i }} DJANGO PART</strong>
  {% endfor %}

  <div id=app>
  </div>
  {% render_bundle 'app' %}

</body>

</html>

{% render_bundle 'chunk-vendors' %} — это загрузчик для splitChunks в dev иprod окружениях. Если мы не настроим splitChunks для dev Django покажет следующую ошибку.

Главное приложение vue.js:

<div id=app>
  </div>
  {% render_bundle 'app' %}
...

Протестируем dev проект. Для этого в папке фронтенд проекта запустите следующую команду:

npm run serve

А в главной папке Django проекта:

(myproject) bash-3.2$ sh run.sh dev

Протестируем запуск в прод окружение. Для этого запустите в папке фронтенд проект

npm run build

А в папке Django команду:

(myproject) bash-3.2$ sh run.sh prod

Добавление url для страницы About

Добавим новый url в приложение на Django. Это может быть тот же файл, что и для приложения (index.html), потому что vue.js знает, какой URL-путь вы хотите использовать.

...

urlpatterns = [
    path('', TemplateView.as_view(template_name='index.html')),
    path('about/', TemplateView.as_view(template_name='index.html')),
....

Интеграция Django с GraphQL

Установим graphene_django. Для этого в папке проекта Django наберите следующие команды:

(myproject) bash-3.2$ pipenv install graphene_django
(myproject) bash-3.2$ pipenv install django-filter==1.1.0

Далее добавим новое приложение Django

(myproject) bash-3.2$ django-admin startapp tasks

И добавим новые модели в myproject/tasks/models.py:

from django.db import models
from django.contrib import admin


# Create your models here.
class Task(models.Model):
    isDone = models.BooleanField()
    name = models.CharField(max_length=100)
    description = models.TextField()

    def __str__(self):
        return self.name


@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
    pass

Добавим новое приложение в настройки myproject/settings/settings.py:

INSTALLED_APPS = [
    ...
    # Install the ingredients app
    'tasks',
]

Запустим миграцию:

(myproject) bash-3.2$ python manage.py makemigrations
(myproject) bash-3.2$ python manage.py migrate

Создадим JSON файл с данными фикстур tasks\fixtures\tasks.json:

[{
  "model": "tasks.Task",
  "pk": 1,
  "fields": {
    "isDone": "True",
    "name": "Do shoppping",
    "description": "milk, butter"
  }
}, {
  "model": "tasks.Task",
  "pk": 2,
  "fields": {
    "isDone": "False",
    "name": "Do laundry",
    "description": "all clothes"
  }
}, {
  "model": "tasks.Task",
  "pk": 3,
  "fields": {
    "isDone": "False",
    "name": "Fix computer",
    "description": "Fix computer"
  }
}]

Загрузим этот файл в базу:

(myproject) bash-3.2$ python manage.py loaddata tasks

Создадим файл tasks\schema.py со следующим содержимым и добавим схему для наших моделей:

import graphene
from graphene_django.types import DjangoObjectType
from graphql_relay.node.node import from_global_id
from tasks.models import Task


class TaskType(DjangoObjectType):
    class Meta:
        model = Task


class CreateTask(graphene.Mutation):
    ok = graphene.Boolean()
    task = graphene.Field(lambda: TaskType)

    class Arguments:
        name = graphene.String()
        description = graphene.String()

    def mutate(self, info, name, description):
        task = Task(name=name, description=description, isDone=False)
        task.save()
        ok = True
        return CreateTask(task=task, ok=ok)


class UpdateTask(graphene.Mutation):
    task = graphene.Field(lambda: TaskType)
    ok = graphene.Boolean()

    class Arguments:
        id = graphene.String()
        IsDone = graphene.Boolean()

    def mutate(self, info, id, IsDone):
        task = Task.objects.get(pk=id)
        task.isDone = IsDone
        task.save()
        ok = True
        return UpdateTask(task=task, ok=ok)


class Query(graphene.ObjectType):
    tasks = graphene.List(TaskType)

    def resolve_tasks(self, info):
        return Task.objects.all()


class Mutations(graphene.ObjectType):
    create_task = CreateTask.Field()
    update_task = UpdateTask.Field()

Добавим глобальную схему в наш проект то есть создадим файл myproject\schema.py со следующим содержимым:

import graphene
import tasks.schema


class Query(tasks.schema.Query, graphene.ObjectType):
    # This class will inherit from multiple Queries
    # as we begin to add more apps to our project
    pass


class Mutation(tasks.schema.Mutations, graphene.ObjectType):
    pass

schema = graphene.Schema(query=Query, mutation=Mutation)

Добавим graphene_django в настройки Django myproject\settings\settings.py:

INSTALLED_APPS = [
...
    'graphene_django',
]

Добавить новую схему в список GRAPHENE в файле нстроек myproject\settings\settings.py:

GRAPHENE = {
    'SCHEMA': 'myproject.schema.schema'
}

Добавим url для graphql в файле myproject\urls.py:

...
from graphene_django.views import GraphQLView
from django.views.decorators.csrf import csrf_exempt

urlpatterns = [
    ...
    url(r'^graphql', csrf_exempt(GraphQLView.as_view(graphiql=True))),
]

Запустите сервер и зайдите на страницу http://127.0.0.1:8000/graphql/ для тестового запроса:

query {
  tasks {
    id,
    isDone,
    name,
    description
  }
}

Протестируйте запрос на создание записи:

mutation createTask($name:String, $description: String) {
      createTask(name: $name, description: $description) {
        task {
            name
           description
        }
        ok
    }
}

переменные:

{
  "name": "something to do",
  "description": "description for task"
}

Протестируем запрос на редактирование записи:

mutation updateTask($id: String, $IsDone: Boolean) {
  updateTask(id: $id, IsDone: $IsDone) {
    task {
      id
      isDone
      name
      description
    }
    ok
  }
}

переменные:

{"id": "2", "IsDone": true}

Интеграция vue.js с graphql

Устанвим apollo в фронтенд проект

npm install --save vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag

Создадим конфигурационный файл для apollo frontend/src/vue-apollo.js:

import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import VueApollo from 'vue-apollo'

const httpLink = new HttpLink({
  // You should use an absolute URL here
  uri: '/graphql',
})

// Create the apollo client
const apolloClient = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache(),
  connectToDevTools: true,
})

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
})

// Install the vue plugin
Vue.use(VueApollo)

export default apolloProvider

uri: /graphql — Это urlдля взаимодействия graphql с Django.

Добавим apolloProvider в main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import apolloProvider from './vue-apollo'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  apolloProvider: apolloProvider,
  render: h => h(App)
}).$mount('#app')

Добавим a query в vue.js компонент (имя tasks должно совпадать с именем в query). Внесите следующие изменения в frontend/src/views/Home.vue:

<template>
  <div class="home">
    <div v-for="i in   tasks" :key="i.id">
      <ul>
        <li>
          <strong>{{i.name}}</strong>:<span>{{i.description}}</span>
        </li>
      </ul>
    </div>

....
<script>
import gql from 'graphql-tag'

const TaskQuery = gql`
  query {
    tasks {
      id
      isDone
      name
      description
    }
  }
`;

export default {
 data() {
      return {
        // Initialize your apollo data
        tasks: '',
      }
  },
  apollo: {
    // Simple query that will update the 'hello' vue property
    tasks: TaskQuery,
  },
      
...

Запустите dev и prod конфигурации что бы проверить как это работает. В локальном запуске фронтенд не будет работать ( http://127.0.0.1:8080/ ) потому что мы определили uri как /graphql а адрес http://127.0.0.1:8080/graphql вернет ошибку.

npm run serve
(myproject) bash-3.2$ sh run.sh dev

Добавить создание модели в vue.js

Добавьте поля ввода в шаблон для добавления новых значений. Файл frontend\src\views\Home.vue:

<template>
  <div class="home">
    <div>
      <span>Name:</span><input type="text" v-model="name">
      <span>Description:</span><input type="text" v-model="description">
      <button @click="create_task">Add</button>
    </div>
...
 data() {
      return {
        name: '',
        description: '',
...

Добавим запрос createTask. Файл frontend\src\views\Home.vue:

<script>
...

const TaskCreate = gql`mutation createTask($name:String, $description: String) {
      createTask(name: $name, description: $description) {
        task {
            id
            isDone
            name
           description
        }
        ok
    }
 }`

Добавим метод create_task. Файл frontend\src\views\Home.vue:

... 

  methods: {
    async create_task() {
      const name = this.name
      const description = this.description 

      // Call to the graphql mutation
      let data = await this.$apollo.mutate({
        // Query
        mutation: TaskCreate,
        // Parameters
        variables: {
          name: name,
          description: description
        },
        update: (store, { data: { createTask } }) => {
          // Add to All tasks list
          const data = store.readQuery({ query: TaskQuery })
          data.tasks.push(createTask.task)
          store.writeQuery({ query: TaskQuery, data })
    },
    // optimisticResponse: {
    //       __typename: 'Mutation',
    //       createTask: {
    //         __typename: 'CreateTask',
    //         task: {
    //           __typename: "TaskType",
    //           id: -1,
    //           isDone: false,
    //           name: name,
    //           description: description                 
    //         },
    //         ok: false
    //       }
    // },
      })
      var t = data.data.createTask.task
      console.log('Added: ' , t)
      this.name = ''
      this.description = ''
    }
...
  • mutation: запрос на мутацию
  • variables: перменные используемы в зпросе
  • update: что будет делаться после update. Считайте локального cash для запроса readQuery с запросом TaskQuery, подстановка новых данных в data.tasks и запуск writeQuery, чтобы обновить локальный список задач.
  • optimisticResponse: Только если необходимо вернуть ответ до завершения запроса, чтобы не ждать результатов.

Протестируем запрос на создание записи

Добавление редактирование модели в vue.js

Добавьте checkbox для редактируемой модели. Файл frontend\src\views\Home.vue:

...

<div v-for="i in   tasks" :key="i.id">
      <ul>
        <li>
            <input type="checkbox" @input="update_task(i)" :checked="i.isDone">

Добавим запрос updateTask. Файл frontend\src\views\Home.vue:

<script>
...

     const TaskUpdate = gql`mutation updateTask($id: String, $IsDone: Boolean) {
  updateTask(id: $id, IsDone: $IsDone) {
    task {
      id
      isDone
      name
      description
    }
    ok
  }
}`
     
...

Добавим метод update_task. Файл frontend\src\views\Home.vue:

methods: {
    async update_task(i) {
      await this.$apollo.mutate({
        mutation: TaskUpdate,
        variables: {
          id: i.id,
          IsDone: !i.isDone
        },
      })
    },

...

Протестируйте метод редактирования

Заключение

В статье было продемонстрировано создания приложения на Django с API на GraphQL и реализацией фронтенд части на Vue.js.

Оригинальная статья: Django Vue.js and GraphQL — Step by step

Была ли вам полезна эта статья?
[8 / 4.3]

Spread the love
Editorial Team

View Comments

Recent Posts

Vue 3.4 Новая механика v-model компонента

Краткий перевод: https://vuejs.org/guide/components/v-model.html Основное использование​ v-model используется для реализации двусторонней привязки в компоненте. Начиная с Vue…

11 месяцев ago

Анонс Vue 3.4

Сегодня мы рады объявить о выпуске Vue 3.4 «🏀 Slam Dunk»! Этот выпуск включает в…

11 месяцев ago

Как принудительно пере-отобразить (re-render) компонент Vue

Vue.js — это универсальный и адаптируемый фреймворк. Благодаря своей отличительной архитектуре и системе реактивности Vue…

2 года ago

Проблемы с установкой сертификата на nginix

Недавно, у меня истек сертификат и пришлось заказывать новый и затем устанавливать на хостинг с…

2 года ago

Введение в JavaScript Temporal API

Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами…

2 года ago

Когда и как выбирать между медиа запросами и контейнерными запросами

Все, кто следит за последними событиями в мире адаптивного дизайна, согласятся, что введение контейнерных запросов…

2 года ago