☰
旅游推荐系统全栈实战:Python+Django+Vue.js从零到部署
2026/10/14 4:17:10 网站建设 项目流程

做旅行攻略最耗精力的不是研究交通和预算,而是面对几十个景点不知道选哪个。旅游推荐系统要解决的,恰恰就是这个信息过载问题:用户打开系统之后,不再面对一长串冷冰冰的列表,而是拿到一组“大概率符合他口味”的目的地和玩法。最近我把 Python + Django + Vue.js 这套全栈组合跑通了一个完整的旅游推荐系统,从后端的数据模型、推荐算法、接口封装,到前端的页面交互和部署联调,自己走了一遍全链路。这篇文章会把这套系统从零到一拆开细讲,包括每一步的思考过程、实际代码和踩过的坑,适合正在做全栈练习、课程设计或毕业设计,尤其是想动手实现一个“真正能跑”的推荐系统的朋友参考。

1. 项目定位:这个推荐系统到底解决什么问题

1.1 一个核心痛点:攻略很多,决策很难

旅游推荐领域最不缺的就是“数据量”。各类旅游网站上,一个中等城市就能挂出几百条景点、线路和攻略,用户真正需要的不是更多信息,而是更精准的筛选。我在设计模拟项目 X 时,先把目标画得很清楚:系统要帮助用户回答三个问题——现在适合去哪里、这个目的地适不适合自己、同类型用户都去了哪里。

这三个问题决定了推荐系统的功能边界,也直接指导了技术方案。第一个问题需要热门度与季节性排序,第二个问题需要内容特征匹配,第三个问题需要用户行为协同过滤。很多教程一上来就只谈“用协同过滤算相似度”,结果做出一个界面精美但推荐结果毫无逻辑的 demo,问题就出在没想清楚推荐到底要回答什么问题。

1.2 为什么选 Python + Django + Vue.js 这套组合

技术选型方面,我实际对比过几个方案,最终确定 Python + Django + Vue.js,核心原因有三个。

第一,Python 生态对数据处理和算法实现太友好了。推荐算法最常用到的矩阵运算、相似度计算、数据清洗,用 Python 写起来比 Java 或 PHP 少一半代码,尤其是算法迭代阶段,改完代码马上能看到效果,开发体验非常好。

第二,Django 自带 ORM、Admin 后台、认证系统和数据迁移机制,非常适合这种“管理系统 + 推荐接口 + 内容发布”三位一体的项目。旅游推荐系统必然涉及景点信息维护、用户行为记录、后台数据管理,Django 的 Admin 可以直接当运营后台用,省掉单独开发一套管理界面的大量时间。

第三,Vue.js 做前端更适合前后端分离的场景。推荐系统的页面交互集中在列表筛选、详情跳转、收藏点赞这几类操作上,Vue 的组件响应式数据模型可以很好地把页面状态管理和接口数据绑定在一起,不像传统模板渲染那样需要频繁刷新整页。数据加载的体验、筛选交互的流畅度,是推荐系统能否留得住用户的关键要素。

对比下来,如果用 Flask + 原生 JS,项目会自由但是开发量大;如果用全栈框架直接渲染模板,交互体验又不够现代。Django + Vue.js 是这套项目里开发和体验之间的最佳平衡点。

1.3 功能清单与模块边界

拿到项目后的第一件事,不是急着写代码,而是先画出功能边界。我最终锁定了六个核心模块:

  • 用户模块:注册、登录、个人信息维护,使用 JWT 做无状态认证;
  • 景点模块:景点信息录入与展示,支持按省份、城市、类型、价格筛选;
  • 推荐模块:基于内容推荐的相似景点、基于协同过滤的用户个性化推荐;
  • 行为模块:记录浏览、点赞、收藏、评分等行为数据;
  • 搜索模块:景点搜索与关键词联想;
  • 后台管理:通过 Django Admin 管理景点、标签和用户数据。

这个边界的好处是每一部分都能独立完成、独立测试,不会在项目中期陷入“改一处崩三处”的困境。推荐模块是算法核心,但它的数据来源依赖景点模块和行为模块,因此实现顺序上应该先搭好内容数据,再做行为数据,最后把算法架在上面。

2. 推荐算法落地:从模型设计到可运行代码

2.1 数据从哪来:构建景点特征与用户行为矩阵

推荐算法没有数据就是空中楼阁。我在项目里设计了两种数据源:显性数据和隐性数据。

