LoginSignup
5
6

More than 1 year has passed since last update.

Vue.js×Django REST Frameworkで画像登録及び表示処理を実装

Posted at

概要

Vue.jsとDjango REST Frameworkを用いて、画像の登録及び表示する。

GitHubリンク

環境

  • macOS Monterey

  • Chip Apple M1 Max

  • Python 3.10.3

  • Django 4.1.4

  • Django REST Framework 3.14.0

  • vue-cli 5.0.1

  • Vue.js 3.2.13

手順

仮想環境構築

仮想環境を構築します。

% python -m venv venv

アクティベートします。

% source venv/bin/activate

Django環境構築

Django, djangorestframework, Pillowをインストールします。

(venv)% pip install Django==4.1.4 djangorestframework==3.14.0 Pillow==9.3.0

djangoプロジェクトを生成します。

(venv)% django-admin start project project .

開発サーバーを起動し、ブラウザでhttp://127.0.0.1:8000を表示し、Djangoのデフォルト画面が表示されることを確認しましょう。

(venv)% python manage.py runserver

バックエンドを実装

djangoアプリを生成します。

(venv)% python manage.py startapp app

project/settings.pyを一部編集します。

...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework', # 追加
    'app' # 追加
]

...

# 以下を追記
MEDIA_URL = '/media/' # 画像ファイルを参照するためのURL
MEDIA_ROOT = BASE_DIR / "media" # 画像保存先ディレクトリ

# 信頼できる発信元リストに、Vueのオリジンを追加
CSRF_TRUSTED_ORIGINS = ['http://localhost:8080']

app/models.pyに、Imageモデルを定義します。

from django.db import models

class Image(models.Model):

    image = models.ImageField(
        verbose_name="画像",
        blank=True,
        null=True
    )

    class Meta:
        db_table = "image"

app/views.pyにViewを定義します。

Vue.js側は後程実装します。

from rest_framework import viewsets, status
from rest_framework.response import Response
from .models import Image
from .serializers import ImageSerializer

class ImageViewSet(viewsets.ModelViewSet):
    queryset = Image.objects.all()
    serializer_class = ImageSerializer

app/serializers.pyを作成し、ImageSerializerを定義します。

from rest_framework import serializers
from .models import Image

class ImageSerializer(serializers.ModelSerializer):

    class Meta:
        model = Image
        fields = "__all__"

project/urls.pyを編集

from django.contrib import admin
from django.conf import settings
from django.urls import path, include
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('app.urls')) # appのurls.pyを指定
]

if settings.DEBUG:
    # 画像表示用
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)

app/urls.pyを作成します。

from django.urls import path, include

from rest_framework.routers import DefaultRouter
from app.views import ImageViewSet

