【DRF】venv仮想環境を用いて、DRFシステムで書籍管理システムを作成する(6)データの取得
ここからは、GET、POST、PUT、DELETE の各種メソッドを使用して、API にリクエストを送り、データを処理します。
14.データ取得
14-1.メソッドの種類と役割
一覧表にまとめると以下のようになります。
メソッド | 内容 |
---|---|
GET | 取得 |
POST | 作成 |
PUT / PATCH | 更新 |
DELETE | 削除 |
PUT / PATCH が目新しい感じがしますが、こうやって見ると単純ですね。
まずは、GET メソッドを使用して、API からデータを取得して、画面に表示していきます。
14-2.App.vue 変更
App.vue はメインのファイルになります。ここで、ルーターを指定しています。
Vue Routerとは?
Vue Routerとは Vue.jsのプラグインの一つで、SPA(Single Page Application)のルーティング制御をする為のものです。
Vue Routerを使用すると、ページ全体のリロードをせずに画面遷移するアプリケーションを簡易に実装する事が出来ます。
vue/src/App.vue
編集前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<template> <v-app> <v-main> <router-view/> </v-main> </v-app> </template> <script> export default { name: 'App', data: () => ({ // }) } </script> |
編集後
1 2 3 4 5 6 7 8 9 10 11 |
<template> <v-app> <router-view /> </v-app> </template> <script> export default { name: 'App' } </script> |
14-3.router 変更
ルーターを修正します。
vue/src/router/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue' const routes = [ { path: '/', name: 'home', component: HomeView }, { 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/AboutView.vue') } ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router |
12-4.axiosをインストール
APIでデータを取得できるように設定していきます。
API実行のため、axiosをインストールします。
axios とはどういったものなのかをChatGPTさんに質問したところ、下記の回答を得ました。
【特徴】
1. Promiseベース:非同期通信をPromiseで扱えるので、コードがシンプルになります。
2. ブラウザとNode.js両方で動作:フロントエンドとバックエンドの両方で利用可能です。
3. リクエストの設定が簡単:ヘッダーやタイムアウト設定などを簡単に指定できます。
4. インターセプター機能:リクエストやレスポンスを処理する前にカスタムロジックを追加できます。
5. 自動的なJSONデータ変換:リクエストとレスポンスのデータを自動的にJSON形式で扱います。
次のコマンドでインストールを行います。
実行する階層は vue ディレクトリです。
依存関係の競合を無視してインストールするため、オプションを付けてインストールしてみます。
1 |
npm install axios --legacy-peer-deps |
npm 5 以降では、上記コマンドでaxios
はpackage.json
のdependencies
に追加されるとのことですので、追加されたか確認します。
vue/package.json
1 2 3 4 |
"dependencies": { "axios": "^1.7.7", ←★これ ..... }, |
vue/src/common/api.service.js を作成し、以下を設定します。
common ディレクトリを作成し、その中に api.service.js ファイルを作成します。
vue/src/common/api.service.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import axios from 'axios' axios.defaults.xsrfCookieName = 'csrftoken' axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN' axios.defaults.withCredentials = true export { axios } const apiClient = axios.create({ baseURL: 'http://localhost:8000/book/api', // APIのベースURLを設定 headers: { 'Content-Type': 'application/json' } }) export default { get (resource) { return apiClient.get(resource) }, post (resource, data) { return apiClient.post(resource, data) }, put (resource, data) { return apiClient.put(resource, data) }, delete (resource) { return apiClient.delete(resource) } } |
14-5.vue.config.js の変更
vue/vue.config.js を変更します。
vue/vue.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
const webpack = require('webpack') // 追加 ~(中略)~ module.exports = { ~(中略)~ // 以下を追加 configureWebpack: { plugins: [ new webpack.DefinePlugin({ '__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': JSON.stringify(false), '__VUE_OPTIONS_API__': JSON.stringify(true), '__VUE_PROD_DEVTOOLS__': JSON.stringify(false) }), ] } }, ~(以下略)~ |
14-6.APIから本のタイトルを取得する
いよいよAPIを実行し、DB内のタイトルを取得してみます。
vue/src/views/HomeView.vue を以下のように修正します。
ボタンは Vuetify のボタンを使用しています。
vue/src/views/HomeView.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<template> <h1>Books</h1> <div> <v-btn class="mx-5" @click="fetchItems"> BookTitleを取得 </v-btn> <ul v-for='book in items' :key="book.id" class="ml-10 mt-5"> <li>ID: {{ book.id }}</li> <li>Title: {{ book.title }}</li> </ul> </div> </template> <script> import ApiService from '@/common/api.service' export default { name: 'bookTitle', data () { return { items: [] } }, methods: { async fetchItems () { try { const response = await ApiService.get('/books/') this.items = response.data.results || [] } catch (error) { console.error('APIエラー:', error) } } } } </script> |
ボタンの表示画面
タイトルを取得した画面
14-7. ボタンの色を変えてみる
白いボタンが見にくかったので、ボタン色を青くしてみます。
v-btn に "color" を設定します。
vue/src/views/HomeView.vue
1 2 3 |
<v-btn color="primary" class="mx-5" @click="fetchItems"> BookTitleを取得 </v-btn> |
結果
14-8. 詳細データの取得
本のタイトルを詳細画面に遷移するリンクとし、遷移先画面で詳細情報を表示するようにします。
まずルーターに詳細画面用の設定を追加します。
vue/src/router/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue' import BookDetailView from '../views/BookDetailView.vue' // (14-8)この一行を追加 const routes = [ { path: '/', name: 'home', component: HomeView }, { 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/AboutView.vue') }, // (14-8)以下を追加 { path: '/books/:id', name: 'book_detail', component: BookDetailView, props: true } ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router |
14-9.HomeView.vue修正
書籍のタイトルを取得しているページの書籍表示部分を修正し、書籍タイトルをリンクとし、詳細画面に遷移するようにします。
ここでは、 router-link を使用してリンクを作成します。
vue/src/views/HomeView.vue
1 |
<li>Title: <router-link :to="{ name: 'book_detail', params:{ id: book.id } }">{{ book.title }}</router-link></li> |
name には vue/src/router/index.js
で指定した book_detail
をあてがいます。
14-10.詳細ページ(BookDetailView.vue)追加
詳細データを表示する画面のファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<template> <h1>Book Detail</h1> <div> <v-container> <p>ID: {{ id }}</p> <p>Title: {{ book.title }}</p> <p>Publisher: {{ book.publisher }}</p> </v-container> </div> </template> <script> import ApiService from '@/common/api.service' export default { name: 'BookDetail', props: ['id'], data () { return { book: {} } }, async created () { try { const response = await ApiService.get(`/books/${this.id}/`) this.book = response.data } catch (error) { console.error('APIエラー:', error) } } } </script> |
キモは
1 |
const response = await ApiService.get(`/books/${this.id}/`) |
の部分です。
この get() 中のURLは、項番5-2.で行ったDjangoのルーティングで指定したエンドポイントを指定します。
項番5-2.で指定した、Django のルーティング内容は以下の内容です。
django/apps/book/api/urls.py
1 2 3 4 5 6 7 8 9 10 |
from django.urls import path from . import views urlpatterns = [ path('books/', views.BookListView.as_view(), name='book_list'), path('books/<int:pk>/', views.BookDetailView.as_view(), name='book_detail'), ←★これ path('publishers/', views.PublisherListView.as_view(), name='publisher_list'), path('publishers/<int:pk>/', views.PublisherDetailView.as_view(), name='publisher_detail'), ] |
また、 .get() の中はシングルクォーテーションではなく、バッククォート(@のキーにあると思います)で囲み、最後にスラッシュを忘れずに記述しましょう。
私はこのスラッシュをつけ忘れたがために20分ほど悩んでしまいました(苦笑)
動作確認
ボタン押下後
タイトル押下後
上記動作の動画Gifです。
実際にAPIを利用してフロント側の処理を行えるようにできました。