显性数据是用户主动表达的喜好,包括给景点打分、点赞、收藏。这部分数据准确但稀疏,大部分用户不会闲着没事给几十个景点打分。隐性数据则是用户在浏览过程中产生的行为,包括停留时长、点击次数、搜索关键词,这些数据量大但噪声很高。实际项目中我会把显性数据作为主信号,隐性数据用来修正。

景点特征方面,每个景点要有结构化标签。我的原始模型里有这样一组字段:省份、城市、景点类型、游玩时长、最佳季节、价格区间、适合人群。每个字段都是一个特征维度,最后把这些维度编码成一个特征向量。比如某个景区是历史人文类、春秋季适合游玩、价格偏低,程式化表示为向量时,该维度置 1,否则置 0。

用户行为矩阵的记录方式也很关键。我不会直接存一个巨大的二维矩阵,而是在数据库里用一条条行为记录存储,查询时再动态生成矩阵。Django 模型设计上,每一条用户行为记录就是一行数据:user_id、item_id、behavior_type、weight、created_at。点赞权重 1.0,收藏权重 2.0,评分按实际评分值归一化,浏览权重只有 0.3。有了原始记录,就可以在任何时间点重新生成矩阵,也方便做行为衰减。

2.2 基于内容推荐:余弦相似度计算候选集

基于内容的推荐,核心逻辑是“找到与你喜欢过的景点相似的景点”。我这里没有直接用深度学习模型,而是用余弦相似度做特征向量计算,原因有二:一是冷启动阶段没有足够的行为数据,特征相似度仍然有效;二是余弦相似度计算逻辑透明,方便调试和解释。

先看向量化代码:

import math def cosine_similarity(vec_a, vec_b): if len(vec_a) != len(vec_b): return 0.0 dot = sum(x * y for x, y in zip(vec_a, vec_b)) norm_a = math.sqrt(sum(x * x for x in vec_a)) norm_b = math.sqrt(sum(y * y for y in vec_b)) if norm_a == 0 or norm_b == 0: return 0.0 return dot / (norm_a * norm_b)

向量维度怎么设计?我把每个景点转成 10 维特征向量:省份 one-hot 4 维、类型 one-hot 3 维、适合人群 one-hot 3 维。这样两个景点计算相似度时,同一个省份的会加分,同一种类型的会加分,适合人群一致的也会加分。比如“A 湖景区”向量是[1,0,0,0, 0,1,0, 1,0,0],“B 国家公园”是[1,0,0,0, 0,0,1, 1,0,0],代入余弦相似度公式后得到一个 0.5 左右的分数,这个分数就能用来排序候选景点。

这里我建议不要在特征维度中加入太多数值型字段,尤其是价格、评分这类连续值。连续值不归一化会和 one-hot 向量混淆,直接拉低相似度计算的准确性。我的处理方式是把价格分成几档再做 one-hot,比如“低价”“平价”“高端”三个维度,比直接用价格数字靠谱。

2.3 协同过滤:相似用户加权评分与“猜你喜欢”

基于内容的推荐只能找到“和你喜欢过的景点像的景点”,但它发现不了跨类别的兴趣。一个用户既喜欢徒步又喜欢博物馆,如果只做内容推荐,系统会推荐更多徒步地和博物馆,而不会发现另一个用户喜欢的“老街区+茶馆”的休闲路线。协同过滤的作用是挖掘这类隐性的跨域偏好。

协同过滤的核心分两步走。

第一步,构建用户-景点评分矩阵。矩阵行是用户,列是景点,值是用户对景点的综合得分。综合得分 = 点赞得分 + 收藏得分 + 评分得分 + 浏览加权分。其中每种行为要有一个衰减系数,比如行为发生在 30 天前,权重衰减为原来的 0.7,我这边用的是weight * 0.5 ** (days / 30),时间越久影响越小。

第二步,计算用户相似度,用相似用户的评分加权预测当前用户的未评分景点。用户相似度我采用的是皮尔逊相关系数,因为它对评分尺度的偏差不敏感:两个人评分标准不同,一个习惯打高分,一个习惯打低分,皮尔逊系数能消除这种系统性偏差。

预测用户 u 对景点 i 的评分公式:

pred(u, i) = avg_u + sum( sim(u, v) * (r_v_i - avg_v) ) / sum( |sim(u, v)| )

