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
相关产品推荐
相关产品推荐

