☰
基于Django的诗词鉴赏与交流网站设计与实现
2026/10/15 14:11:14 网站建设 项目流程

又到了毕业设计选题的季节,每年我都能收到大量"能不能用Django做一个XX网站"的咨询。今年被问得最多的题目之一,就是"基于Django框架和Python的诗词鉴赏与交流网站的设计与实现"。老实说,这个题目听起来很"老牌",但它覆盖面广、可深可浅,反而是我最愿意展开讲的一个项目——它既能完整展示Django的核心能力,又不必涉及太重的前后端分离架构,做出来的成果可视性还特别强。

这个网站要解决的本质问题其实很简单:让用户能浏览、检索、品味古诗词,同时能围绕某一首诗展开讨论、收藏、互动。它不只是把古诗词"堆"出来给人看的静态站,而是一个有用户体系、内容运营、互动行为的社区型站点。无论你是正在选毕设题目的人,还是Django学完基础想找个完整项目练手的人,这篇文章应该都能帮你少走一些弯路。

1. 先想清楚"诗词鉴赏与交流"要拆成哪些功能

当一个题目落到纸面上,最忌讳的就是直接开写代码。我见过太多人拿到题目后第一件事就是django-admin startproject,结果写到一半发现需求完全没理清,表结构返工了三次。所以第一步踏踏实实做需求拆解,后续才不会翻车。

1.1 内容消费、用户参与、后台管理三块怎么划分

我习惯把这类网站拆成三个子模块来思考。

第一是内容消费模块。诗词列表、诗词详情、按作者筛选、按朝代归类、关键词检索,这些是网站的"地基"。用户进来第一眼要看的是内容,不是你有多厉害的花哨动画。诗词详情页至少要包含原文、作者、朝代、注释、译文、赏析这些字段,其中"赏析"是内容运营的核心,也是区别于普通古诗文仓库的关键点。

第二是用户参与模块。注册、登录、收藏、评论、点赞、浏览历史,这些构成了"交流"的语义。题目里的"交流"最容易被人理解成站内信或者实时聊天室,但实际上最贴合古典文学文化氛围、也最容易落地的是评论区讨论。围绕一首诗展开观点碰撞、补充典故、纠正注释错误,这比做一个没人用的聊天室有价值得多。

第三是后台管理模块。诗词数据从哪来?人工录入几百首没问题,上千首呢?管理员需要一个好用的内容管理界面。Django自带的admin后台在这里能发挥巨大作用,运营人员可以快速录入、批量修改、上下架内容,不需要写一行代码。

1.2 用户角色和核心故事线

我不建议把用户角色写得特别复杂,什么"超级管理员、运营员、审核员、普通用户、游客"五层权限,纯属给自己找麻烦。实际项目里三类角色就够了:

角色能力边界典型使用场景
游客浏览、检索、看评论路过搜一首诗,读一读赏析,觉得不错但不想注册
注册用户在游客基础上增加收藏、评论、点赞、个人中心看到喜欢的诗句,收藏起来,在评论区写下自己的理解
管理员在注册用户基础上管理诗词内容、审核评论、查看数据统计录入新诗词、处理违规评论、看每日活跃数据

用故事线来验证功能是否完整会更直观。假设一位古典文学爱好者打开了网站,他先按朝代筛选出宋词,点进苏轼那首《水调歌头》,读完赏析后在评论区看到一条关于"明月几时有"用典的深度讨论,他觉得很有意思,顺手收藏了这首词,并留下了一条自己的注解。这套流程走通,核心功能的骨架就算立住了。

1.3 划清MVP边界,别被题目"带着跑"

每个巴子题目都恨不得把"大数据、推荐算法、知识图谱"都塞进去,但作为项目设计,第一版永远要先做最小完整闭环。我的建议是:检索、详情、收藏、评论、后台内容管理这五件事是底线。像用户积分、等级勋章、排行榜、每日推荐、个人主页这些属于"锦上添花",有时间再做。还有一点要提醒:如果题目里没有明确写"人工智能""算法推荐",就千万别自作主张加一个基于协同过滤的推荐系统,工作量翻倍而且答辩时容易被问穿。

