You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js无法向Django Rest Framework提交数据求助

问题描述

我使用Vue.js、Django及Django Rest Framework开发了一个Web应用,仅能通过后端提交数据,使用Vue.js表单填写数据时无法完成提交。已按照教程尝试两次,怀疑教程代码存在问题,附上相关代码寻求技术帮助。

相关代码

Tasks.vue

<template>
  <div class="tasks_container">
    <div class="add_task">
      <form v-on:submit.prevent="submitForm">
        <div class="form-group">
          <label for="title">Title</label>
          <input type="text" class="form-control" id="title" v-model="title" />
        </div>
        <div class="form-group">
          <label for="description">Description</label>
          <textarea
            class="form-control"
            id="description"
            v-model="description"
          ></textarea>
        </div>
        <div class="form-group">
          <button type="submit">Add Task</button>
        </div>
      </form>
    </div>
    <div class="tasks_content">
      <h1>Tasks</h1>
      <ul class="tasks_list">
        <li v-for="task in tasks" :key="task.id">
          <h2>{{ task.title }}</h2>
          <p>{{ task.description }}</p>
          <button @click="toggleTask(task)">
            {{ task.completed ? "Undo" : "Complete" }}
          </button>
          <button @click="deleteTask(task)">Delete</button>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tasks: [],
      title: "",
      description: "",
    };
  },
  methods: {
    async getData() {
      try {
        // fetch tasks
        const response = await this.$http.get(
          "http://localhost:8000/api/tasks/"
        );
        // set the data returned as tasks
        this.tasks = response.data;
      } catch (error) {
        // log the error
        console.log(error);
      }
    },
  },
  async submitForm() {
    try {
      // Send a POST request to the API
      const response = await this.$http.post(
        "http://localhost:8000/api/tasks/",
        {
          title: this.title,
          description: this.description,
          completed: false,
        }
      );
      // Append the returned data to the tasks array
      this.tasks.push(response.data);
      // Reset the title and description field values.
      this.title = "";
      this.description = "";
    } catch (error) {
      // Log the error
      console.log(error);
    }
  },
  async toggleTask(task) {
    try {
      // Send a request to API to update the task
      const response = await this.$http.put(
        `http://localhost:8000/api/tasks/${task.id}/`,
        {
          completed: !task.completed,
          title: task.title,
          description: task.description,
        }
      );

      // Get the index of the task being updated
      let taskIndex = this.tasks.findIndex((t) => t.id === task.id);

      // Reset the tasks array with the new data of the updated task

      this.tasks = this.tasks.map((task) => {
        if (this.tasks.findIndex((t) => t.id === task.id) === taskIndex) {
          return response.data;
        }
        return task;
      });
    } catch (error) {
      // Log any error
      console.log(error);
    }
  },
  async deleteTask(task) {
    // Confirm if one wants to delete the task
    let confirmation = confirm("Do you want to delete this task?");

    if (confirmation) {
      try {
        // Send a request to delete the task
        await this.$http.delete(`http://localhost:8000/api/tasks/${task.id}`);

        // Refresh the tasks
        this.getData();
      } catch (error) {
        // Log any error

        console.log(error);
      }
    }
  },
  created() {
    // Fetch tasks on page load
    this.getData();
  },
};
</script>

App.vue

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <Tasks />
  </div>
</template>

<script>
import Tasks from "./components/Tasks.vue";
export default {
  name: "App",
  components: {
    Tasks,
  },
};
</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>

main.js

import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'

// set a prototype for http
Vue.prototype.$http = axios;
Vue.config.productionTip = false

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

views.py(注:原代码文件名拼写为vies.py,应为views.py)

# parsing data from the client
from rest_framework.parsers import JSONParser
# To bypass having a CSRF token
from django.views.decorators.csrf import csrf_exempt
# for sending response to the client
from django.http import HttpResponse, JsonResponse
# API definition for task
from .serializers import TaskSerializer
# Task model
from .models import Task


@csrf_exempt
def tasks(request):
    '''
    List all task snippets
    '''
    if(request.method == 'GET'):
        # get all the tasks
        tasks = Task.objects.all()
        # serialize the task data
        serializer = TaskSerializer(tasks, many=True)
        # return a Json response
        return JsonResponse(serializer.data,safe=False)
    elif(request.method == 'POST'):
        # parse the incoming information
        data = JSONParser().parse(request)
        # instanciate with the serializer
        serializer = TaskSerializer(data=data)
        # check if the sent information is okay
        if(serializer.is_valid()):
            # if okay, save it on the database
            serializer.save()
            # provide a Json Response with the data that was saved
            return JsonResponse(serializer.data, status=201)
            # provide a Json Response with the necessary error information
        return JsonResponse(serializer.errors, status=400)


