全网最详细!!!Django+Vue3 全栈开发进阶:从后端接口到前端交互实战

全网最详细!!!Django+Vue3 全栈开发进阶:从后端接口到前端交互实战

引路者👇:

引言:

第一章:Django 视图与路由:构建灵活的后端入口

1.1 URL 路由进阶:精准匹配与模块化拆分

1.1.1 路径转换器:捕获动态 URL 参数

1.1.2 正则表达式:复杂 URL 模式匹配

1.1.3 命名空间与模块化路由:解决多 App 冲突

1. 根路由配置命名空间(project/urls.py)

2. App 内路由定义(blog/urls.py)

3. 反向解析路由(视图 / 模板中)

1.2 视图函数优化:简化代码与增强功能

1.2.1 快捷函数:减少重复逻辑

1.2.2 视图装饰器:增强视图功能

1. 限制 HTTP 方法(require_http_methods系列)

2. 用户认证与权限控制(login_required/permission_required)

3. 页面缓存(cache_page)

第二章:Django ORM 高级操作:高效处理数据库数据

2.1 高级查询:精准筛选与数据统计

2.1.1 条件查询:复杂筛选逻辑

2.1.2 聚合查询:统计数据(Count/Sum/Avg)

2.2 批量操作:提升大数据处理效率

2.2.1 批量创建:bulk_create()

2.2.2 批量更新:update()

2.2.3 批量删除:delete()

2.3 关联查询优化:避免 N+1 查询问题

第三章:Vue3 与 Django 交互:表单提交与异步处理

3.1 Vue3 表单设计:双向绑定与数据验证

3.2 Django 接收表单数据:处理 JSON 与表单格式

3.2.1 接收 JSON 格式数据(推荐)

3.2.2 接收表单格式数据

3.2.3 Vue3 发送表单格式数据的配置

第四章:跨域问题解决:Django CORS 配置

4.1 三种 CORS 配置方法(推荐第一种)

4.1.1 方法 1:使用django-cors-headers(推荐,功能完善)

1. 安装依赖

2. 配置settings.py

3. 前端配合配置(Axios 携带凭证)

4.1.2 方法 2:手动编写自定义中间件(无需第三方依赖)

4.1.3 方法 3:在视图中单独添加 CORS 头(适用于个别接口)

4.2 生产环境 CORS 配置注意事项

第五章:Promise 与异步处理:Vue3 中的异步请求优化

5.1 Promise 基础:管理异步状态

5.1.1 创建 Promise 对象

5.1.2 消费 Promise:then/catch/finally

5.2 Promise 链式调用:避免回调嵌套

5.3 async/await:简化异步代码

5.4 Promise 静态方法:并行处理多个异步请求

第六章:综合案例:Django+Vue3 实现产品管理系统

6.1 后端 Django 实现

1. 模型定义(models.py)

2. 视图函数(views.py)

3. URL 配置(urls.py)

4. CORS 配置(settings.py)

6.2 前端 Vue3 实现

第七章:总结


引言:

        在前后端分离开发模式中,Django 凭借强大的后端能力负责数据处理与接口提供,Vue3 则以灵活的组件化开发构建用户交互界面。本文Fly从 Django 的视图路由优化、ORM 高级操作,到 Vue3 的表单交互与异步处理,再到跨域问题解决,完整覆盖全栈开发的核心环节,附带可直接复用的代码示例,助力你快速落地项目。

第一章:Django 视图与路由:构建灵活的后端入口

        视图与路由是 Django 接收前端请求、分发业务逻辑的核心。合理的路由设计与视图封装,能让后端接口更易维护、扩展性更强。本节将深入讲解路由的高级配置与视图的实战技巧。

1.1 URL 路由进阶:精准匹配与模块化拆分

1.1.1 路径转换器:捕获动态 URL 参数

        Django 内置路径转换器,可快速提取 URL 中的动态参数(如文章 ID、分类别名),无需手动解析字符串,大幅简化代码。

转换器功能描述示例代码
str匹配除/外的任意字符(默认类型)path('user/<str:username>/', views.user_detail)
int匹配非负整数(常用于 ID、页码等场景)path('article/<int:pk>/', views.article_detail)
slug匹配字母、数字、_-组成的 URL 友好字符串path('category/<slug:alias>/', views.category_articles)
uuid匹配 UUID 格式字符串(唯一性强,适合安全场景)path('order/<uuid:order_id>/', views.order_detail)
path匹配包含/的完整路径(如文件路径)path('file/<path:file_path>/', views.file_download)

实战示例:通过转换器实现文章详情与分类列表

# myapp/urls.py from django.urls import path from . import views urlpatterns = [ # 文章详情:URL格式 /article/1/ → 提取pk=1(文章ID) path('article/<int:pk>/', views.article_detail, name='article_detail'), # 分类文章:URL格式 /category/tech/ → 提取alias='tech'(分类别名) path('category/<slug:alias>/', views.category_articles, name='category_articles'), # 文章归档:URL格式 /archive/2025/08/ → 提取year=2025、month=08 path('archive/<int:year>/<int:month>/', views.article_archive, name='article_archive'), ] # myapp/views.py from django.shortcuts import render, get_object_or_404 from .models import Article, Category def article_detail(request, pk): """通过文章ID获取详情,不存在则返回404""" # get_object_or_404:查询失败时自动抛出404,替代try-except article = get_object_or_404(Article, pk=pk) return render(request, 'article_detail.html', {'article': article}) def category_articles(request, alias): """通过分类别名获取文章列表""" category = get_object_or_404(Category, alias=alias) articles = Article.objects.filter(category=category).order_by('-pub_date') return render(request, 'category_list.html', {'category': category, 'articles': articles}) 

