Django5 与 Vue3 表单交互全解析:从基础到实战
在前后端分离开发日益普及的今天,Django 作为成熟的后端框架,Vue3 作为灵活高效的前端框架,二者的结合成为开发 Web 应用的热门选择。本文将基于专业课件内容,从 Django5 表单基础操作、接收 Vue3 表单数据、数据库交互,到综合案例实战,全方位解析二者的表单交互逻辑,附带完整代码示例与详细说明,助力开发者快速上手。
一、Django5 表单基础:从 Form 到 ModelForm
Django 提供了两种核心表单类 ——Form
(无模型关联)和ModelForm
(与模型绑定),可快速实现表单创建、验证与数据处理。
1.1 Form 类:处理无模型关联的表单
当表单无需与数据库模型直接绑定(如登录表单、留言表单)时,使用Form
类最为合适。以下以 “留言表单” 为例,展示完整实现流程。
步骤 1:定义 Form 类(forms.py)
在 Django 应用目录下创建forms.py
,定义表单字段与验证规则:
# forms.py
from django import formsclass ContactForm(forms.Form):# 姓名:最大长度100字符,自定义标签name = forms.CharField(label='姓名', max_length=100)# 邮箱:自动验证邮箱格式email = forms.EmailField(label='邮箱')# 留言:使用文本域(Textarea)组件message = forms.CharField(label='留言', widget=forms.Textarea)
步骤 2:视图处理表单逻辑(views.py)
在视图中判断请求方法,处理表单提交与验证:
# views.py
from django.shortcuts import render
from .forms import ContactFormdef contact(request):# 1. 处理POST请求(表单提交)if request.method == 'POST':# 将POST数据传入Form类,创建表单实例form = ContactForm(request.POST)# 验证表单数据是否合法if form.is_valid():# 从验证后的字典中获取数据(cleaned_data)name = form.cleaned_data['name']email = form.cleaned_data['email']message = form.cleaned_data['message']# 此处可添加业务逻辑(如发送邮件、存储到数据库)print(f"收到留言:{name}({email}):{message}")# 跳转至成功页面return render(request, 'contact_success.html')# 2. 处理GET请求(首次访问表单页面)else:form = ContactForm() # 创建空表单实例# 渲染表单页面,传递表单实例到模板return render(request, 'contact.html', {'form': form})
步骤 3:模板渲染表单(templates/contact.html)
使用 Django 模板语法快速渲染表单,无需手动编写 HTML 输入框:
<!-- contact.html -->
<form method="post"><!-- 必加:Django CSRF 防护令牌 -->{% csrf_token %}<!-- 以段落(<p>)形式渲染所有表单字段 -->{{ form.as_p }}<button type="submit">提交留言</button>
</form>
form.as_p
会自动为每个字段生成<p>
标签包裹的 HTML(包含标签、输入框、错误提示),也可使用form.as_table
(表格形式)或form.as_ul
(列表形式)。
1.2 ModelForm 类:绑定数据库模型
当表单需要直接操作数据库(如创建文章、添加商品)时,ModelForm
可自动生成表单字段,并简化数据存储逻辑(无需手动调用save()
以外的方法)。以下以 “文章发布” 为例实现。
步骤 1:定义数据库模型(models.py)
首先创建与表单关联的模型:
# models.py
from django.db import modelsclass Article(models.Model):title = models.CharField(max_length=200, verbose_name='文章标题')content = models.TextField(verbose_name='文章内容')pub_date = models.DateTimeField(auto_now_add=True, verbose_name='发布时间')# auto_now_add=True:创建时自动填充当前时间class Meta:verbose_name = '文章'verbose_name_plural = '文章' # 复数形式(避免后台显示为“article”)
步骤 2:定义 ModelForm 类(forms.py)
通过Meta
类指定关联的模型与字段:
# forms.py
from django import forms
from .models import Articleclass ArticleForm(forms.ModelForm):class Meta:model = Article # 关联的模型fields = ['title', 'content'] # 表单需包含的模型字段labels = { # 自定义表单字段标签(替代模型的verbose_name)'title': '标题','content': '内容'}# 可选:自定义字段组件(如为content添加样式)widgets = {'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10})}
步骤 3:视图处理 ModelForm(views.py)
ModelForm
的save()
方法可直接将验证后的数据存储到数据库:
# views.py
from django.shortcuts import render, redirect
from .forms import ArticleFormdef create_article(request):if request.method == 'POST':form = ArticleForm(request.POST)if form.is_valid():# 直接保存到数据库(无需手动创建模型实例)form.save()# 跳转至文章列表页(需提前定义article_list视图与URL)return redirect('article_list')else:form = ArticleForm()return render(request, 'create_article.html', {'form': form})
1.3 表单验证:内置与自定义结合
Django 表单验证分为 “内置验证器”(快速实现基础规则)和 “自定义验证方法”(处理复杂业务逻辑),确保数据合法性。
1.3.1 内置验证器
直接在表单字段中添加validators
参数,示例:
# forms.py
from django import forms
from django.core.validators import MinLengthValidator, MaxLengthValidatorclass UserForm(forms.Form):username = forms.CharField(label='用户名',# 验证规则:长度5-20字符validators=[MinLengthValidator(5, message='用户名长度不能少于5个字符'),MaxLengthValidator(20, message='用户名长度不能超过20个字符')])password = forms.CharField(label='密码',widget=forms.PasswordInput, # 密码隐藏输入validators=[MinLengthValidator(8, message='密码长度不能少于8个字符')])
1.3.2 自定义验证方法
当内置验证器无法满足需求(如 “两次密码一致”“用户名不重复”)时,需定义自定义验证方法:
# forms.py
from django import forms
from django.core.exceptions import ValidationError
from .models import User # 假设存在User模型class RegisterForm(forms.Form):username = forms.CharField(label='用户名')email = forms.EmailField(label='邮箱')password1 = forms.CharField(label='密码', widget=forms.PasswordInput)password2 = forms.CharField(label='确认密码', widget=forms.PasswordInput)# 1. 单字段验证:方法名格式为“clean_字段名”def clean_username(self):username = self.cleaned_data.get('username')# 检查用户名是否已存在(查询数据库)if User.objects.filter(username=username).exists():raise ValidationError('用户名已存在') # 抛出验证错误return username # 必须返回验证后的字段值# 2. 多字段验证:重写clean()方法def clean(self):# 调用父类clean()获取验证后的所有数据cleaned_data = super().clean()password1 = cleaned_data.get('password1')password2 = cleaned_data.get('password2')# 验证两次密码是否一致if password1 and password2 and password1 != password2:raise ValidationError('两次输入的密码不一致') # 全局错误提示return cleaned_data
二、Django5 接收 Vue3 表单数据:axios 与 JSON 交互
前后端分离架构中,Vue3 通过axios
发送表单数据,Django5 需接收 JSON 或表单格式数据,并存储到 MySQL 数据库。
2.1 Vue3 前端:用 axios 发送数据
Vue3 中使用axios
发送 POST 请求,需注意数据格式(表单格式或 JSON 格式)与 CSRF 防护。
示例:Vue3 文章提交表单
<!-- ArticleForm.vue -->
<template><div class="form-container"><h2>添加文章</h2><!-- 阻止默认表单提交,用自定义方法处理 --><form @submit.prevent="submitForm"><div class="form-group"><label for="title">标题</label><!-- v-model绑定表单数据 --><input type="text" id="title" v-model="formData.title" requiredclass="form-control"></div><div class="form-group"><label for="content">内容</label><textarea id="content" v-model="formData.content" requiredclass="form-control"rows="8"></textarea></div><button type="submit" class="btn btn-primary">提交</button></form></div>
</template><script setup>
// 1. 导入依赖
import { ref } from 'vue';
import axios from 'axios';// 2. 定义表单数据(响应式)
const formData = ref({title: '', // 初始为空content: ''
});// 3. 提交表单方法(异步)
const submitForm = async () => {try {// 发送POST请求:两种数据格式可选const response = await axios.post('/api/articles/', // Django后端接口URLformData.value, // 发送的数据(默认JSON格式)// 可选:若需发送表单格式数据,添加以下配置// {// headers: { 'Content-Type': 'application/x-www-form-urlencoded' },// transformRequest: [(data) => Object.entries(data).join('&')]// });// 提交成功后的处理(如提示、跳转)alert('文章创建成功!');console.log('响应数据:', response.data);// 跳转至文章列表页(需导入useRouter)// router.push('/articles');} catch (error) {// 捕获错误(如后端验证失败)alert('提交失败:' + (error.response?.data?.message || '未知错误'));console.error('错误详情:', error);}
};
</script><style scoped>
.form-container { max-width: 800px; margin: 20px auto; padding: 0 20px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
</style>
2.2 Django5 后端:接收不同格式数据
Django 接收数据的方式取决于 Vue3 发送的格式(JSON 或表单格式),以下分两种场景实现。
2.2.1 接收 JSON 格式数据(推荐)
当 Vue3 发送Content-Type: application/json
时,Django 需通过request.body
解析 JSON 数据:
# views.py
import json
from django.http import JsonResponse
from .models import Articledef create_article_api(request):# 仅允许POST请求if request.method != 'POST':return JsonResponse({'status': 'error','message': '仅支持POST请求'}, status=405) # 405:方法不允许try:# 解析JSON数据(request.body是字节流,需先解码)data = json.loads(request.body.decode('utf-8'))# 提取字段并验证title = data.get('title')content = data.get('content')if not title or not content:return JsonResponse({'status': 'error','message': '标题和内容不能为空'}, status=400) # 400:请求参数错误# 存储到数据库article = Article.objects.create(title=title, content=content)# 返回成功响应(包含新创建文章的信息)return JsonResponse({'status': 'success','message': '文章创建成功','data': {'id': article.id,'title': article.title,'pub_date': article.pub_date.strftime('%Y-%m-%d %H:%M:%S') # 格式化时间}}, status=201) # 201:创建成功# 捕获JSON解析错误except json.JSONDecodeError:return JsonResponse({'status': 'error','message': '数据格式错误(需JSON格式)'}, status=400)# 捕获其他异常(如数据库错误)except Exception as e:return JsonResponse({'status': 'error','message': f'服务器错误:{str(e)}'}, status=500) # 500:服务器内部错误
2.2.2 接收表单格式数据
当 Vue3 发送Content-Type: application/x-www-form-urlencoded
时,Django 可直接通过request.POST
获取数据:
# views.py(表单格式版)
from django.http import JsonResponse
from .models import Articledef create_article_api(request):if request.method != 'POST':return JsonResponse({'status': 'error', 'message': '仅支持POST请求'}, status=405)# 从request.POST获取表单数据title = request.POST.get('title')content = request.POST.get('content')if not title or not content:return JsonResponse({'status': 'error', 'message': '标题和内容不能为空'}, status=400)try:article = Article.objects.create(title=title, content=content)return JsonResponse({'status': 'success','message': '文章创建成功','data': {'id': article.id, 'title': article.title}}, status=201)except Exception as e:return JsonResponse({'status': 'error', 'message': str(e)}, status=500)
三、Django5 查库与 Vue3 渲染:JSON 数据响应
Django 从数据库查询数据后,以 JSON 格式响应给 Vue3,Vue3 接收数据并渲染到页面。
3.1 Django5 后端:查询数据并返回 JSON
3.1.1 原生 Django 实现(无 DRF):
# views.py
from django.http import JsonResponse
from django.utils import timezone
from datetime import timedelta
from .models import Articledef get_articles(request):# 1. 数据库查询(示例:获取近7天的文章,按发布时间倒序)seven_days_ago = timezone.now() - timedelta(days=7)articles = Article.objects.filter(pub_date__gte=seven_days_ago # 发布时间≥7天前).order_by('-pub_date') # 按发布时间倒序(最新的在前)# 2. 转换数据格式(QuerySet→列表字典,便于JSON序列化)articles_list = []for article in articles:articles_list.append({'id': article.id,'title': article.title,'content': article.content[:100] + '...' if len(article.content) > 100 else article.content, # 内容截取'pub_date': article.pub_date.strftime('%Y-%m-%d %H:%M')})# 3. 返回JSON响应return JsonResponse({'status': 'success','data': articles_list,'count': len(articles_list) # 附加数据总数})
3.1.2 DRF 实现(自动序列化)
若已使用 DRF,ListAPIView
可自动查询并序列化数据:
# views.py
from rest_framework import generics
from .models import Article
from .serializers import ArticleSerializerclass ArticleListView(generics.ListAPIView):# 自定义查询逻辑(如仅返回近7天的文章)def get_queryset(self):seven_days_ago = timezone.now() - timedelta(days=7)return Article.objects.filter(pub_date__gte=seven_days_ago).order_by('-pub_date')serializer_class = ArticleSerializer # 自动序列化
3.2 Vue3 前端:接收并渲染数据
Vue3 通过axios
获取 Django 响应的 JSON 数据,结合v-for
渲染列表,并处理加载状态与错误。
示例:Vue3 文章列表组件
<!-- ArticleList.vue -->
<template><div class="articles-container"><h2>文章列表(近7天)</h2><!-- 加载中状态 --><div class="alert alert-info" v-if="loading">加载中...</div><!-- 错误状态 --><div class="alert alert-danger" v-else-if="error">{{ error }}</div><!-- 数据渲染 --><div v-else><p class="text-muted">共 {{ articles.length }} 篇文章</p><div class="card" v-for="article in articles" :key="article.id"><div class="card-body"><h5 class="card-title">{{ article.title }}</h5><p class="card-text">{{ article.content }}</p><p class="card-subtitle text-muted">{{ article.pub_date }}</p></div></div></div></div>
</template><script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';// 响应式数据
const articles = ref([]); // 文章列表
const loading = ref(false); // 加载状态
const error = ref(''); // 错误信息// 加载文章列表的函数
const fetchArticles = async () => {loading.value = true; // 开始加载error.value = ''; // 清空错误try {// 调用Django接口const response = await axios.get('/api/articles/');// 适配数据格式(原生Django响应 vs DRF响应)articles.value = response.data.data || response.data.results;} catch (err) {error.value = '获取文章失败:' + (err.response?.data?.message || '网络错误');console.error('错误详情:', err);} finally {loading.value = false; // 结束加载}
};// 组件挂载时加载数据
onMounted(() => {fetchArticles();
});
</script><style scoped>
.articles-container { max-width: 1000px; margin: 20px auto; padding: 0 20px; }
.card { margin-bottom: 15px; }
</style>
四、总结
本文从 Django5 表单基础出发,逐步深入到与 Vue3 的表单交互、数据库操作,最终通过 “产品管理系统” 案例实现了完整的前后端协同流程。核心要点总结如下:
- Django 表单:
Form
处理无模型关联场景,ModelForm
绑定模型简化存储; - 数据交互:Vue3 用
axios
发送 JSON / 表单数据,Django 用request.body
或 DRF 接收; - 数据库:配置 MySQL 后,通过 Django ORM 或 DRF 自动处理数据存储与查询;