Django5 与 Vue3 表单交互(上):后端表单构建与前后端数据传递
Django 的学习,本质上是为了搭建稳定、高效的后端服务,而 Vue3 则是构建灵活、流畅前端界面的利器。当我们将 Django5 的后端能力与 Vue3 的前端优势结合,实现二者间的表单数据交互,就能真正打通"前端输入-后端处理-数据库存储-前端渲染"的完整链路。在前后端交互的流程中,"后端表单构建"与"数据传递"是基础且核心的第一步------前者确保后端能合规接收数据,后者实现前后端数据的顺畅流转。本文作为系列第一篇,将聚焦这两大模块,拆解关键技术细节。
一、Django5 表单:后端数据验证与处理的基石
在前后端分离架构中,Django 表单承担着"数据守门人"的核心角色:它不仅能快速生成表单结构,更能对前端传入的数据进行严格校验,过滤非法值后再传递给后续的数据库存储环节。Django 提供两种核心表单类型,分别适配"非数据库关联"和"数据库关联"场景,覆盖绝大多数业务需求。
1.1 Form 类:非模型关联的灵活表单方案
当表单无需直接操作数据库(如用户登录表单、留言表单)时,Form 类是最优选择------它允许开发者完全自定义字段类型、验证规则,灵活掌控表单逻辑。其使用流程可拆解为"定义表单-处理提交-模板渲染"三步,每一步都有明确的规范和便捷工具。
(1)定义表单结构(forms.py)
首先在 Django 应用的 forms.py
文件中,通过继承 forms.Form
类定义表单字段。需指定字段类型(如字符型、邮箱型)、显示标签(label
)及基础约束(如最大长度),Django 会自动根据字段类型生成基础验证逻辑(如邮箱格式校验)。
示例:留言表单定义
python
# forms.py
from django import forms
class ContactForm(forms.Form):
# 姓名字段:最大长度100字符,前端显示"姓名"
name = forms.CharField(label='姓名', max_length=100)
# 邮箱字段:自动验证邮箱格式(如@符号、域名后缀),前端显示"邮箱"
email = forms.EmailField(label='邮箱')
# 留言字段:多行文本输入(通过widget指定),前端显示"留言"
message = forms.CharField(label='留言', widget=forms.Textarea)
(2)处理表单提交(views.py)
表单的提交逻辑需在视图函数中实现,核心是判断请求方法(GET/POST),并完成"数据接收-验证-业务处理"的流程。GET 请求用于返回空表单供前端渲染,POST 请求用于接收用户提交的数据并验证。
示例:留言表单视图处理
python
# views.py
from django.shortcuts import render
from .forms import ContactForm
def contact(request):
# 1. 处理POST请求(用户提交表单)
if request.method == 'POST':
# 用前端传入的POST数据初始化表单实例
form = ContactForm(request.POST)
# 验证数据是否合规(自动触发字段类型、长度等基础校验)
if form.is_valid():
# 提取验证后的"干净数据"(已过滤非法值,如HTML标签)
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()
# 3. 将表单实例传递给模板,供前端渲染
return render(request, 'contact.html', {'form': form})
(3)模板渲染表单(HTML)
Django 提供便捷的模板标签,无需手动编写大量 HTML 输入框代码。通过 {``{ form.as_p }}
可将表单字段以段落(<p>
)形式渲染,自动包含标签(<label>
)和输入框(<input>
/<textarea>
);同时必须添加 {% csrf_token %}
标签,这是 Django 防范跨站请求伪造(CSRF)攻击的关键,缺失会导致表单提交失败。
示例:留言表单模板
html
<!-- contact.html -->
<form method="post" action="{% url 'contact' %}">
{% csrf_token %} <!-- CSRF防护必备 -->
{{ form.as_p }} <!-- 自动渲染所有表单字段(段落形式) -->
<button type="submit" class="btn">提交留言</button>
</form>
1.2 ModelForm 类:与数据库模型无缝对接的高效方案
若表单需直接操作数据库(如文章发布、用户注册),ModelForm 类能极大简化开发------它会根据数据库模型(Model)自动生成表单字段,甚至支持"一键保存数据到数据库",无需手动编写 create
逻辑,大幅减少重复代码。
(1)定义数据库模型(models.py)
首先需在 models.py
中定义对应的数据库模型,明确字段类型和约束,这是 ModelForm 类的"数据基础"。
示例:文章模型定义
python
# models.py
from django.db import models
class Article(models.Model):
# 文章标题:最大长度200字符
title = models.CharField(max_length=200)
# 文章内容:长文本类型(无长度限制)
content = models.TextField()
# 发布时间:自动填充当前时间(创建时生效,后续不更新)
pub_date = models.DateTimeField(auto_now_add=True)
# 可选:定义模型的字符串显示(便于后台管理和调试)
def __str__(self):
return self.title
(2)定义 ModelForm 表单(forms.py)
通过继承 forms.ModelForm
类,并在内部 Meta
类中指定关联的模型(model
)和需生成的表单字段(fields
),即可完成表单定义。无需重复声明字段类型------ModelForm 会自动根据模型字段类型映射为表单字段(如模型的 CharField
对应表单的 CharField
)。
示例:文章发布表单定义
python
# forms.py
from django import forms
from .models import Article # 导入关联的数据库模型
class ArticleForm(forms.ModelForm):
class Meta:
model = Article # 关联的数据库模型
fields = ['title', 'content'] # 需生成的表单字段(排除自动填充的pub_date)
labels = { # 自定义前端显示的字段名称(优化用户体验)
'title': '文章标题',
'content': '文章内容'
}
(3)处理表单提交与数据存储(views.py)
ModelForm 的核心优势体现在数据存储环节:当 form.is_valid()
验证通过后,直接调用 form.save()
即可将表单数据自动存入数据库,无需手动创建模型实例并调用 save()
方法。
示例:文章发布视图处理
python
# views.py
from django.shortcuts import render, redirect
from .forms import ArticleForm # 导入ModelForm表单
from .models import Article
def create_article(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
# 一键保存数据到数据库(自动创建Article实例并存储)
form.save()
# 跳转至文章列表页,避免刷新重复提交
return redirect('article_list') # 需提前定义article_list路由
else:
form = ArticleForm()
return render(request, 'create_article.html', {'form': form})
1.3 表单验证:确保数据合规的双重机制
无论使用 Form 还是 ModelForm,Django 都提供了强大的验证机制,覆盖"基础规则"和"复杂业务规则",确保只有合规数据能进入后续流程。
(1)内置验证器:快速实现基础规则
Django 内置了多种开箱即用的验证器,如 MinLengthValidator
(最小长度)、MaxLengthValidator
(最大长度)、EmailValidator
(邮箱格式)等,直接在字段定义中通过 validators
参数引用即可,无需手动编写校验逻辑。
示例:用户表单内置验证
python
# forms.py
from django import forms
from django.core.validators import MinLengthValidator, MaxLengthValidator
class 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, # 密码隐藏输入(优化安全性)
# 添加"最小8字符"的验证
validators=[MinLengthValidator(8, message='密码长度不能少于8个字符')]
)
(2)自定义验证方法:应对复杂业务规则
当内置验证器无法满足需求(如"两次密码一致""用户名不重复")时,可在表单类中定义自定义验证方法。分为两种场景:"单字段验证"(针对单个字段的规则)和"全局验证"(针对多个字段的关联规则)。
示例:注册表单自定义验证
python
# 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')
# 校验两次密码是否一致(需判断两者均不为空,避免None值比较)
if password1 and password2 and password1 != password2:
raise ValidationError('两次输入的密码不一致,请重新输入')
二、Django5 接收 Vue3 表单数据:前后端数据传递的关键链路
在前后端分离架构中,Vue3 无法直接操作数据库,需通过 axios 库 发送 HTTP 请求,将表单数据传递给 Django 后端。根据数据格式(表单格式/JSON 格式)和项目复杂度,Django 提供三种接收方案,分别适配不同场景。
2.1 Vue3 前端:用 axios 发送表单数据
axios 是 Vue3 生态中最常用的 HTTP 客户端,支持异步请求、拦截器等功能,能轻松实现"表单数据发送-响应处理"的流程。核心步骤是"绑定表单数据-阻止默认提交-发送请求-处理结果"。
(1)绑定表单数据与发送请求
通过 Vue3 的 ref
定义响应式表单数据(formData
),用 v-model
将输入框与 formData
绑定,确保用户输入实时同步;通过 @submit.prevent
阻止表单默认提交行为(避免页面刷新),转而调用自定义方法 submitForm
发送请求。
示例:Vue3 文章发布表单
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"
required <!-- 前端基础校验(非空) -->
placeholder="请输入文章标题"
>
</div>
<div class="form-group">
<label for="content">文章内容</label>
<textarea
id="content"
v-model="formData.content"
required
placeholder="请输入文章内容"
rows="6"
></textarea>
</div>
<button type="submit" class="btn-submit">提交文章</button>
</form>
</div>
</template>
<script setup>
// 1. 导入所需工具:ref(响应式数据)、axios(HTTP请求)
import { ref } from 'vue';
import axios from 'axios'; // 需提前安装:npm install axios
// 2. 定义响应式表单数据(初始值为空)
const formData = ref({
title: '',
content: ''
});
// 3. 表单提交方法(异步函数,处理HTTP请求)
const submitForm = async () => {
try {
// 发送POST请求到Django后端接口(需与Django路由对应)
const response = await axios.post('/api/articles/', formData.value);
// 请求成功后的处理:如提示用户、重置表单、跳转页面
alert('文章发布成功!');
console.log('后端返回数据:', response.data);
// 重置表单(清空输入)
formData.value = { title: '', content: '' };
// 可选:跳转至文章列表页(需导入useRouter)
// import { useRouter } from 'vue-router';
// const router = useRouter();
// router.push('/articles');
} catch (error) {
// 请求失败后的处理:如提示错误信息
alert('文章发布失败,请稍后重试!');
console.error('请求错误详情:', error);
}
};
</script>
<style scoped>
/* 简单样式优化,提升用户体验 */
.form-container { max-width: 800px; margin: 20px auto; padding: 0 20px; }
.form-group { margin-bottom: 20px; }
label { display: block; margin-bottom: 8px; font-weight: bold; }
input, textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
.btn-submit { padding: 10px 20px; background: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer; }
.btn-submit:hover { background: #0056b3; }
</style>
2.2 Django 后端:三种数据接收方案
Vue3 发送的数据格式不同,Django 的接收方式也不同。核心差异在于"表单格式"(application/x-www-form-urlencoded
)和"JSON 格式"(application/json
)的解析逻辑,以及是否使用 DRF(Django REST framework)简化开发。
(1)方案1:request.POST 接收表单格式数据
若 Vue3 发送的是传统表单格式数据(需手动设置 Content-Type: application/x-www-form-urlencoded
),Django 可直接通过 request.POST
获取字段值------这是最基础的接收方式,适用于简单场景。
示例:Django 接收表单格式数据
python
# views.py
from django.http import JsonResponse
from .models import Article
def create_article_api(request):
# 仅处理POST请求(数据提交)
if request.method == 'POST':
# 1. 从request.POST中提取字段值(get方法可避免字段不存在时报错)
title = request.POST.get('title', '') # 第二个参数为默认值(空字符串)
content = request.POST.get('content', '')
# 2. 基础数据校验(非空校验)
if not title.strip() or not content.strip():
# 返回JSON格式错误响应(前端可通过response.data获取信息)
return JsonResponse({
'status': 'error',
'message': '文章标题和内容不能为空,请补充完整'
}, status=400) # 400状态码表示"请求参数错误"
# 3. 数据存储到数据库
article = Article.objects.create(
title=title.strip(), # 去除前后空格(避免无效数据)
content=content.strip()
)
# 4. 返回JSON格式成功响应(包含新创建文章的关键信息)
return JsonResponse({
'status': 'success',
'message': '文章创建成功',
'data': {
'article_id': article.id,
'title': article.title,
'pub_date': article.pub_date.strftime('%Y-%m-%d %H:%M:%S') # 格式化时间
}
})
# 非POST请求(如GET):返回不支持的方法响应
return JsonResponse({
'status': 'error',
'message': '仅支持POST请求,请使用正确的请求方式'
}, status=405) # 405状态码表示"方法不允许"
(2)方案2:request.body 接收 JSON 格式数据
Vue3 用 axios 发送数据时,默认格式为 application/json
(无需手动设置 Content-Type
),此时 request.POST
无法获取数据,需通过 request.body
处理------request.body
返回二进制数据,需先通过 json.loads
解析为 Python 字典,再提取字段值。
示例:Django 接收 JSON 格式数据
python
# views.py
import json # 导入JSON解析模块
from django.http import JsonResponse
from .models import Article
def create_article_api(request):
if request.method == 'POST':
try:
# 1. 解析JSON数据(request.body是二进制,需先解码为字符串)
# 捕获JSONDecodeError:处理前端发送的JSON格式错误
data = json.loads(request.body.decode('utf-8'))
# 2. 提取字段值(与表单格式接收逻辑一致)
title = data.get('title', '').strip()
content = data.get('content', '').strip()
# 3. 基础数据校验
if not title or not content:
return JsonResponse({
'status': 'error',
'message': '文章标题和内容不能为空'
}, status=400)
# 4. 数据存储与成功响应
article = Article.objects.create(title=title, content=content)
return JsonResponse({
'status': 'success',
'message': '文章创建成功',
'data': {
'article_id': article.id,
'title': article.title
}
})
# 处理JSON格式错误(如前端发送的不是合法JSON)
except json.JSONDecodeError:
return JsonResponse({
'status': 'error',
'message': '数据格式错误,请发送合法的JSON数据'
}, status=400)
# 非POST请求响应
return JsonResponse({
'status': 'error',
'message': '仅支持POST请求'
}, status=405)
(3)方案3:Django REST framework(DRF)接收数据(复杂场景优选)
对于需要构建完整 API 接口的项目(如支持分页、权限控制、数据序列化),推荐使用 Django REST framework(DRF)------它是 Django 的扩展库,提供了"序列化器""视图集"等工具,可自动处理数据解析、验证、响应,无需手动编写大量重复代码。
步骤1:安装并配置 DRF
首先通过 pip 安装 DRF:
bash
pip install djangorestframework
然后在 Django 项目的 settings.py
中添加 DRF 到 INSTALLED_APPS
:
python
# settings.py
INSTALLED_APPS = [
# ... 其他已安装的应用
'rest_framework', # 添加DRF应用
]
步骤2:定义序列化器(serializers.py)
序列化器的核心作用是"数据格式转换":将 Django 模型实例转为 JSON 数据(供前端渲染),或把前端传入的 JSON/表单数据转为模型实例(供后端存储),同时自动完成数据验证。
示例:文章序列化器
python
# serializers.py
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
# 内部Meta类指定关联的模型和序列化字段
class Meta:
model = Article # 关联的数据库模型
fields = ['id', 'title', 'content', 'pub_date'] # 需序列化的字段
read_only_fields = ['pub_date'] # pub_date为只读字段(自动生成,不允许前端修改)
步骤3:定义视图集(views.py)
DRF 的 ModelViewSet
是"全能视图",可自动生成 CRUD 接口(GET
列表/详情、POST
创建、PUT
更新、DELETE
删除),无需手动判断请求方法和解析数据。
示例:文章视图集
python
# views.py
from rest_framework import viewsets
from .models import Article
from .serializers import ArticleSerializer
class ArticleViewSet(viewsets.ModelViewSet):
# 1. 数据源:查询所有文章(可添加过滤条件,如按发布时间排序)
queryset = Article.objects.all().order_by('-pub_date') # 倒序:最新文章在前
# 2. 关联的序列化器:处理数据转换和验证
serializer_class = ArticleSerializer
步骤4:配置 URL 路由(urls.py)
通过 DRF 的 DefaultRouter
注册视图集,自动生成对应的 API 路由。
示例:API 路由配置
python
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet
# 1. 创建路由注册器
router = DefaultRouter()
# 2. 注册视图集:路由前缀为'articles',生成的接口路径为/api/articles/
router.register(r'articles', ArticleViewSet)
# 3. 配置URL:将注册器生成的路由包含到项目URL中
urlpatterns = [
# ... 其他已配置的URL
path('api/', include(router.urls)), # API接口根路径:/api/
]
接口使用说明
注册完成后,DRF 会自动生成以下接口,Vue3 可直接请求:
GET /api/articles/
:获取所有文章列表(支持分页,默认每页10条)POST /api/articles/
:创建新文章(自动验证数据,返回创建结果)GET /api/articles/{id}/
:获取单篇文章详情({id} 为文章ID)PUT /api/articles/{id}/
:更新单篇文章(全量更新)DELETE /api/articles/{id}/
:删除单篇文章
Vue3 无需关注数据格式解析------DRF 会自动处理 application/json
和 application/x-www-form-urlencoded
两种格式,且返回的 JSON 数据包含分页、状态等信息,便于前端处理。