2. 技术选型的几个现实理由:为什么锁死Django

很多同学喜欢在选型阶段纠结半天,其实这个题目几乎为Django量身定制。我来分析一下为什么。

2.1 和Flask、FastAPI、Node.js对比,Django赢在哪

我做过一个小表格,给周围的朋友做选型参考:

框架上手难度自带功能适合场景本项目关键需求匹配度
Flask低弱,什么都要自己拼轻量API、极简站点需要自己集成ORM、Admin、Auth,中
FastAPI中自动文档、异步高性能API服务前端全分离场景、偏接口,中低
Node/Express中弱,生态靠NPM高并发I/O密集全JS技术栈,重起炉灶,低
Django中高强:ORM、Admin、Auth、中间件内容型网站、管理后台重的系统高

Django对这个题目的匹配度是碾压级的。上来就自带一套完整的用户认证系统,注册、登录、会话管理、密码加密、权限控制全都有了,你只需要做少量的配置和模板适配,不需要从零写登录逻辑。Admin后台更是为"内容运营"而生,诗词数据的增删改查开箱即用。

2.2 自带Admin是"内容型网站"的隐藏王牌

诗词鉴赏网站的本质是"内容驱动",而内容驱动就意味着会有一个人不断往里填数据。Django Admin让内容管理者可以不用面对命令行和SQL语句,直接在网页上以表格形式维护几百首诗词,还能用search_fields快速搜索某个作者的全部作品。对毕设答辩来说,"管理员可通过后台便捷管理上数百条诗词数据"这句话写进论文里极其加分,而且它是真的能演示的,不是画在PPT上的功能。

2.3 MTV结构如何正好对接这个项目

Django的MTV(Model-Template-View)结构听着抽象,放到诗词网站里立刻清晰:Model层对应诗词、用户、评论、收藏这些数据库表;View层负责处理"用户请求了某首诗的详情""用户提交了一条评论"这样的业务逻辑;Template层渲染最终的HTML页面。数据从数据库到页面的流转路径是单向清晰的,这特别适合做一个以CRUD为核心的网站,也方便在论文里画出清晰的架构图和时序图。

3. 把诗词、用户和讨论装进哪几张表:建模与字段设计

数据库设计是整个项目里返工率最高的部分,建表建错了,后面写多少代码都在打补丁。我在这套项目里总共用了六张核心表,这里我一张一张讲清楚设计思路。

3.1 六张核心数据表与字段设计

第一张是用户表。但不要自己建,直接使用Django自带的django.contrib.auth里的User模型,它已经包含了用户名、密码哈希、邮箱、最后登录时间等字段。在它的基础上扩展一个Profile表用来存用户的个性签名、头像、注册时间这些附加信息即可。

第二张是诗词表Poem,这是全站信息密度最高的表。我用的字段大致如下:

class Poem(models.Model): title = models.CharField('标题', max_length=100) author = models.CharField('作者', max_length=50) dynasty = models.CharField('朝代', max_length=20) content = models.TextField('正文') translation = models.TextField('译文', blank=True) appreciation = models.TextField('赏析', blank=True) tags = models.ManyToManyField('Tag', verbose_name='标签', blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['dynasty', 'author', 'id'] indexes = [ models.Index(fields=['author']), models.Index(fields=['dynasty']), ] def __str__(self): return f'{self.title} - {self.author}'

我特意把朝代设成CharField而不是外键到一张朝代表,因为古诗词的朝代就那么几个,比如唐、宋、元、明、清,用外键去关联反而要多做一次表连接,没任何实际收益。标签用了多对多关系,因为一首词可以同时属于"豪放派""咏月""中秋"等多个标签。

第三张是评论区表Comment。关键点是既要把评论关联到诗歌Poem,也要关联到用户User,再带一个content字段和created_at字段。这里有个很常见的设计失误:有人会加一个"评论的评论"的自关联来搞楼中楼,但第一版切不要做这个,做一层平铺评论就够了。

第四张是收藏表Favorite。它本质上是用户和诗词之间的关联表,我通常额外加一个created_at记录收藏时间,用于个人中心按时间排序。

第五张是标签表Tag,字段极简,一个name一个slug就够。

第六张表是浏览记录表ViewLog,这个表可能很多人忽略,但它很有用——用户个人中心的"最近浏览"、"猜你喜欢"功能,以及后台的"内容热度统计"都靠它支撑。

3.2 为什么赏析内容要单独可维护

做内容型网站最容易忽略的一点是:诗词原文是固定的,但赏析是可更新的。同一首《静夜思》,不同读者不同时期理解不同,运营人员可能需要不断优化赏析文本。把appreciation作为Poem表的一个字段,好处是天然自然关联,坏处是每次修改都要更新整条Poem记录。但考虑到项目体量,这个设计完全够用,不必像大型CMS那样把内容拆成版本化存储。我还额外增加了一个is_published布尔字段,用于控制草稿和发布状态——这个字段可能你前期用不上,但内容上千条后你就会感谢它。

3.3 检索字段的隐藏设计

后面要做的全文检索功能,依赖的数据结构在设计阶段就得打算好。直接用content LIKE '%明月%'去查询在数据量小的时候没问题,几百首诗根本感觉不到慢,但数据超过几千条,加上多字段组合筛选,性能就不可控了。我这里采取的做法是:在Poem表上增加两个冗余搜索字段title_search和content_search,在数据录入时自动把对应文本做规范化处理,去掉标点空格、统一简体。这样检索时只需要对这两个字段做icontains匹配,配合数据库的索引,真实体感会比裸查全表好很多。

4. 从空项目到完整跑通:核心功能的实现顺序与代码细节

项目初始化这些基础操作我就不赘述了,无非是建虚拟环境、安装Django、创建项目和app。我更想分享的是按什么顺序去实现功能才能避免反复推倒重来,以及每个模块的关键代码细节。

4.1 开发顺序建议:先内容,再用户,最后互动

我的推荐顺序是:第一步打通诗词列表页和详情页,把纯静态内容先跑起来;第二步接入检索和筛选功能,让用户能找到诗;第三步再做注册登录和评论收藏。这套顺序的核心逻辑是把"内容消费"这条主链路优先打通,用户系统虽然是基础但它是配角,评论收藏又依赖用户系统,放在最后才合理。

词条列表页用Django的ListView非常简单:

# views.py from django.views.generic import ListView from .models import Poem class PoemListView(ListView): model = Poem template_name = 'poem_list.html' context_object_name = 'poems' paginate_by = 20 def get_queryset(self): queryset = super().get_queryset() keyword = self.request.GET.get('kw', '').strip() if keyword: queryset = queryset.filter( Q(title_search__icontains=keyword) | Q(content_search__icontains=keyword) | Q(author__icontains=keyword) ) dynasty = self.request.GET.get('dynasty', '') if dynasty: queryset = queryset.filter(dynasty=dynasty) return queryset

这样一次搞定关键词搜索和朝代筛选,配合模板里的表单即可。分页组件我用的是Django内置的Paginator,模板里手动渲染上一页下一页即可,这里不再赘述。

4.2 注册登录与评论:Django Auth的正确打开方式

注册登录直接用django.contrib.auth的login、logout、authenticate三个函数就够了。需要注意的一个细节是注册成功后要自动登录,否则用户刚注册完还要再输一遍密码,体验极差。注册的视图逻辑大概是:

from django.contrib.auth.models import User from django.contrib.auth import login, authenticate def register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') password2 = request.POST.get('password2') if password != password2: # 提示两次密码不一致 ... user = User.objects.create_user(username=username, password=password) auth_user = authenticate(username=username, password=password) login(request, auth_user) return redirect('home')

提交评论时要判断用户是否登录,未登录则跳转到登录页,登录后才允许写评论。评论区展示时注意一次查询把评论和用户信息联查出来,避免在模板循环里逐条查询用户表造成N+1问题。我一般会这样优化:

comments = Comment.objects.filter(poem=poem).select_related('user')

4.3 收藏功能的幂等设计

收藏是个典型的"点两次就是取消"的交互。用户第一次点收藏,Insert一条记录,第二次再点同样的位置,应该Delete这条记录而不是再插一条。数据库层面用UniqueConstraint约束(user, poem)唯一,视图层先判断记录是否存在,存在就删,不存在就建:

from django.db.models import UniqueConstraint class Favorite(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户') poem = models.ForeignKey(Poem, on_delete=models.CASCADE, verbose_name='诗词') created_at = models.DateTimeField(auto_now_add=True) class Meta: constraints = [ UniqueConstraint(fields=['user', 'poem'], name='unique_user_poem') ]

4.4 Admin后台定制:让运营者友好录入诗词

Admin后台的定制是这个项目的重头戏。我的做法是在admin.py里把常用查询字段全部暴露出来:

from django.contrib import admin from .models import Poem @admin.register(Poem) class PoemAdmin(admin.ModelAdmin): list_display = ('title', 'author', 'dynasty', 'created_at', 'updated_at') list_filter = ('dynasty',) search_fields = ('title', 'author', 'content') list_per_page = 50

这样管理者在后台直接按朝代筛选,通过搜索框快速定位某位作者的全部作品,点进去就能编辑。数据录入的效率比直接操作数据库不知道高到哪里去了。这里强烈建议配一个批量导入脚本,可以把Excel表里的诗词数据读出来转成Poem实例,几百条数据几分钟就能倒进去。

5. 我在这套网站上踩过的坑和绕过的弯

任何项目跑通后回头整理一遍踩坑记录,都是价值最高的部分。我把这几个坑记录下来,希望你能直接绕开。

5.1 中文搜索体验稀碎:分词问题比想象中严重

第一次做搜索功能时我天真地以为用icontains加数据库索引就够了。实际测试时输入"月"匹配没问题,但输入"明月"之后,一些内容是"月明"或中间插入了其他字的诗词就匹配不到了,更别提一些译文里包含异体字的情况。后来我做了一个折衷方案:在录入时把诗词正文做了清洗,去掉所有标点符号,并在数据库层面对content_search建立索引,检索时同样先把输入的关键词做清洗,再去匹配。这样虽然不解决语义搜索这种高级问题,但传统文学网站最常见的"按关键词找诗句"场景已经够用。

5.2 分页时查询参数丢失

列表页如果同时有kw=明月和dynasty=宋两个参数,翻到第二页时只保留了page=2,关键词和朝代筛选全部丢失,点击下一页看到的是没有过滤的完整列表。这个bug很隐蔽,因为它只在"筛选状态+翻页"同时发生时出现。问题根源在于模板里分页链接生成时没有把现有query参数拼接进去。解决方案是视图中打包参数时把当前请求的所有GET参数带上,页面URL长一点没关系,功能要正确。

5.3 静态文件404的经典陷阱

开发环境一切正常,一部署就发现CSS全没了。问题十有八九在DEBUG=False时Django默认不再帮你提供静态文件服务。所以部署前必须收集静态文件,配置STATIC_ROOT、STATIC_URL,再让Nginx去托管静态目录,这是一个此前没跑过正式环境的人一定会踩的坑。还有一个不那么明显的问题:在开发阶段用runserver时,如果访问的是非根路径,模板里用绝对路径/static/css/style.css没问题,但用了相对路径就可能导致找不到资源,所以模板里写静态资源地址一定要用{% static %}标签或从根路径开始写。

5.4 数据导入时的不规范内容

从网上下载的古诗词数据集质量参差不齐,有的有错别字,有的空行没清理,有的作者名带了奇怪的全角空格。我在写导入脚本时加入了校验:空字段自动跳过,标题里包含换行符自动清除,朝代字段不在白名单里的归为"其他"。另外设置了一个--dry-run参数,可以先预跑一遍输出统计信息,确认无误后再真正写入数据库。这些细节听起来平平无奇,但确实救过我一次——有一批数据里将近三分之一带有导出工具生成的冗余日志文字,差点污染了整个库。

6. 本地能跑不等于能上线:部署与安全检查清单

把项目从开发机搬到服务器,往往是整个项目环节里被最多人忽略、也最容易出问题的环节。我把部署相关的思考压缩成一份可以直接照做的清单。

6.1 settings配置的环境分离

我强烈建议从一开始就把settings.py拆成base.py、dev.py、prod.py三个文件。base.py放公共配置,dev.py放调试期配置,prod.py专门放生产环境的SECRET_KEY、ALLOWED_HOSTS、DEBUG = False这些。不要觉得麻烦,等你上线部署时就知道有多省心。尤其是数据库密码和SECRET_KEY这种敏感信息,务必通过环境变量注入,不能让它们以明文形式出现在代码里。曾经有大量线上项目因为SECRET_KEY泄漏导致会话被伪造,这个教训在安全领域反复被提起。

6.2 gunicorn加Nginx的经典组合

这是目前最成熟稳定的部署姿势。业务代码由Gunicorn作为WSGI服务器托管,Nginx负责反向代理和静态文件托管。我没有采用Windows服务器上常用的等待Perl之类的方案,因为那是把方钉子往圆洞里砸。Nginx配置文件要关注两个关键点:第一是把/static/和/media/两个路径直接映射到磁盘目录,减少对Python进程的请求压力;第二是配置合理的超时参数,proxy_read_timeout如果设太短,用户提交长评论时可能超时返回502。

6.3 登录安全与评论内容清洗

上线环境与开发环境最大的差异是暴露在公网上,会被各种扫描工具扫。我做的几件最基础但有效的事是:登录接口限制频率,防止暴力猜密码;评论提交时过滤掉带<script>标签的内容,用bleach这类库做标签白名单清洗,防止存储型XSS;Admin后台路径从默认的/admin/改成语义无关的路径,虽然不能防内鬼,但能过滤掉绝大多数自动化扫描流量。

6.4 上线前的自检清单

我把每次部署前都会过一遍的检查事项列在下面:

检查项操作达标标准
DEBUG开关确认prod.py中为False不再显示详细报错堆栈
ALLOWED_HOSTS填入域名/IP不能漏配导致403
静态文件运行collectstatic并确认Nginx可访问页面无样式丢失
数据库备份生产库手动dump一份文件可正常恢复
定时任务如果有内容更新脚本,确认Celery或cron已配置任务能自动执行
日志确认Django日志已输出到文件出错时有迹可循

最后聊几句

做完这个项目我最大的体会是,Django这种"大而全"的框架其实特别适合练手和做毕设,它把很多你不想操心的脏活累活都预先做好了,你反而能把精力集中在业务设计和交互体验上。回顾整个开发过程,我自己收获最大的不是写了多少行代码,而是经历了从需求拆解、建模、编码、测试、部署这一整条完整的工程链路。

一个小建议给正在做类似网站的朋友:数据质量比功能数量更重要。哪怕你的功能只有诗词浏览和评论区,只要选的诗、赏析文本、作者信息都准确规范,用户打开后觉得内容可靠,你这个网站就已经赢过了八成功能花哨但内容粗制滥造的项目。先通过Admin后台认认真真录入三五十首打磨过的诗词,再开始调前端样式,你会发现整个系统的"质感"立刻不一样。

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

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

立即咨询