Django5 与 Vue3 表单交互全解析:从基础到实战

在前后端分离开发日益普及的今天,Django 作为成熟的后端框架,Vue3 作为灵活高效的前端框架,二者的结合成为开发 Web 应用的热门选择。本文将基于专业课件内容,从 Django5 表单基础操作、接收 Vue3 表单数据、数据库交互,到综合案例实战,全方位解析二者的表单交互逻辑,附带完整代码示例与详细说明,助力开发者快速上手。

一、Django5 表单基础:从 Form 到 ModelForm

Django 提供了两种核心表单类 ------Form(无模型关联)和ModelForm(与模型绑定),可快速实现表单创建、验证与数据处理。

1.1 Form 类:处理无模型关联的表单

当表单无需与数据库模型直接绑定(如登录表单、留言表单)时,使用Form类最为合适。以下以 "留言表单" 为例,展示完整实现流程。

步骤 1:定义 Form 类(forms.py

在 Django 应用目录下创建forms.py,定义表单字段与验证规则:

python 复制代码
# forms.py
from django import forms

class 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

在视图中判断请求方法,处理表单提交与验证:

python 复制代码
# views.py
from django.shortcuts import render
from .forms import ContactForm

def 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 输入框:

python 复制代码
<!-- 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

首先创建与表单关联的模型:

python 复制代码
# models.py
from django.db import models

class 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类指定关联的模型与字段:

python 复制代码
# forms.py
from django import forms
from .models import Article

class 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

ModelFormsave()方法可直接将验证后的数据存储到数据库:

python 复制代码
# views.py
from django.shortcuts import render, redirect
from .forms import ArticleForm

def 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参数,示例:

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,  # 密码隐藏输入
        validators=[MinLengthValidator(8, message='密码长度不能少于8个字符')]
    )
1.3.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')
        
        # 验证两次密码是否一致
        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 文章提交表单
python 复制代码
<!-- 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" 
          required
          class="form-control"
        >
      </div>
      <div class="form-group">
        <label for="content">内容</label>
        <textarea 
          id="content" 
          v-model="formData.content" 
          required
          class="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后端接口URL
      formData.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 数据:

python 复制代码
# views.py
import json
from django.http import JsonResponse
from .models import Article

def 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获取数据:

python 复制代码
# views.py(表单格式版)
from django.http import JsonResponse
from .models import Article

def 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):
python 复制代码
# views.py
from django.http import JsonResponse
from django.utils import timezone
from datetime import timedelta
from .models import Article

def 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可自动查询并序列化数据:

python 复制代码
# views.py
from rest_framework import generics
from .models import Article
from .serializers import ArticleSerializer

class 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 文章列表组件
python 复制代码
<!-- 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 的表单交互、数据库操作,最终通过 "产品管理系统" 案例实现了完整的前后端协同流程。核心要点总结如下:

  1. Django 表单Form处理无模型关联场景,ModelForm绑定模型简化存储;
  2. 数据交互 :Vue3 用axios发送 JSON / 表单数据,Django 用request.body或 DRF 接收;
  3. 数据库:配置 MySQL 后,通过 Django ORM 或 DRF 自动处理数据存储与查询;
相关推荐
Elastic 中国社区官方博客2 小时前
在 Elasticsearch 中使用 Mistral Chat completions 进行上下文工程
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
编程爱好者熊浪3 小时前
两次连接池泄露的BUG
java·数据库
TDengine (老段)5 小时前
TDengine 字符串函数 CHAR 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
qq7422349845 小时前
Python操作数据库之pyodbc
开发语言·数据库·python
姚远Oracle ACE5 小时前
Oracle 如何计算 AWR 报告中的 Sessions 数量
数据库·oracle
Dxy12393102166 小时前
MySQL的SUBSTRING函数详解与应用
数据库·mysql
码力引擎6 小时前
【零基础学MySQL】第十二章:DCL详解
数据库·mysql·1024程序员节
杨云龙UP6 小时前
【MySQL迁移】MySQL数据库迁移实战(利用mysqldump从Windows 5.7迁至Linux 8.0)
linux·运维·数据库·mysql·mssql
l1t6 小时前
利用DeepSeek辅助修改luadbi-duckdb读取DuckDB decimal数据类型
c语言·数据库·单元测试·lua·duckdb
安当加密6 小时前
Nacos配置安全治理:把数据库密码从YAML里请出去
数据库·安全