1.1.2 正则表达式:复杂 URL 模式匹配

        对于更灵活的 URL 规则(如 “按年月归档”“多条件筛选”),可使用re_path结合正则表达式,实现精准匹配。

示例:匹配 “/archive/2025/08/” 格式的归档 URL

# myapp/urls.py from django.urls import re_path from . import views urlpatterns = [ # 正则表达式:匹配4位年份+2位月份(如2025/08) re_path(r'^archive/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.article_archive, name='article_archive'), ] # myapp/views.py from datetime import datetime from django.shortcuts import render from .models import Article def article_archive(request, year, month): """获取指定年月的文章归档""" # 转换参数类型(URL提取的是字符串,需转为整数) year = int(year) month = int(month) # 筛选该年月的文章 articles = Article.objects.filter( pub_date__year=year, pub_date__month=month ).order_by('-pub_date') return render(request, 'archive.html', { 'articles': articles, 'year': year, 'month': month, 'current_date': datetime.now().strftime('%Y-%m-%d') }) 

1.1.3 命名空间与模块化路由:解决多 App 冲突

        当项目包含多个 App 时,可能出现路由名称重复(如article_detail)。通过 “命名空间” 可区分不同 App 的路由,通过 “模块化拆分” 可让路由配置更清晰。

1. 根路由配置命名空间(project/urls.py)
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), # 为blog App配置命名空间'blog' path('blog/', include(('blog.urls', 'blog'), namespace='blog')), # 为shop App配置命名空间'shop' path('shop/', include(('shop.urls', 'shop'), namespace='shop')), ] 
2. App 内路由定义(blog/urls.py)
# blog/urls.py from django.urls import path from . import views # 必须指定app_name,与根路由的命名空间对应 app_name = 'blog' urlpatterns = [ path('article/<int:pk>/', views.article_detail, name='article_detail'), path('category/<slug:alias>/', views.category_articles, name='category_articles'), ] 
3. 反向解析路由(视图 / 模板中)
# 视图中反向解析(views.py) from django.urls import reverse def test_view(request): # 解析blog App的article_detail路由:/blog/article/1/ blog_url = reverse('blog:article_detail', args=[1]) # 解析shop App的product_detail路由(假设存在) shop_url = reverse('shop:product_detail', args=[100]) return render(request, 'test.html', {'blog_url': blog_url, 'shop_url': shop_url}) 
<!-- 模板中反向解析(test.html) --> <a href="{% url 'blog:article_detail' 1 %}">博客文章1</a> <a href="{% url 'shop:product_detail' 100 %}">商城商品100</a> 

1.2 视图函数优化:简化代码与增强功能

1.2.1 快捷函数:减少重复逻辑

        Django 提供render()redirect()get_object_or_404()等快捷函数,可替代冗余的手动代码,提升开发效率。