其中avg_u是用户 u 的平均评分,sim(u, v)是用户 u 和 v 的相似度,r_v_i是用户 v 对景点 i 的评分。实际在做的时候,要注意只取相似度大于 0 的用户参与计算,否则一个差异很大的用户会把预测结果拉偏。

有一段简化实现可以放到 Django 的 recommend 模块里:

def predict_score(user_id, item_id): user = User.objects.get(id=user_id) item = ScenicSpot.objects.get(id=item_id) reviews = Behavior.objects.filter(spot=item).exclude(user=user) total_score = 0.0 total_weight = 0.0 for review in reviews: sim = user_similarity(user.id, review.user_id) if sim > 0: total_score += sim * review.normalized_score total_weight += sim if total_weight == 0: return 0.0 return total_score / total_weight

这段伪逻辑在数据量小的时候跑起来没问题,但一旦用户数和行为数过万,全量实时计算就会很吃力。后来的优化我是在 Redis 里缓存了用户相似度矩阵,而不是每次都现场计算。

2.4 冷启动与结果排序:保证新用户也有体验

冷启动是任何推荐系统都绕不开的问题。新用户注册进来,没有任何行为记录,协同过滤直接失效。新景点刚录入,没有任何评分,内容相似度也会因为没有行为数据而缺乏验证。

针对新用户,我的处理策略是“热门 + 内容降权”混合推荐:先把热门景点按浏览量和收藏量排序,取前 50 个作为候选池;再判断新用户注册时有没有选择兴趣标签,如果有,就用兴趣标签做一次内容匹配,把匹配度高的景点提到热门列表前面。注册引导里让用户选兴趣,是实现冷启动推荐性价比最高的一步。

针对新景点,策略是打上足够的特征标签后,直接进入内容相似度候选池。比如新录入一个“老建筑摄影点”,标签里有历史人文、适合摄影、市区,那么它就有机会出现在喜欢历史建筑或者摄影类用户的推荐流里,用户一旦产生行为,后续协同过滤就会自动接手。

排序逻辑也要说明白。算法算出来的是一个候选集,但候选集本身不能直接展示。需要做三件事:第一,过滤掉用户已经点赞或收藏过的景点,避免重复推荐;第二,过滤掉当下不适合游玩的景点,比如夏季推荐温泉、冬季推荐草原,都是很糟糕的体验;第三,对剩余结果做加权排序,最终得分 = 0.6 × 算法预测分 + 0.3 × 热门分 + 0.1 × 新鲜度分。这样既能保证个性化,又不会让推荐结果完全冷冰冰。

3. Django 后端开发:模型、接口与权限设计

3.1 项目结构与数据模型设计

项目结构方面我没有按 Django 的默认单 app 方式来做,而是采用了多 app 分层。一个项目拆成config、apps/user、apps/travel、apps/recommend、apps/interaction五个部分。config放项目配置和根路由;apps/user负责认证和用户资料;apps/travel负责景点、省份、标签管理;apps/recommend负责算法和推荐结果;apps/interaction处理行为记录和收藏。

这样分层的理由是,推荐系统是一个强耦合业务,但每一个关注点应该独立演进。比如景点字段调整时,不希望影响用户模块的代码;算法参数更换时,也不应该去改动景点表的迁移逻辑。

核心模型大致如下:

class Province(models.Model): name = models.CharField(max_length=32, unique=True) class Tag(models.Model): name = models.CharField(max_length=32, unique=True) class ScenicSpot(models.Model): name = models.CharField(max_length=128) province = models.ForeignKey(Province, on_delete=models.CASCADE) city = models.CharField(max_length=64) spot_type = models.CharField(max_length=32, db_index=True) price = models.DecimalField(max_digits=8, decimal_places=2) description = models.TextField() visit_duration = models.CharField(max_length=16) best_season = models.CharField(max_length=32) tags = models.ManyToManyField(Tag) heat = models.IntegerField(default=0) created_at = models.DateTimeField(auto_now_add=True) class Behavior(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) spot = models.ForeignKey(ScenicSpot, on_delete=models.CASCADE) behavior_type = models.CharField(max_length=16, db_index=True) weight = models.FloatField(default=1.0) created_at = models.DateTimeField(auto_now_add=True)

这里我刻意把best_season设计成字符串而不是日期范围,因为“春季”“秋季”这类语义不适合用日期硬编码,提前期、主旺季、延后期这种运营向的表达,用字符串可读性更高。另外,所有表都加了db_index,这套系统核心就是查询和计算,索引带来的性能收益非常明显。