# ImageViewSetを設定
router = DefaultRouter()
router.register(r'image', ImageViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

マイグレーションします。

% python manage.py makemigrations
% python manage.py migrate

開発サーバーを起動し、http://127.0.0.1:8000/api/v1/imageを開きます。

Screen Shot 2022-12-18 at 13.53.21.png
試しにChoose Fileに画像を設定し、POSTボタンで画像登録ができるか確認します。

Screen Shot 2022-12-18 at 14.05.44.png
URLにアクセスしてみます。

Screen Shot 2022-12-18 at 14.06.58.png
登録できました!

Vue.jsプロジェクトを生成

本プロジェクトをVueプロジェクトを生成します。

% vue create frontend

Vue3を選択します。

Vue CLI v5.0.1
┌─────────────────────────────────────────┐
                                         
   New version available 5.0.1  5.0.8   
                                         
└─────────────────────────────────────────┘

? Please pick a preset: (Use arrow keys)
 Default ([Vue 3] babel, eslint) 
  Default ([Vue 2] babel, eslint) 
  Manually select features

メッセージが表示されればOK。

Vue CLI v5.0.1
  Creating project in /Users/ryosukemaeda/Programming/Issues/django-vue-env/frontend.
⚙️  Installing CLI plugins. This might take a while...

added 848 packages in 12s
🚀  Invoking generators...
📦  Installing additional dependencies...

added 96 packages in 3s
  Running completion hooks...

📄  Generating README.md...

🎉  Successfully created project frontend.
👉  Get started with the following commands:

 $ cd frontend
 $ npm run serve

開発サーバーを起動します。

% cd frontend
frontend% npm run serve

Vue.jsのデフォルト画面が表示されればOKです。

aaa.png

フロントエンドを実装

API実行のため、axiosをインストールします。

frontend% npm install axios

frontend/src/common/api.service.jsを作成し、以下を設定します。

import axios from "axios";

axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.withCredentials = true;

export { axios };

frontend/src/components/RegisterImage.vueを作成します。

<template>
  <div>
    <form @submit.prevent="onSubmit">
      <div>画像</div>
      <div>
        <input
          type="file"
          name=""
          id="img-upload"
          ref="file"
          multiple="multiple"
          @change="uploadFile"
          @click="
            (e) => {
              e.target.value = '';
            }
          "
        />
      </div>
      <button>登録</button>
    </form>
    <!-- プレビュー表示 -->
    <img :src="imgUrl" />
  </div>
</template>

<script>
import { axios } from "@/common/api.service.js";

export default {
  data() {
    return {
      imgData: null,
      imgUrl: null,
    };
  },
  methods: {
    uploadFile() {
      this.imgData = this.$refs.file.files[0];
      if (!this.imgData) {
        return;
      }
    },
    async onSubmit() {
      const formData = new FormData();
      formData.append("imageData", this.imgData);
      console.log(this.imgData);
      try {
        const endpoint = "/api/v1/image/";
        const response = await axios.post(endpoint, formData, {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        });
        this.imgUrl = `http://localhost:8000${response.data}`;
        alert("Success!");
      } catch (error) {
        console.log(error);
        alert(error.response.data);
      }
      this.imgData = null;
    },
  },
};
</script>

※本来であれば、画像ファイルのみ受付できるよう実装すべきですが、今回は省略します。

frontend/vue.config.jsを編集します。

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  publicPath: "/",
  devServer: {
    host: "localhost",
    hot: "only",
    proxy: {
      "^/api": {
        target: "http://localhost:8000",
        changeOrigin: true,
      },
    },
  },
})

frontend/src/App.vueを編集し、RegisterImageコンポーネントを表示するようにします。

<template>
  <div>
    <!-- <img alt="Vue logo" src="./assets/logo.png" /> -->
    <!-- <HelloWorld msg="test" /> -->
    <RegisterImage />
  </div>
</template>

<script>
// import HelloWorld from "./components/HelloWorld.vue";
import RegisterImage from './components/RegisterImage.vue'

export default {
  name: "App",
  components: {
    // HelloWorld,
    RegisterImage
  },
};
</script>

...

ここで、バックエンドのソースを一部編集します。

app/views.pyを編集し、フロントエンドからの登録リクエストにより、画像データを登録するよう修正します。

from rest_framework import viewsets, status
from rest_framework.response import Response
from .models import Image
from .serializers import ImageSerializer

class ImageViewSet(viewsets.ModelViewSet):
    queryset = Image.objects.all()
    serializer_class = ImageSerializer

		# 以下を追加します
    def create(self, request, *args, **kwargs):
	      # 画像登録処理
        img_data = request.data['imageData']
        if img_data is not None:
            img = Image.objects.create(image=img_data)
            return Response(img.image.url,
                            status=status.HTTP_200_OK)

        return Response("Failed to register image.",
                        status=status.HTTP_400_BAD_REQUEST)

画像登録を実施

ここまでできたら、実際に登録処理を試してみましょう。

DjangoとVue.jsの開発用サーバーを両方起動します。

(venv)% python manage.py runserver
% cd frontend
% npm run server

ブラウザでhttp://localhost:8080を開き、以下の画面が開くことを確認します。

Screen Shot 2022-12-18 at 15.20.42.png
ファイルをセットし、登録ボタンを押下します。

Screen Shot 2022-12-18 at 15.24.28.png
成功しました。

アラートのOKを押下すると、アップロードしたプレビューが表示されることを確認します。
Screen Shot 2022-12-18 at 15.24.40.png

以上になります。
ありがとうございました。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6