@csrf_exempt
def task_detail(request, pk):
    try:
        # obtain the task with the passed id.
        task = Task.objects.get(pk=pk)
    except:
        # respond with a 404 error message
        return HttpResponse(status=404)  
    if(request.method == 'PUT'):
        # parse the incoming information
        data = JSONParser().parse(request)  
        # instanciate with the serializer
        serializer = TaskSerializer(task, data=data)
        # check whether the sent information is okay
        if(serializer.is_valid()):  
            # if okay, save it on the database
            serializer.save() 
            # provide a JSON response with the data that was submitted
            return JsonResponse(serializer.data, status=201)
        # provide a JSON response with the necessary error information
        return JsonResponse(serializer.errors, status=400)
    elif(request.method == 'DELETE'):
        # delete the task
        task.delete() 
        # return a no content response.
        return HttpResponse(status=204) 

serializers.py

from rest_framework import routers,serializers,viewsets
from .models import Task
class TaskSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Task
        fields = ['id', 'title', 'description', 'completed', 'created_at']
问题排查与修复方案

1. Vue组件方法定义错误(核心问题)

原代码中submitForm、toggleTask、deleteTask三个方法未放在methods对象内部,而是作为组件顶级属性存在,导致Vue无法识别这些方法,表单提交时无法触发提交逻辑。

修复后的Tasks.vue脚本核心部分:

<script>
export default {
  data() {
    return {
      tasks: [],
      title: "",
      description: "",
    };
  },
  methods: {
    async getData() {
      try {
        const response = await this.$http.get("http://localhost:8000/api/tasks/");
        this.tasks = response.data;
      } catch (error) {
        console.log(error);
      }
    },
    async submitForm() {
      try {
        const response = await this.$http.post("http://localhost:8000/api/tasks/", {
          title: this.title,
          description: this.description,
          completed: false,
        });
        this.tasks.push(response.data);
        this.title = "";
        this.description = "";
      } catch (error) {
        console.log(error);
      }
    },
    async toggleTask(task) {
      try {
        const response = await this.$http.put(
          `http://localhost:8000/api/tasks/${task.id}/`,
          {
            completed: !task.completed,
            title: task.title,
            description: task.description,
          }
        );
        let taskIndex = this.tasks.findIndex((t) => t.id === task.id);
        this.tasks = this.tasks.map((t, index) => 
          index === taskIndex ? response.data : t
        );
      } catch (error) {
        console.log(error);
      }
    },
    async deleteTask(task) {
      let confirmation = confirm("Do you want to delete this task?");
      if (confirmation) {
        try {
          await this.$http.delete(`http://localhost:8000/api/tasks/${task.id}`);
          this.getData();
        } catch (error) {
          console.log(error);
        }
      }
    },
  },
  created() {
    this.getData();
  },
};
</script>

2. 跨域资源共享(CORS)问题

Vue前端(通常端口8080)与Django后端(端口8000)属于不同域名,浏览器会拦截跨域请求,需在Django中配置CORS:

  • 安装依赖:
pip install django-cors-headers
  • 修改项目settings.py:
INSTALLED_APPS = [
    # ...其他应用
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # 置于最前
    'django.middleware.common.CommonMiddleware',
    # ...其他中间件
]

# 开发环境允许所有来源,生产环境需指定具体域名
CORS_ALLOW_ALL_ORIGINS = True

3. 序列化器类型不匹配

HyperlinkedModelSerializer需处理URL字段,当前场景无相关路由配置,改用ModelSerializer更适配:
修改serializers.py:

from rest_framework import serializers
from .models import Task

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = ['id', 'title', 'description', 'completed', 'created_at']

4. DRF视图优化(可选)

用DRF的APIView替代函数视图,更符合框架规范,自动处理请求解析与响应:
修改views.py:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from .models import Task
from .serializers import TaskSerializer
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

@method_decorator(csrf_exempt, name='dispatch')
class TaskList(APIView):
    def get(self, request):
        tasks = Task.objects.all()
        serializer = TaskSerializer(tasks, many=True)
        return Response(serializer.data)
    
    def post(self, request):
        serializer = TaskSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@method_decorator(csrf_exempt, name='dispatch')
class TaskDetail(APIView):
    def get_object(self, pk):
        return get_object_or_404(Task, pk=pk)
    
    def put(self, request, pk):
        task = self.get_object(pk)
        serializer = TaskSerializer(task, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    def delete(self, request, pk):
        task = self.get_object(pk)
        task.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

更新urls.py:

from django.urls import path
from .views import TaskList, TaskDetail

urlpatterns = [
    path('api/tasks/', TaskList.as_view()),
    path('api/tasks/<int:pk>/', TaskDetail.as_view()),
]

内容的提问来源于stack exchange,提问作者robert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 23:00:42