快捷函数功能描述
render(request, template, context)渲染模板并返回HttpResponse,自动处理模板路径查找
redirect(to, *args, **kwargs)重定向到指定 URL(支持路由名称、绝对路径、外部链接)
get_object_or_404(Model, **kwargs)根据条件查询单个对象,不存在则返回 404(替代try-except Model.DoesNotExist
get_list_or_404(Model, **kwargs)根据条件查询对象列表,为空则返回 404

示例:使用快捷函数简化视图

# myapp/views.py from django.shortcuts import render, redirect, get_object_or_404, get_list_or_404 from .models import Article def article_detail(request, pk): # 替代:try: article = Article.objects.get(pk=pk) except Article.DoesNotExist: raise Http404 article = get_object_or_404(Article, pk=pk) return render(request, 'article_detail.html', {'article': article}) def published_articles(request): # 若没有已发布的文章,直接返回404 articles = get_list_or_404(Article, is_published=True) return render(request, 'article_list.html', {'articles': articles}) def delete_article(request, pk): article = get_object_or_404(Article, pk=pk) article.delete() # 重定向到文章列表页(使用路由名称,避免硬编码URL) return redirect('article_list') 

1.2.2 视图装饰器:增强视图功能

        装饰器可在不修改视图核心逻辑的前提下,为其添加额外功能(如限制 HTTP 方法、要求登录、缓存页面)。

1. 限制 HTTP 方法(require_http_methods系列)

        确保视图仅接收指定的 HTTP 方法(如GET/POST),非法方法自动返回 405(Method Not Allowed):

from django.views.decorators.http import require_http_methods, require_GET, require_POST # 仅允许GET和POST方法 @require_http_methods(['GET', 'POST']) def article_edit(request, pk): article = get_object_or_404(Article, pk=pk) if request.method == 'GET': # 显示编辑表单 return render(request, 'article_edit.html', {'article': article}) elif request.method == 'POST': # 处理表单提交 title = request.POST.get('title') content = request.POST.get('content') article.title = title article.content = content article.save() return redirect('article_detail', pk=pk) # 仅允许GET方法(简化版@require_http_methods(['GET'])) @require_GET def article_list(request): articles = Article.objects.all().order_by('-pub_date') return render(request, 'article_list.html', {'articles': articles}) # 仅允许POST方法(简化版@require_http_methods(['POST'])) @require_POST def article_delete(request, pk): article = get_object_or_404(Article, pk=pk) article.delete() return redirect('article_list') 
2. 用户认证与权限控制(login_required/permission_required

        限制视图仅允许已登录用户或拥有特定权限的用户访问:

from django.contrib.auth.decorators import login_required, permission_required # 仅允许已登录用户访问(未登录自动跳转到登录页) @login_required def user_profile(request): # request.user 为当前登录用户 return render(request, 'profile.html', {'user': request.user}) # 仅允许拥有“修改文章”权限的用户访问 @permission_required('myapp.change_article') # 权限格式:app_label.permission_name def article_edit(request, pk): article = get_object_or_404(Article, pk=pk) # 处理编辑逻辑... return render(request, 'article_edit.html', {'article': article}) 
3. 页面缓存(cache_page

        对频繁访问且变化少的页面(如首页、热门文章)进行缓存,减少数据库查询,提升性能:

from django.views.decorators.cache import cache_page # 缓存15分钟(单位:秒) @cache_page(60 * 15) def home_page(request): # 复杂查询(如热门文章、分类统计) hot_articles = Article.objects.order_by('-views')[:5] category_count = Category.objects.annotate(article_count=Count('article')) return render(request, 'home.html', { 'hot_articles': hot_articles, 'category_count': category_count }) 

第二章:Django ORM 高级操作:高效处理数据库数据

        ORM(对象关系映射)是 Django 的核心特性,通过 Python 代码即可操作数据库,无需编写 SQL。本节将讲解 ORM 的高级查询、批量操作与关联查询优化,助力处理复杂数据场景。

2.1 高级查询:精准筛选与数据统计

2.1.1 条件查询:复杂筛选逻辑

        除基础的filter()/exclude(),Django ORM 支持通过 “双下划线” 语法实现范围、模糊匹配、日期筛选等复杂条件。

条件类型ORM 语法示例对应 SQL 逻辑
大于 / 小于filter(price__gt=100)/filter(price__lt=50)WHERE price > 100/WHERE price < 50
大于等于 / 小于等于filter(pub_date__gte='2025-01-01')WHERE pub_date >= '2025-01-01'
模糊匹配filter(title__contains='Python')WHERE title LIKE '%Python%'
开头 / 结尾匹配filter(title__startswith='Django')/filter(title__endswith='教程')WHERE title LIKE 'Django%'/WHERE title LIKE '%教程'
范围查询filter(id__in=[1,2,3])/filter(price__range=(50, 100))WHERE id IN (1,2,3)/WHERE price BETWEEN 50 AND 100
日期筛选filter(pub_date__year=2025)/filter(pub_date__week_day=1)WHERE YEAR(pub_date) = 2025/WHERE WEEKDAY(pub_date) = 0(周日)

示例:多条件组合查询

from django.db.models import Q from .models import Article # 1. 基础多条件(AND逻辑):2025年发布且标题含“Python”的已发布文章 articles1 = Article.objects.filter( pub_date__year=2025, title__contains='Python', is_published=True ) # 2. 复杂逻辑(OR+AND):使用Q对象 # 查询“标题含Python”或“阅读量>1000”,且发布时间在2025年的文章 articles2 = Article.objects.filter( Q(title__contains='Python') | Q(views__gt=1000), # OR逻辑 pub_date__year=2025 # AND逻辑(与Q对象组合) ) # 3. 排除条件:非“技术”分类且阅读量>=500的文章 articles3 = Article.objects.filter( views__gte=500 ).exclude( category__name='技术' ) 

2.1.2 聚合查询:统计数据(Count/Sum/Avg)

        通过aggregate()结合聚合函数,实现数据统计(如总数、求和、平均值),返回字典格式结果;通过annotate()实现分组聚合(如按分类统计文章数)。

示例:聚合函数使用

from django.db.models import Count, Sum, Avg, Max, Min from .models import Article, Order # 1. 基础聚合:统计文章总数、平均阅读量、最高阅读量 article_stats = Article.objects.aggregate( total=Count('id'), avg_views=Avg('views'), max_views=Max('views') ) print(article_stats) # 输出:{'total': 100, 'avg_views': 250.5, 'max_views': 1000} # 2. 分组聚合:按分类统计文章数量(并按数量降序) category_article_count = Category.objects.annotate( count=Count('article') # 按分类分组,统计每组的文章数 ).order_by('-count') print(category_article_count) # 输出:[<Category: 技术 (50)>, <Category: 生活 (30)>, ...] # 3. 关联聚合:统计每个用户的订单总金额 user_order_stats = User.objects.annotate( total_amount=Sum('order__amount') # 关联Order模型的amount字段求和 ).filter(total_amount__gt=1000) # 筛选总金额>1000的用户 

2.2 批量操作:提升大数据处理效率

        对于大量数据(如批量导入、批量更新),使用 ORM 的批量操作方法,可减少数据库交互次数,大幅提升性能。

2.2.1 批量创建:bulk_create()

        一次性创建多个对象,仅需 1 次数据库交互(替代循环调用create()):

# 批量创建100篇测试文章 articles = [ Article( title=f"测试文章{i}", content=f"文章{i}的内容...", category_id=1, is_published=True ) for i in range(1, 101) ] # 批量插入(batch_size控制每次插入数量,避免单次请求过大) Article.objects.bulk_create(articles, batch_size=50) 

2.2.2 批量更新:update()

        一次性更新符合条件的所有对象,仅需 1 次数据库交互:

from django.db.models import F from datetime import timezone # 1. 批量更新状态:将“待审核”文章改为“已发布” Article.objects.filter(status='pending').update(status='published') # 2. 批量更新数值字段:所有文章阅读量+100(F()避免竞态条件) Article.objects.all().update(views=F('views') + 100) # 3. 批量更新时间字段:更新所有文章的最后修改时间 Article.objects.all().update(updated_at=timezone.now()) 

2.2.3 批量删除:delete()

        一次性删除符合条件的所有对象,注意谨慎使用(无回收站,删除后不可恢复):

from datetime import datetime, timedelta # 1. 删除1年前的过期文章 one_year_ago = datetime.now() - timedelta(days=365) Article.objects.filter(pub_date__lt=one_year_ago).delete() # 2. 删除“测试”分类下的所有文章 Article.objects.filter(category__name='测试').delete() 

2.3 关联查询优化:避免 N+1 查询问题

        “N+1 查询问题” 指:查询 N 个对象时,先执行 1 次查询获取主表数据,再执行 N 次查询获取每个对象的关联数据。Django 提供select_related()prefetch_related()解决该问题。

方法适用场景原理
select_related()一对一(OneToOneField)、一对多(ForeignKey,正向查询)执行 JOIN 查询,一次性获取主表 + 关联表数据
prefetch_related()多对多(ManyToManyField)、一对多(反向查询)执行 2 次独立查询,内存中关联数据

示例:优化关联查询

# 1. 优化一对多正向查询(Article → Category) # 原写法(N+1问题):获取10篇文章,执行1次Article查询 + 10次Category查询 articles = Article.objects.all()[:10] for article in articles: print(article.category.name) # 每次循环执行1次Category查询 # 优化写法(1次查询):使用select_related() JOIN Category表 articles = Article.objects.select_related('category').all()[:10] for article in articles: print(article.category.name) # 无额外查询 # 2. 优化多对多查询(Article → Tag) # 原写法(N+1问题):获取10篇文章,执行1次Article查询 + 10次Tag查询 articles = Article.objects.all()[:10] for article in articles: print(article.tags.all()) # 每次循环执行1次Tag查询 # 优化写法(2次查询):使用prefetch_related() articles = Article.objects.prefetch_related('tags').all()[:10] for article in articles: print(article.tags.all()) # 无额外查询 # 3. 组合优化(多关联层级):获取文章的分类+标签+作者 articles = Article.objects.select_related('category', 'author').prefetch_related('tags').all()[:10] 

第三章:Vue3 与 Django 交互:表单提交与异步处理

        前后端分离架构中,Vue3 通过 Axios 发送异步请求,与 Django 后端交互。本节将讲解 Vue3 的表单设计、异步请求处理,以及 Django 如何接收并处理前端数据。

3.1 Vue3 表单设计:双向绑定与数据验证

        Vue3 的v-model实现表单数据双向绑定,结合axios可快速将表单数据发送到 Django 后端。以下为 “文章发布” 表单示例:

<!-- src/components/ArticleForm.vue --> <template> <div> <h2>发布文章</h2> <form @submit.prevent="submitArticle"> <!-- 文章标题 --> <div> <label>标题:</label> <input type="text" v-model="form.title" required placeholder="请输入文章标题(不超过200字)" @blur="validateTitle" > <p v-if="titleError">{{ titleError }}</p> </div> <!-- 文章分类 --> <div> <label>分类:</label> <select v-model="form.categoryId" required> <option>请选择分类</option> <option v-for="category in categories" :key="category.id" :value="category.id"> {{ category.name }} </option> </select> </div> <!-- 文章内容 --> <div> <label>内容:</label> <textarea v-model="form.content" required rows="8" placeholder="请输入文章内容" ></textarea> </div> <!-- 提交按钮(加载状态) --> <button type="submit" :disabled="loading"> {{ loading ? "发布中..." : "发布文章" }} </button> </form> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import axios from 'axios'; // 表单数据 const form = ref({ title: "", categoryId: "", content: "" }); // 分类列表(从Django后端获取) const categories = ref([]); // 加载状态 const loading = ref(false); // 标题验证错误 const titleError = ref(""); // 验证标题长度 const validateTitle = () => { if (form.value.title.length > 200) { titleError.value = "标题长度不能超过200字"; } else { titleError.value = ""; } }; // 获取分类列表(组件挂载时执行) const fetchCategories = async () => { try { const response = await axios.get("/api/categories/"); categories.value = response.data.data; } catch (error) { console.error("获取分类失败:", error); alert("获取分类失败,请刷新页面重试"); } }; // 提交文章 const submitArticle = async () => { // 先验证表单 validateTitle(); if (titleError.value || !form.value.title || !form.value.categoryId || !form.value.content) { alert("请完善表单信息"); return; } loading.value = true; try { // 发送POST请求到Django后端 const response = await axios.post("/api/articles/", form.value); alert(`文章发布成功!文章ID:${response.data.data.id}`); // 重置表单 form.value = { title: "", categoryId: "", content: "" }; } catch (error) { console.error("发布失败:", error); alert(error.response?.data?.message || "发布失败,请稍后再试"); } finally { loading.value = false; } }; // 组件挂载时加载分类 onMounted(() => { fetchCategories(); }); </script> <style scoped> .article-form { max-width: 800px; margin: 20px auto; padding: 0 20px; } .form-item { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; font-weight: bold; } input, select, textarea { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } .error { color: #dc3545; font-size: 0.9em; margin: 5px 0 0 0; } button { padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } button:disabled { background: #6c757d; cursor: not-allowed; } </style> 

3.2 Django 接收表单数据:处理 JSON 与表单格式

        Django 可接收 Vue3 发送的两种数据格式:application/x-www-form-urlencoded(表单格式)和application/json(JSON 格式),以下为两种格式的处理示例。

3.2.1 接收 JSON 格式数据(推荐)

        Vue3 的 Axios 默认发送 JSON 格式数据,Django 需通过request.body解析:

# myapp/views.py import json from django.http import JsonResponse from .models import Article, Category def create_article(request): """接收Vue3发送的JSON数据,创建文章""" if request.method == "POST": try: # 解析JSON格式的请求体 data = json.loads(request.body) title = data.get("title") category_id = data.get("categoryId") content = data.get("content") # 数据验证 if not title or not category_id or not content: return JsonResponse({ "status": "error", "message": "标题、分类、内容不能为空" }, status=400) # 验证分类是否存在 category = get_object_or_404(Category, id=category_id) # 存储到数据库 article = Article.objects.create( title=title, category=category, content=content, is_published=True ) # 返回成功响应 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) except json.JSONDecodeError: # 数据格式错误(非JSON) return JsonResponse({ "status": "error", "message": "数据格式错误,请发送JSON格式" }, status=400) # 非POST请求 return JsonResponse({ "status": "error", "message": "仅支持POST请求" }, status=405) 

3.2.2 接收表单格式数据

        若 Vue3 发送表单格式数据(需配置 Axios 的Content-Type),Django 可通过request.POST获取:

# myapp/views.py from django.http import JsonResponse from django.shortcuts import get_object_or_404 from .models import Article, Category def create_article_form(request): """接收表单格式数据,创建文章""" if request.method == "POST": # 从request.POST获取表单数据 title = request.POST.get("title") category_id = request.POST.get("categoryId") content = request.POST.get("content") # 数据验证(同JSON格式) if not title or not category_id or not content: return JsonResponse({ "status": "error", "message": "标题、分类、内容不能为空" }, status=400) category = get_object_or_404(Category, id=category_id) article = Article.objects.create( title=title, category=category, content=content ) return JsonResponse({ "status": "success", "data": {"id": article.id} }, status=201) return JsonResponse({"status": "error", "message": "仅支持POST请求"}, status=405) 

3.2.3 Vue3 发送表单格式数据的配置

        若需发送表单格式数据,需在 Axios 中配置Content-Type

// Vue3组件中配置Axios const submitArticleForm = async () => { try { const formData = new FormData(); formData.append("title", form.value.title); formData.append("categoryId", form.value.categoryId); formData.append("content", form.value.content); await axios.post("/api/articles/form/", formData, { headers: { "Content-Type": "multipart/form-data" // 表单格式(支持文件上传) } }); alert("发布成功"); } catch (error) { console.error(error); } }; 

第四章:跨域问题解决:Django CORS 配置

        前后端分离架构中,Vue3(如运行在http://localhost:8080)与 Django(如运行在http://localhost:8000)因域名 / 端口不同,会触发浏览器的 “同源策略”,导致请求被拦截。需通过 CORS(跨域资源共享)配置允许跨域访问。

4.1 三种 CORS 配置方法(推荐第一种)

4.1.1 方法 1:使用django-cors-headers(推荐,功能完善)

  django-cors-headers是 Django 官方推荐的跨域解决方案,支持细粒度控制允许的域名、方法、请求头。

1. 安装依赖
pip install django-cors-headers 
2. 配置settings.py

        需在INSTALLED_APPS中注册应用,并在MIDDLEWARE中添加中间件(顺序必须靠前,建议放在SecurityMiddleware之前):

# settings.py INSTALLED_APPS = [ # Django内置应用... 'corsheaders', # 注册cors-headers应用 'myapp', # 自定义应用 ] # 中间件配置(corsheaders.middleware.CorsMiddleware必须靠前) MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', # 跨域中间件 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', # 其他中间件... ] # ------------------- CORS核心配置 ------------------- # 开发环境:允许所有域名(方便调试,生产环境禁用) # CORS_ALLOW_ALL_ORIGINS = True # 生产环境:指定允许的域名(推荐,更安全) CORS_ALLOWED_ORIGINS = [ "http://localhost:8080", # Vue3开发环境默认端口 "http://127.0.0.1:8080", "https://yourdomain.com", # 生产环境域名 ] # 允许的HTTP方法(默认支持GET/POST,可扩展) CORS_ALLOW_METHODS = [ "DELETE", "GET", "OPTIONS", "PATCH", "POST", "PUT", ] # 允许的请求头(如Authorization、Content-Type) CORS_ALLOW_HEADERS = [ "accept", "authorization", "content-type", "user-agent", "x-csrftoken", "x-requested-with", ] # 是否允许携带凭证(如Cookies、Token,需前后端同时配置) CORS_ALLOW_CREDENTIALS = True # 预检请求(OPTIONS)的缓存时间(秒),减少重复请求 CORS_PREFLIGHT_MAX_AGE = 86400 # 24小时 
3. 前端配合配置(Axios 携带凭证)

        若CORS_ALLOW_CREDENTIALS = True,Vue3 的 Axios 需开启withCredentials

// src/utils/axios.js import axios from "axios"; const service = axios.create({ baseURL: "http://127.0.0.1:8000/api", timeout: 5000, withCredentials: true // 允许携带凭证 }); export default service; 

4.1.2 方法 2:手动编写自定义中间件(无需第三方依赖)

        若不想安装额外包,可自定义中间件,手动添加 CORS 响应头:

# myapp/middleware.py class CustomCorsMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 处理请求 response = self.get_response(request) # 添加CORS响应头(允许Vue3开发环境跨域) response["Access-Control-Allow-Origin"] = "http://localhost:8080" response["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS" response["Access-Control-Allow-Headers"] = "Content-Type, Authorization" response["Access-Control-Allow-Credentials"] = "true" return response 

        在settings.py中注册中间件:

# settings.py MIDDLEWARE = [ 'myapp.middleware.CustomCorsMiddleware', # 自定义跨域中间件 # 其他中间件... ] 

4.1.3 方法 3:在视图中单独添加 CORS 头(适用于个别接口)

        若仅需个别接口支持跨域,可在视图函数中手动添加响应头:

# myapp/views.py from django.http import JsonResponse def cross_domain_api(request): """仅该接口支持跨域""" data = {"message": "支持跨域的接口数据"} response = JsonResponse(data) # 添加CORS头 response["Access-Control-Allow-Origin"] = "http://localhost:8080" response["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS" response["Access-Control-Allow-Headers"] = "Content-Type, Authorization" return response 

4.2 生产环境 CORS 配置注意事项

  1. 禁止使用CORS_ALLOW_ALL_ORIGINS = True:生产环境必须明确指定允许的域名,避免恶意网站跨域访问;
  2. 限制允许的方法和头:仅开放业务必需的 HTTP 方法(如GET/POST/PUT)和请求头,减少攻击面;
  3. 凭证携带需谨慎:若CORS_ALLOW_CREDENTIALS = TrueCORS_ALLOWED_ORIGINS不能使用通配符*,必须指定具体域名;
  4. 处理预检请求(OPTIONS):复杂请求(如带自定义头、PUT/DELETE方法)会先发送OPTIONS请求,django-cors-headers会自动处理,自定义中间件需手动支持。

第五章:Promise 与异步处理:Vue3 中的异步请求优化

        Vue3 中处理异步请求(如 Axios 调用)时,Promise是核心工具,可避免 “回调地狱”;async/await则是Promise的语法糖,让异步代码更易读。本节将讲解Promise的核心用法与async/await的实战应用。

5.1 Promise 基础:管理异步状态

  Promise用于管理异步操作的状态(pendingfulfilled/rejected),通过then()/catch()/finally()处理结果。

5.1.1 创建 Promise 对象

        模拟 “用户登录” 异步操作,成功返回用户信息,失败返回错误原因:

// 模拟登录接口(异步操作) const login = (username, password) => { return new Promise((resolve, reject) => { // 模拟后端接口延迟(1秒) setTimeout(() => { if (username === "admin" && password === "123456") { // 成功:调用resolve返回结果 resolve({ userId: 1, username: "admin", role: "superuser" }); } else { // 失败:调用reject返回错误 reject(new Error("用户名或密码错误")); } }, 1000); }); }; 

5.1.2 消费 Promise:then/catch/finally

// 调用登录接口 login("admin", "123456") .then((userInfo) => { console.log("登录成功:", userInfo); // 后续操作:如存储Token、跳转首页 localStorage.setItem("token", "xxx"); window.location.href = "/home"; }) .catch((error) => { console.error("登录失败:", error.message); // 显示错误提示 alert(error.message); }) .finally(() => { console.log("登录请求结束(无论成功/失败)"); // 关闭登录按钮的加载状态 document.getElementById("loginBtn").disabled = false; }); 

5.2 Promise 链式调用:避免回调嵌套

        每个then()都会返回一个新的Promise,支持连续调用,实现 “依次执行多个异步操作”:

// 步骤1:登录 → 步骤2:获取用户权限 → 步骤3:获取用户菜单 login("admin", "123456") .then((userInfo) => { // 步骤1成功:携带用户ID请求权限 return axios.get(`/api/permissions?userId=${userInfo.userId}`); }) .then((permissionRes) => { const permissions = permissionRes.data; // 步骤2成功:携带权限请求菜单 return axios.get(`/api/menus?role=${permissions.role}`); }) .then((menuRes) => { const menus = menuRes.data; console.log("用户菜单:", menus); // 渲染菜单到页面 }) .catch((error) => { // 任一环节失败,都会进入catch console.error("流程失败:", error); }); 

5.3 async/await:简化异步代码

   async/await是 ES8 推出的语法糖,基于Promise实现,将异步代码 “同步化” 书写,更易读、易调试:

// 定义async函数(必须在async函数中使用await) const initUser = async () => { // 关闭按钮加载状态 const loginBtn = document.getElementById("loginBtn"); loginBtn.disabled = true; try { // 等待登录结果(暂停执行,直到Promise完成) const userInfo = await login("admin", "123456"); console.log("登录成功:", userInfo); // 等待权限请求结果 const permissionRes = await axios.get(`/api/permissions?userId=${userInfo.userId}`); const permissions = permissionRes.data; // 等待菜单请求结果 const menuRes = await axios.get(`/api/menus?role=${permissions.role}`); const menus = menuRes.data; console.log("用户菜单:", menus); // 渲染菜单 renderMenus(menus); } catch (error) { // 所有await的错误,都会被catch捕获 console.error("初始化失败:", error); alert(error.message); } finally { // 无论成功/失败,都会执行 loginBtn.disabled = false; } }; // 调用async函数 initUser(); 

5.4 Promise 静态方法:并行处理多个异步请求

        Vue3 中常需同时请求多个接口(如 “用户信息 + 未读消息数”),Promise的静态方法可高效处理并行请求。

静态方法功能描述适用场景
Promise.all()接收多个 Promise,全部成功才返回结果数组;任一失败则立即返回错误依赖多个接口数据才能渲染页面
Promise.race()接收多个 Promise,返回第一个完成的结果(无论成功 / 失败)超时控制(如 “接口 5 秒未响应则提示超时”)
Promise.allSettled()接收多个 Promise,所有操作结束后返回每个结果的状态(status+value/reason需知道所有请求的结果(成功 / 失败)
Promise.any()接收多个 Promise,返回第一个成功的结果;全部失败才返回错误多源数据备份(如 “优先请求 CDN,失败则请求本地”)

示例:使用Promise.all()并行请求

// 并行请求“用户信息”和“未读消息数” const fetchUserInfo = axios.get("/api/user/1"); const fetchUnreadCount = axios.get("/api/messages/unread"); Promise.all([fetchUserInfo, fetchUnreadCount]) .then(([userRes, countRes]) => { const userInfo = userRes.data; const unreadCount = countRes.data; // 渲染用户信息和未读消息数 renderUserInfo(userInfo); renderUnreadCount(unreadCount); }) .catch((error) => { console.error("任一请求失败:", error); alert("数据加载失败,请刷新页面"); }); 

第六章:综合案例:Django+Vue3 实现产品管理系统

        结合前文知识点,实现一个简单的 “产品管理系统”,包含 “产品列表展示”“产品添加”“产品删除” 功能,完整覆盖前后端交互、跨域配置、ORM 操作。

6.1 后端 Django 实现

1. 模型定义(models.py)

from django.db import models class Product(models.Model): """产品模型""" name = models.CharField(max_length=200, verbose_name="产品名称") price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="产品价格") description = models.TextField(blank=True, verbose_name="产品描述") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") is_active = models.BooleanField(default=True, verbose_name="是否激活") class Meta: verbose_name = "产品" verbose_name_plural = "产品" ordering = ["-created_at"] 

2. 视图函数(views.py)

import json from django.http import JsonResponse from django.shortcuts import get_object_or_404 from .models import Product # 1. 获取产品列表 def get_products(request): if request.method == "GET": products = Product.objects.filter(is_active=True).values("id", "name", "price", "created_at") products_list = list(products) # 格式化日期 for p in products_list: p["created_at"] = p["created_at"].strftime("%Y-%m-%d %H:%M:%S") return JsonResponse({"status": "success", "data": products_list}) return JsonResponse({"status": "error", "message": "仅支持GET"}, status=405) # 2. 添加产品 def add_product(request): if request.method == "POST": try: data = json.loads(request.body) name = data.get("name") price = data.get("price") description = data.get("description", "") if not name or not price: return JsonResponse({"status": "error", "message": "名称和价格不能为空"}, status=400) product = Product.objects.create( name=name, price=price, description=description ) return JsonResponse({ "status": "success", "data": {"id": product.id, "name": product.name} }, status=201) except json.JSONDecodeError: return JsonResponse({"status": "error", "message": "数据格式错误"}, status=400) return JsonResponse({"status": "error", "message": "仅支持POST"}, status=405) # 3. 删除产品 def delete_product(request, pk): if request.method == "DELETE": product = get_object_or_404(Product, pk=pk) product.delete() return JsonResponse({"status": "success", "message": "产品已删除"}) return JsonResponse({"status": "error", "message": "仅支持DELETE"}, status=405) 

3. URL 配置(urls.py)

from django.urls import path from . import views urlpatterns = [ path("products/", views.get_products, name="get_products"), path("products/add/", views.add_product, name="add_product"), path("products/delete/<int:pk>/", views.delete_product, name="delete_product"), ] 

4. CORS 配置(settings.py)

INSTALLED_APPS = [ # ...其他应用 'corsheaders', 'myapp', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', # ...其他中间件 ] # 允许Vue3开发环境跨域 CORS_ALLOWED_ORIGINS = ["http://localhost:8080"] CORS_ALLOW_METHODS = ["GET", "POST", "DELETE"] CORS_ALLOW_HEADERS = ["Content-Type"] 

6.2 前端 Vue3 实现

<template> <div> <h2>产品管理系统</h2> <!-- 添加产品表单 --> <div> <h3>添加产品</h3> <form @submit.prevent="addProduct"> <div> <label>产品名称:</label> <input type="text" v-model="form.name" required placeholder="请输入名称"> </div> <div> <label>产品价格:</label> <input type="number" step="0.01" v-model="form.price" required placeholder="请输入价格"> </div> <div> <label>产品描述:</label> <textarea v-model="form.description" placeholder="请输入描述"></textarea> </div> <button type="submit" :disabled="loading"> {{ loading ? "添加中..." : "添加产品" }} </button> </form> </div> <!-- 产品列表 --> <div> <h3>产品列表</h3> <div v-if="loadingList">加载中...</div> <div v-else-if="errorList">{{ errorList }}</div> <table v-else> <thead> <tr> <th>ID</th> <th>名称</th> <th>价格</th> <th>创建时间</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="product in products" :key="product.id"> <td>{{ product.id }}</td> <td>{{ product.name }}</td> <td>¥{{ product.price }}</td> <td>{{ product.created_at }}</td> <td> <button @click="deleteProduct(product.id)" :disabled="loadingDelete === product.id"> {{ loadingDelete === product.id ? "删除中..." : "删除" }} </button> </td> </tr> </tbody> </table> </div> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import axios from 'axios'; // 表单数据 const form = ref({ name: "", price: "", description: "" }); // 列表数据 const products = ref([]); const loadingList = ref(false); const errorList = ref(""); // 加载状态 const loading = ref(false); const loadingDelete = ref(null); // 获取产品列表 const fetchProducts = async () => { loadingList.value = true; errorList.value = ""; try { const res = await axios.get("/api/products/"); products.value = res.data.data; } catch (err) { errorList.value = "获取产品列表失败"; console.error(err); } finally { loadingList.value = false; } }; // 添加产品 const addProduct = async () => { loading.value = true; try { await axios.post("/api/products/add/", form.value); alert("产品添加成功!"); // 重置表单 form.value = { name: "", price: "", description: "" }; // 重新加载列表 fetchProducts(); } catch (err) { alert(err.response?.data?.message || "添加失败"); console.error(err); } finally { loading.value = false; } }; // 删除产品 const deleteProduct = async (id) => { if (!confirm("确定要删除该产品吗?")) return; loadingDelete.value = id; try { await axios.delete(`/api/products/delete/${id}/`); alert("产品删除成功!"); // 重新加载列表 fetchProducts(); } catch (err) { alert(err.response?.data?.message || "删除失败"); console.error(err); } finally { loadingDelete.value = null; } }; // 组件挂载时加载列表 onMounted(() => { fetchProducts(); }); </script> <style scoped> .product-manager { max-width: 1200px; margin: 20px auto; padding: 0 20px; } .add-form { margin-bottom: 30px; padding: 20px; border: 1px solid #eee; border-radius: 4px; } .form-item { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; font-weight: bold; } input, textarea { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } button { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; } .add-form button { background: #28a745; color: white; } .product-list button { background: #dc3545; color: white; } button:disabled { background: #6c757d; cursor: not-allowed; } table { width: 100%; border-collapse: collapse; margin-top: 10px; } th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; } th { background: #f8f9fa; } </style> 

第七章:总结

        本文从 Django 的视图路由、ORM 高级操作,到 Vue3 的表单交互、异步处理,再到跨域问题解决,完整覆盖了全栈开发的核心环节。通过实战案例,展示了如何从 0 到 1 实现前后端交互功能,关键总结如下:

  1. 视图路由:路径转换器与正则表达式实现灵活 URL 匹配,命名空间解决多 App 路由冲突,装饰器增强视图功能;
  2. ORM 操作:高级查询、批量操作、关联查询优化是提升后端性能的关键,避免 N+1 查询问题;
  3. 前后端交互:Vue3 通过 Axios 发送 JSON / 表单数据,Django 通过request.body/request.POST接收,CORS 配置解决跨域问题;
  4. 异步处理Promise管理异步状态,async/await简化异步代码,静态方法高效处理并行请求。

如果实践操作中遇到其他问题,也可以在评论区留言,Fly帮你在线答疑!!!

Read more

AI时代的内容创作:从代码到认知的范式转移

AI时代的内容创作:从代码到认知的范式转移

✨道路是曲折的,前途是光明的! 📝 专注C/C++、Linux编程与人工智能领域,分享学习笔记! 🌟 感谢各位小伙伴的长期陪伴与支持,欢迎文末添加好友一起交流! * 前言:技术变革的双重面向 * 一、AI内容创作技术架构解析 * 1.1 整体技术流程 * 1.2 核心技术模块 * 二、实战代码:构建轻量级AI创作助手 * 2.1 基础配置类 * 2.2 提示词模板引擎 * 2.3 AI客户端实现 * 2.4 内容创作助手主类 * 2.5 使用示例 * 三、流式响应处理的实现细节 * 3.1 SSE数据解析器 * 四、质量检测与内容优化 * 4.1 简易质量检测实现 * 五、AI时代创作者的成长路径 * 5.1

By Ne0inhk
无线联邦学习:在保护隐私的无线网络中,让AI协同进化

无线联邦学习:在保护隐私的无线网络中,让AI协同进化

🔥作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习 🎬擅长领域:驱动开发,嵌入式软件开发,BSP开发 ❄️作者主页:一个平凡而乐于分享的小比特的个人主页 ✨收录专栏:无线通信技术,本专栏介绍无线通信相关技术 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖 无线联邦学习:在保护隐私的无线网络中,让AI协同进化 一、什么无线联邦学习? 想象这样一个场景:全国各地的医院都想联合训练一个AI模型来诊断疾病,但患者的医疗数据极其敏感,不能离开医院。传统方法是把所有数据集中到一个中心服务器,但这会造成隐私泄露风险。怎么办? 无线联邦学习就像一位“知识快递员”——它不收集原始数据,而是让各地的医院在本地训练模型,然后只把模型“更新心得”(梯度或参数)通过无线网络传给中心服务器,由服务器汇总大家的智慧,形成一个更强大的模型。 核心思想 * 数据不动模型动:原始数据永远留在本地设备 * 仅上传模型更新:只传输学习到的参数,而非数据本身 * 无线传输媒介:通过Wi-Fi、5G等无线网络进行通信 本地设备3 本地设备2 本地设

By Ne0inhk
小白也能玩 OpenClaw?ToDesk AI桌面助手ToClaw 把门槛打到了零

小白也能玩 OpenClaw?ToDesk AI桌面助手ToClaw 把门槛打到了零

一、开篇 最近"小龙虾"彻底火出圈了。打开抖音、刷刷小红书,满屏都是 OpenClaw 的教程、测评和安装实录。更夸张的是,有人专门上门帮人部署,甚至有公司门口排起了长队——就为了装一只"龙虾"。 这波热度不亚于当年 ChatGPT 刚出来的时候。但热闹背后,有一个问题没人说清楚:这么多人在排队,到底在排什么?排的是环境配置、是服务器、是 API Key、是一堆看不懂的命令行。原生 OpenClaw 能力确实强,但它本质上是一个开源框架,想真正跑起来,你得先过技术这关。对普通用户来说,光是部署这一步,就足够劝退了。 所以问题来了——龙虾这么香,普通人就真的没办法吃到吗? 还真不一定。ToDesk 悄悄做了一件事,把这只龙虾"

By Ne0inhk
AI时代人人都是产品经理:落地流程:AI 核心功能,从需求到上线的全流程管控方法

AI时代人人都是产品经理:落地流程:AI 核心功能,从需求到上线的全流程管控方法

AI的普及正在重构产品经理的工作模式——不再依赖传统的跨部门协作瓶颈,AI可以成为产品经理的"全职助手",覆盖需求分析、原型设计、开发协同、测试验证全流程。本文将拆解AI时代产品核心功能从0到1落地的完整管控方法,让你用AI能力提升300%的落地效率。 一、需求阶段:AI辅助的需求挖掘与标准化 需求是产品的起点,AI可以帮你从海量信息中精准定位用户真实需求,避免"伪需求"浪费资源。 1. 需求挖掘:AI辅助用户洞察 传统需求调研依赖问卷、访谈,效率低且样本有限。AI可以通过以下方式快速完成用户洞察: * 结构化处理非结构化数据:用AI分析用户在社交媒体、客服对话、应用评论中的碎片化反馈,自动提炼高频需求点 * 需求优先级排序:基于KANO模型,AI可以自动将需求划分为基础型、期望型、兴奋型、无差异型四类,输出优先级列表 实战工具与示例: 使用GPT-4+Python脚本批量处理应用商店评论: import openai import pandas as

By Ne0inhk