3.2 REST API 设计与推荐接口封装

API 设计遵循 RESTFul 风格,并且把返回结构统一为{ "code": 0, "message": "success", "data": ... }的格式。前端不管成功或失败,都先解这个外壳,再拿数据,处理异常时的逻辑就会非常集中。

我梳理出的主要接口包括:

方法路径功能权限
POST/api/auth/register注册匿名
POST/api/auth/login登录换取 JWT匿名
GET/api/spots景点列表,支持筛选分页登录
GET/api/spots/{id}景点详情登录
GET/api/recommend/for-you个性化推荐列表登录
GET/api/recommend/similar/{id}相似景点推荐登录
POST/api/behaviors上报浏览点赞收藏行为登录
GET/api/favorites收藏列表登录

推荐接口是系统的主接口,我在封装时做了一个“推荐服务层”。视图层只接收用户 ID 和请求参数,然后调用服务层的RecommendService.get_for_user(user_id),服务层内部依次执行:拉取行为数据、计算候选集、过滤已交互景点、排序、组装返回结构。这样一个接口背后的数据处理流程,可以单独单元测试,不影响视图逻辑。

分页参数上,接口默认每页 10 条,支持page和page_size参数。推荐列表的分页有一个特殊设计:因为推荐结果是动态排序的,用户翻页时很容易看到重复项,所以我会在后端缓存整个推荐结果列表的 ID 序列,前端翻页时直接按偏移量取 ID,保证翻页过程中顺序稳定。

3.3 查询优化:避免 N+1 问题

Django 开发中非常常见的一个性能问题是 N+1 查询。景点列表接口里如果简单地ScenicSpot.objects.all(),然后在序列化时再访问spot.province.name和spot.tags.all(),数据库会被反复查询几十次。旅游推荐系统首页一次要返回 20 到 50 个景点,每个景点再来两次额外查询,接口耗时立刻多出几百毫秒。

我的做法是写 ORM 查询时带上select_related和prefetch_related:

spots = ScenicSpot.objects.select_related("province").prefetch_related("tags").order_by("-heat")[:20]

select_related处理外键,适合一对一、多对一关系,它会在 SQL 里直接用 JOIN 把关联表的数据查出来;prefetch_related处理多对多关系,会先查主表,再一次性查询关联表,然后在 Python 内存里完成关联。两者配合,数据库查询次数从几十次降到两三次。

接口返回还要注意一个易错点:如果返回序列化器里嵌套了外键对象,Django REST Framework 默认会把外键序列化成 ID,而不是对象。我这边对景点详情接口使用嵌套序列化器,返回省份名、城市、标签列表,方便前端直接渲染,不额外发请求。

4. Vue.js 前端落地:页面搭建与数据联调

4.1 前端项目骨架与路由划分

前端部分我用的 Vite + Vue 3,整体目录结构是src/views放页面组件、src/components放可复用子组件、src/api放接口请求模块、src/router放路由配置。为什么用 Vite 而不是传统的 Vue CLI?因为 Vite 冷启动速度快、依赖预构建做得好,迭代开发时刷新感明显更好,这对一个要频繁调整推荐展示效果的项目来说非常提升幸福感。

路由按页面划分成四块:首页/、推荐页/recommend、景点详情页/spot/:id、个人中心/profile。首页展示热门景点和搜索入口,用偏向“逛”的心态引导用户;推荐页是核心页,进入时会调用推荐接口拉取个性化列表,并通过卡片瀑布流方式展示。

导航与页面权限用的 Vue Router 守卫。首页对游客开放,但推荐页和个人中心需要登录态,没有 token 的直接重定向到登录页。这个处理要放在路由守卫里做,不能在单个页面里去做判断,否则登录状态过期时每页都要重复处理跳转逻辑。

4.2 请求封装、跨域与状态管理

接口请求我用 axios 统一封装成一个模块。封装时做了三件关键的事:第一,请求统一带上 JWT token,从本地存储读取,存在就放进请求头;第二,响应拦截器统一处理业务码,如果后台返回未登录状态,直接清除本地登录信息并跳转登录页;第三,统一错误提示,网络错误、超时、服务端异常都弹出一个低调的 toast 代替浏览器原生的错误弹窗。

axios 请求模块大概长这样:

import axios from "axios"; const request = axios.create({ baseURL: "/api", timeout: 8000 }); request.interceptors.request.use((config) => { const token = localStorage.getItem("token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); request.interceptors.response.use( (response) => response.data, (error) => { if (error.response && error.response.status === 401) { localStorage.removeItem("token"); window.location.href = "/login"; } return Promise.reject(error); } );

跨域是前后端分离第一天就会遇到的事。本地开发时有两个选择:后端配 CORS,或者前端用代理。我推荐先用代理方式,原因很简单:后端的 Django 不需要额外安装 CORS 库,也不会暴露跨域配置到生产环境。Vite 配置里指定代理:

server: { port: 5173, proxy: { "/api": { target: "http://127.0.0.1:8000", changeOrigin: true } } }

这样前端请求/api/xxx时,Vite 开发服务器会自动把请求转发到 Django 所在的 8000 端口,浏览器视角来看始终是同源请求,完美绕开开发期的跨域问题。生产部署时,再用 Nginx 统一做反向代理,前后端由一个入口向外提供服务。

状态管理方面,这个项目其实不是一个状态非常复杂的系统,我用的轻量方案是局部组件状态加少量全局共享。全局只存一个当前用户信息和一个收藏状态集合,用 Vue 的响应式对象在入口组件里注入即可。不过度引入 Pinia,因为项目功能本身不需要大型中央状态库,强行引入只会增加概念负担。

4.3 关键页面的交互实现与联动

推荐页是这个系统里用户感知最强的部分,交互细节值得认真打磨。推荐页进入时先显示一个骨架屏,数据返回后再渲染卡片列表。卡片展示封面、景点名、类型标签和一个小型热度条。这里我不会一次性加载完所有推荐项,而是用滚动触底加载的方式,每批追加 10 条,这样接口压力分散,用户体验也更加顺滑。

景点详情页里做了两个很有意思的联动。第一个是“相似景点推荐”区块,用户点击某个景点进入详情后,后端返回基于内容相似度的其他景点,前端渲染成横向滑动的卡片列表,点任意卡片进入新的详情页。第二个是收藏联动:点赞和收藏按钮的状态放在全局共享状态里,用户收藏了一个景点,个人中心的收藏列表会立刻更新,不需要重新请求接口。这个联动感受很直接,用户会觉得系统非常“懂”他,也愿意进行更多互动,而互动数据反过来让推荐算法更准,形成正向闭环。

搜索框的联动用了防抖函数。用户输入关键词后不立即发请求,而是等停顿 500ms 才请求搜索接口。这样做能避免每敲一个字符就发一次请求导致的接口压力,搜索结果区也不需要频繁闪烁刷新。

筛选器采用条件组合的方式,省份、价格、类型、适合人群四个维度可以叠加筛选。筛选逻辑我放在前端先对当前加载的数据做过滤,如果候选数据不足,再请求后端接口补充。这种策略让筛选响应很快,同时不会因为筛选维度太多把后端queryset写得过于复杂。

5. 部署上线与问题排查实录

5.1 本地联调与服务器部署两种方案

本地联调时,我同时开两个终端:一个跑 Django,使用 8000 端口;另一个跑 Vite,使用 5173 端口,通过代理打通。第一次跑起来的时候把数据库迁移顺序理清楚非常重要,makemigrations和migrate后立刻创建一个管理员账号,方便直接在 Admin 后台录入第一批测试景点数据。

测试阶段我在后台录入了大约 30 个模拟景点,覆盖历史人文、自然风光、亲子、美食街等不同标签,用两三个测试账号分别点赞、收藏、评分,验证推荐结果是否合理。这个测试过程很直观,比如用 A 账号持续收藏自然风光类景点,推荐页很快就会出现相似类型的未收藏景点,说明内容推荐链路是通的。

上线部署有两种主流方案,我把它们对比了一下:

方案优点缺点适用场景
前端构建产物交给 Django 托管部署简单,一个服务搞定静态文件和业务代码耦合个人项目或低并发场景
Nginx 托管前端静态文件 + 反代后端前后端解耦,性能好需要多维护一个 Nginx 配置正式上线、多用户使用

我生产环境采用的是 Nginx 方案。前端执行npm run build后生成dist目录,放到服务器上的某个静态目录;Django 通过gunicorn启动在本地 8000 端口;Nginx 配置里把/指向前端静态目录,把/api和/admin路径反向代理到127.0.0.1:8000。这套结构一上线就稳定运行了很久,没有出现前后端资源相互干扰的问题。

生产环境还有一个硬性要求,就是 Django 的DEBUG必须设置为False,同时配置好ALLOWED_HOSTS和静态文件收集。不配置这两项,上线后会出现页面能打开但接口 400 的诡异问题,排查起来很费时间。

5.2 常见问题速查表

开发过程中我记录了好几个高频问题,整理成一个排查表:

问题现象可能原因解决办法
前端请求接口报 CORS 错误Django 没开跨域,或代理配置没生效开发期用 Vite proxy,生产期用 Nginx 反代
POST 请求返回 404URL 后面少了斜杠,或路由挂载位置不对Django 默认 require_slash,检查 URL 是否以/结尾
推荐结果直接为空用户没有行为数据,冷启动逻辑未触发检查冷启动分支是否生效,热门兜底列表是否有数据
列表页加载很慢N+1 查询导致数据库压力过大检查 ORM 是否使用 select_related 和 prefetch_related
时区导致行为记录差 8 小时Django 的 TIME_ZONE 与系统时区不一致设置 TIME_ZONE 为当前业务时区,并启用 USE_TZ
前端上传日期字符串后端报错浏览器时区与后端序列化格式不匹配统一使用 ISO 8601 格式字符串,或后端手动解析
Vite 代理失效,请求打到 5173 本身changeOrigin未设为 true在 proxy 配置中加changeOrigin: true
排序后翻页出现重复项每次翻页重新计算排序缓存推荐结果的 ID 序列,按偏移量返回

其中数据量增长后最明显的问题是 N+1 查询。我一开始没有在景点列表的 ORM 上做优化,本地测试 30 条数据完全感觉不到问题,可是把数据量放到几千条以后,接口响应时间从几十毫秒直接涨到接近一秒。加了select_related和prefetch_related之后,性能恢复到几十毫秒级,差距非常明显。

5.3 几点优化建议和踩坑经验

推荐系统上线之后并不是“写完推荐算法就结束了”,它的效果取决于后续的数据闭环。这里有几个我实测下来很有用的优化方向。

第一个是结果缓存。推荐计算结果不需要每一次请求都实时算一遍,尤其是基于协同过滤的推荐,短时间内结果没有变化。我用 Redis 把每个用户的推荐结果缓存 10 分钟,缓存命中时直接返回,不命中再计算结果。这个改动让推荐接口的 P95 响应时间从 800ms 降到了 80ms 左右,效果明显。

第二个是行为数据要设一个过期策略。用户半年前的行为对今天的推荐几乎没意义,我的做法是统计最近 90 天的行为,更早的行为不参与权重计算。实现上可以在查询行为数据时直接加时间过滤条件,而不是在算法层硬算。

第三个是不要迷信模型复杂度。旅游推荐系统在数据量不高的阶段,手写余弦相似度和一个简单的协同过滤公式,效果完全不输现成的推荐算法库。引入复杂模型之前,先确认基础链路已经跑通、数据质量足够好,否则复杂模型只会放大数据噪声。

踩过的坑里,印象最深的是“季节推荐”的实现。一开始我只是单纯用标签匹配,结果用户在冬天看到了大量漂流、水上乐园的推荐,体验非常违和。后来我在排序加权里加入“当前季节是否适合游玩”的分量,只保留真正适合当季的景点,推荐结果质量一下子提升了不少。这类规则不复杂,但对推荐系统实际体验的改善往往是最大的一笔。

最后一个提醒是,Admin 后台要认真配置。往景点表里录入一条数据,要涉及省份、标签、图片、价格等多个字段,如果后台 list_display 和 filter 不配好,测试阶段录入数据会烦躁到怀疑人生。把后台的列表页、筛选器、搜索框都配置好,整个开发效率能提升 30% 以上。

这套系统做下来,我最深的体会是:推荐算法本身只是冰山一角,真正决定项目成败的是数据模型的完整性、接口设计的合理性和前后端联调的细致程度。把 Python 的数据处理能力、Django 的业务建模能力和 Vue.js 的交互表现力放在一条完整链路里,比单点钻研一个算法框架要有价值得多。如果你也准备动手做一个类似项目,建议先把一个最小闭环跑通,再逐步叠加算法和交互,这样每个阶段的成果都能看到、能验证,也更容易坚持下去。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询