使用Vue.js和Django REST Framework来实现图像的注册和显示处理

总结

使用Vue.js和Django REST Framework来注册和展示图片。

GitHub链接 (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

修改项目/ 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文件中定义视图。

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
Screen Shot 2022-12-18 at 14.05.44.png
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

如果消息显示出来,就可以了。

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的默认页面就可以了。

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
Screen Shot 2022-12-18 at 15.24.40.png

以上就是了。
非常感谢。

bannerAds