恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Python Django小说网站源码全解析:模型、部署与优化

  • 首页
  • 资讯中心
  • /
  • Python Django小说网站源码全解析:模型、部署与优化

相关资讯

三轴写字机实战:基于C#与雷赛运动控制卡的上位机开发与路径规划 2026/8/31 15:19:03
基于机器学习的微博恶意用户识别系统设计与实践 2026/8/31 15:14:03
Hadoop+Spark金融信贷风控系统:从数仓分层到信用评分卡实践 2026/8/31 15:14:03

最新资讯

13、MTK平台功耗--音频子系统电源管理:音频编解码器电源控制、音频路径的功耗优化、VoLTE 通话场景下的功耗管理
MATLAB三维路径规划算法实现:A*与RRT避障路径生成
基于STM32的密码门锁开发实战:状态机与掉电存储
15、MTK平台功耗--Wi-Fi/BT/GPS 功耗管理:Wi-Fi 扫描与连接功耗、BT 低功耗模式(BLE)、GPS 定位功耗优化
Hmmsim Legacy报错闪退?手把手教你清理Add-ons文件夹不兼容线路
网易有道测开笔试全复盘:能力模型、题型解析与备考要点

今日推荐

MCU无DAC如何用定时器+DMA 2D输出高保真任意波形
Cortex-M3 Flash下载失败?从编程错误标志到供电瞬态排查
STM32 TouchGFX屏幕切换Transition优化:原理、配置与排障实战

本周热门

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析
数字电路时序基石:深入理解建立时间与保持时间
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

Python Django小说网站源码全解析:模型、部署与优化

发布时间:2026/8/31 15:19:03
Python Django小说网站源码全解析:模型、部署与优化 简介这是一套基于Python与Django框架开发的完整小说网站项目源码面向计算机专业本科生、Web开发初学者及毕业设计需求者提供可直接运行的小说阅读平台实现方案涵盖用户注册登录、小说分类展示、章节阅读、搜索收藏等核心功能。压缩包共851个文件包含61个Python后端逻辑文件含Django模型、视图与路由、252个JavaScript前端交互脚本、115个Vue组件支持动态渲染与状态管理、69个CSS/SCSS/LESS样式文件及84个PNG等静态资源整体体积达460.8MB结构清晰、模块解耦便于理解MVT架构落地细节。目前已有388人学习下载资源附带完整项目目录、基础部署说明及多端适配样式开箱即用适合用于课程设计实践、求职作品集构建或Django全栈开发能力进阶训练。 在社区分享源码项目的时间不算短了几乎每个月都会碰到有人问起有没有一套比较完整的 Python Django 小说网站源码既能拿来学 Django 的 MVT 流程又真的能在本地跑起来当毕设或者个人站点。问的人多了我也发现一个普遍现象不少人下载的源码要么缺依赖文件要么数据库配置写死要么跑起来之后一堆诡异报错最后只能放弃。所以这次我把自己整理过的一套“基于 Python 和 Django 开发的小说网站项目源码”整个拆开从功能范围、模型设计、核心模块实现到本地部署、生产环境上线、常见坑位一次说清楚。如果你是第一次接触 Django或者正打算用 Django 做一个内容型站点这篇应该能帮你省下不少走弯路的时间。1. 这套小说站源码到底包含哪些能力看一套源码值不值得下载我习惯先不看代码而是先看功能清单和目录结构。功能决定它能拿来干什么目录结构决定它好不好改。这套小说网站项目不是那种“只有一个列表页和详情页”的玩具 Demo而是一个相对完整的在线小说阅读站点前台用户端和后台管理端都有拿来当课程设计、毕业设计或者在此基础上做二次开发都是够用的。先列一下前台的功能清单用户注册、登录、退出支持简单的个人中心小说分类浏览按大类玄幻、都市、历史、科幻等筛选小说列表页支持按点击量、更新时间、收藏数排序小说详情页展示封面、作者、简介、最新章节、总字数全文检索支持按书名、作者名模糊搜索小说阅读页支持分章阅读、上一章/下一章跳转书架功能用户可以收藏/取消收藏小说阅读历史自动记录用户阅读到哪一章下次继续阅读后台管理端的功能也按内容型站点的常规需求做了管理小说分类发布、编辑、下架小说管理小说章节支持批量导入章节内容管理用户、查看用户收藏和阅读记录基础数据统计小说数量、用户数量、章节总数从功能范围就能看出来这套项目覆盖了内容型网站几乎所有核心场景内容建模、列表分页、详情聚合、用户体系、搜索、用户行为记录。这些功能点对应的不是某个单独技术点而是一整套 Django 开发思维——Model 设计、ORM 查询优化、CBV 和 FBV 的取舍、模板继承、分页器、中间件、上下文处理器全都有涉及。再看目录结构我拆包之后大概是这样的novel_site/ ├── manage.py ├── requirements.txt ├── config/ # 项目配置 │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── apps/ │ ├── novels/ # 小说模块 │ ├── users/ # 用户模块 │ └── operations/ # 书架、阅读记录 ├── static/ # 静态文件 ├── media/ # 用户上传/封面 ├── templates/ # 模板文件 │ ├── base.html │ ├── novels/ │ ├── users/ │ └── operations/ └── scripts/ # 辅助脚本 ├── import_novels.py └── generate_data.py把业务放到apps包下按模块拆分的做法是我比较推荐的一种结构。原因很简单Django 的 App 本质上是可复用的业务单元按模块拆分之后后续加新功能只需要在apps下新建一个包不需要改动主工程的 URL 和设置维护成本会低很多。很多新手喜欢把所有 model 堆在同一个 models.py 里项目小的时候没问题到了小说、章节、用户、收藏、历史记录这些实体多起来之后文件一长改起来就非常痛苦。这套源码适合的人群我总结下来有三类。第一类是学完 Django 基础教程、想找一个完整项目巩固 MVT 流程的人第二类是准备做毕设、需要一个能演示能跑通、又不想从零写业务逻辑的学生第三类是有点 Django 基础、想自己搭一个阅读类站点、需要一个干净底子的开发者。当然如果你完全没学过 Python直接拿这套源码硬啃会有点吃力建议先补一下 Python 语法和 Django 基础再回来。2. 技术选型与数据模型设计核心是内容建模一套源码值不值得读最关键的部分其实是数据模型。功能是可以堆的但 Model 设计一旦定下来后面所有业务逻辑都建立在它之上想改就得大动。这套小说网站走的是一条经典的 Django 内容站点路线技术栈不花哨但每个选型都有它的理由。2.1 为什么是 Django 而不是 Flask很多人纠结写内容站到底用 Flask 还是 Django我的看法很直接追求快速交付、需要自带后台、需要完整 ORM 和迁移机制的场景Django 的效率远高于 Flask。Python 后端里Flask 的定位是微框架适合做 API 服务、原型验证但到了内容型网站这种场景用户认证、Admin 后台、ORM、模板引擎、表单处理、分页这些需求几乎一个不少Flask 都需要自己拼装。Django 的哲学是“batteries included”官方已经把常用组件都集成了开发时不需要在每个项目里重新造一遍轮子。国内使用 Django 做内容管理系统、企业信息化后台、数据平台的项目其实一直不少。小说网站这种典型的内容站天然就是 Django 的舒适区——它有自带的 Admin 后台可以直接管理小说和章节有完善的 ORM查询、过滤、聚合都很方便有模板继承机制做整站统一布局非常舒服还有自带的用户认证体系注册登录这些功能不需要从零写。2.2 核心数据模型拆解这套项目的数据模型主要围绕五个实体分类、小说、章节、用户、书架/阅读记录。我直接把核心字段列出来并说明为什么这样设计。先看小说的 Modelfrom django.db import models from django.utils import timezone class Category(models.Model): name models.CharField(分类名, max_length32, uniqueTrue) sort models.IntegerField(排序, default0) class Meta: ordering [sort, id] verbose_name 小说分类 verbose_name_plural verbose_name def __str__(self): return self.name class Novel(models.Model): category models.ForeignKey(Category, on_deletemodels.PROTECT, verbose_name分类) name models.CharField(书名, max_length128, db_indexTrue) author models.CharField(作者, max_length64, db_indexTrue) intro models.TextField(简介, blankTrue) cover models.ImageField(封面, upload_tocovers/, blankTrue, nullTrue) status models.CharField(状态, max_length16, choices((serial, 连载中), (finish, 已完结)), defaultserial) clicks models.PositiveIntegerField(点击量, default0) favorites models.PositiveIntegerField(收藏数, default0) created_at models.DateTimeField(创建时间, defaulttimezone.now) updated_at models.DateTimeField(更新时间, auto_nowTrue) class Meta: ordering [-updated_at] verbose_name 小说 verbose_name_plural verbose_name def __str__(self): return self.name class Chapter(models.Model): novel models.ForeignKey(Novel, on_deletemodels.CASCADE, related_namechapters, verbose_name所属小说) title models.CharField(章节名, max_length128) content models.TextField(正文) volume models.IntegerField(卷, default1) sort models.IntegerField(章节序号, default0) word_count models.PositiveIntegerField(字数, default0) created_at models.DateTimeField(创建时间, defaulttimezone.now) class Meta: ordering [sort, id] unique_together (novel, sort) verbose_name 章节 verbose_name_plural verbose_name def __str__(self): return f{self.novel.name} {self.title}有几个设计点我想专门说明一下这些都是实际开发中容易踩坑的地方第一Novel.category用的是on_deletemodels.PROTECT而不是CASCADE。网上很多教程示例默认 CASCADE但小说分类如果被小说引用一旦删除会用书的记录全部被连带删除这个在内容站里是很危险的操作。用PROTECT之后如果分类下还有小说删除分类会被 Django 拦截能避免很多误操作。业务上需要删除分类时应该先转移或删除分类下的小说。第二Chapter的排序字段用的是sort而不是直接用id自增主键排序。原因很现实章节内容有时候需要调整顺序比如作者插了一章番外如果排序依赖 id插入就会很麻烦有了独立的sort字段改顺序只改数字就行。unique_together (novel, sort)保证同一本小说下章节序号不能重复避免数据错乱。第三Chapter.content直接用了TextField正文很长时这个字段会比较大。MySQL 下它的底层类型是longtext优点是简单直接查一章节就是一次主键查询速度很快。后面我会专门讲这个大字段在部署时的配置坑。再来看用户侧的数据模型from django.contrib.auth.models import AbstractUser class User(AbstractUser): nickname models.CharField(昵称, max_length32, blankTrue) avatar models.ImageField(头像, upload_toavatars/, blankTrue, nullTrue) class Shelf(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, related_nameshelf_items, verbose_name用户) novel models.ForeignKey(Novel, on_deletemodels.CASCADE, related_nameshelf_items, verbose_name小说) created_at models.DateTimeField(加入时间, defaulttimezone.now) class Meta: unique_together (user, novel) verbose_name 书架 verbose_name_plural verbose_name class ReadHistory(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, related_nameread_history, verbose_name用户) novel models.ForeignKey(Novel, on_deletemodels.CASCADE, verbose_name小说) chapter models.ForeignKey(Chapter, on_deletemodels.CASCADE, verbose_name最后阅读章节) read_at models.DateTimeField(阅读时间, auto_nowTrue) class Meta: ordering [-read_at] unique_together (user, novel) verbose_name 阅读记录 verbose_name_plural verbose_name用户模型继承了 Django 自带的AbstractUser扩展了昵称和头像两个字段。这样做的好处是能无缝复用 Django 自带的认证、密码管理、Admin 关联不需要自己写登录注册的核心逻辑。Shelf和ReadHistory都加上了unique_together保证同一个用户对同一本小说只有一条记录。书架去重很合理但阅读历史为什么要唯一因为阅读记录最关心的不是“读过多少次”而是“最后一次读到哪”只保留一条记录、每次阅读时更新章节序号和更新时间查询最新阅读列表会非常高效。2.3 ORM 级联删除和查询边界这套项目很容易遇到的问题就是 ORM 的级联行为。Django 的默认策略是如果一个对象被其他对象通过外键引用删除父对象时子对象的行为取决于on_delete参数。之前说过的PROTECT、CASCADE之外还有一个常用的SET_NULL比如某些评论场景删用户后保留评论但把外键置空。举个具体场景删除一本小说时因为Chapter的novel外键用了CASCADE这本书的所有章节会被自动删除这个符合业务预期。但如果Chapter的novel外键误用了PROTECT那你删除小说时就会报ProtectedError必须先把章节删掉。所以设计 Model 时要想清楚“谁是父、谁是子、删了之后子怎么办”这套关系一旦上线后面再想改迁移就很麻烦。3. 核心功能模块实现书架、阅读记录与搜索方案这部分是整套源码的精华所在。很多 Django 初学者看得懂 Model看得懂视图但一遇到“这个功能具体怎么组织代码”就卡住了。我挑三个有代表性的核心功能展开讲它们几乎覆盖了内容站开发的常用套路。3.1 书架去重、状态判断和性能书架功能最核心的就是两件事判断一本书是否已经被当前用户收藏以及在个人中心展示收藏列表。先看加入收藏的视图逻辑from django.contrib.auth.decorators import login_required from django.http import JsonResponse from django.shortcuts import get_object_or_404 from django.views.decorators.http import require_POST from apps.novels.models import Novel from apps.operations.models import Shelf login_required require_POST def toggle_shelf(request, novel_id): novel get_object_or_404(Novel, pknovel_id) shelf_item, created Shelf.objects.get_or_create( userrequest.user, novelnovel, ) if created: Novel.objects.filter(pknovel.pk).update(favoritesmodels.F(favorites) 1) return JsonResponse({status: added, favorites: novel.favorites 1}) else: shelf_item.delete() Novel.objects.filter(pknovel.pk).update(favoritesmodels.F(favorites) - 1) return JsonResponse({status: removed, favorites: novel.favorites - 1})这里用到了get_or_create它本质上先尝试查一条记录不存在则创建。加了unique_together约束后并发情况下也不会出现重复的收藏记录。favorites字段用F()表达式做原子加减避免多用户同时收藏时读到旧值覆盖新值这个细节在高并发场景下很重要。在模板里判断“这本书是否已加入书架”最直接的做法是给模板传入一个novel_ids集合shelf_novel_ids Shelf.objects.filter(userrequest.user).values_list(novel_id, flatTrue)然后模板里判断{% if novel.id in shelf_novel_ids %}。这里要注意values_list返回的是一个 QuerySet判断是否包含时 Django 会执行一次 SQL但因为它只查novel_id一列且走的是唯一性索引性能完全可以接受。不建议在模板里对每个小说单独执行一次Shelf.objects.filter(userrequest.user, novelnovel).exists()那样会有 N1 查询。3.2 阅读记录断点续读如何实现阅读记录的核心不是“记一条日志”而是“断点续读”。用户今天看到第 100 章明天打开详情页要能直接显示“继续阅读第 101 章”而不是从头找。我的做法是在阅读页渲染的时候同时更新ReadHistorylogin_required def chapter_read(request, novel_id, chapter_id): novel get_object_or_404(Novel, pknovel_id) chapter get_object_or_404(Chapter, pkchapter_id, novelnovel) # 更新阅读记录同一本小说只保留一条指向当前章节 ReadHistory.objects.update_or_create( userrequest.user, novelnovel, defaults{chapter: chapter}, ) next_chapter Chapter.objects.filter(novelnovel, sort__gtchapter.sort).order_by(sort).first() prev_chapter Chapter.objects.filter(novelnovel, sort__ltchapter.sort).order_by(-sort).first() return render(request, novels/chapter_read.html, { novel: novel, chapter: chapter, prev_chapter: prev_chapter, next_chapter: next_chapter, })update_or_create是 Django 里非常实用的一个方法如果存在就更新不存在就创建。配合unique_together可以保证每个用户每本书只有一条阅读历史且一直指向最近阅读的那一章。上一章和下一章用sort字段来查注意两条查询的方向不同一条是“大于当前 sort 的最小值”另一条是“小于当前 sort 的最大值”这个逻辑要记牢。个人中心的“最近阅读”列表直接查ReadHistory并按read_at倒序就行每个小说取最近一条。如果有“展示最近看过的 10 本书”这种需求最省事的办法是查ReadHistory后做去重但为了性能也可以在ReadHistory表里额外存一个冗余的novel_name列表页直接展示冗余字段避免连表查询。不过冗余字段会有数据一致性问题小项目里我更推荐直接查询去重代码简单、出错概率低。3.3 搜索方案从 LIKE 到 Whoosh小说网站最常见的搜索需求是搜书名和作者名。最粗暴但最简单的方案是用 Django ORM 的icontainsfrom django.db.models import Q def search(request): keyword request.GET.get(q, ).strip() if keyword: novels Novel.objects.filter( Q(name__icontainskeyword) | Q(author__icontainskeyword) )[:20] else: novels Novel.objects.none() return render(request, novels/search_result.html, {novels: novels, keyword: keyword})icontains对应的 SQL 是LIKE %keyword%数据量小的时候没问题但小说站的书目量上来之后LIKE %xxx%没法走索引全表扫描会越来越慢。所以这套源码里我做了一个折中搜索走一个独立的search.py模块小数据量默认用数据库查询同时预留了接入 Whoosh 的接口。Whoosh 是纯 Python 实现的全文检索引擎相比 Elasticsearch它不需要额外部署服务非常适合中小型 Django 项目。基本思路是启动时把所有小说或更新时异步触发写入索引搜索时查索引得到 id 列表再回数据库取详情。这套方案对 10 万级书目完全够用部署成本也低。如果你未来书目量涨到百万级再考虑把索引层换成 Elasticsearch那是后话。我个人建议刚开始跑这套源码时先用icontains重点理解业务逻辑确认有性能瓶颈之后再引入 Whoosh。不要在项目一开始就上重搜索方案避免复杂度喧宾夺主。4. 从源码包到本地跑起来部署操作全流程源码下载下来第一件事当然是让它跑起来。我按步骤拆一遍包括环境准备、依赖安装、数据库配置、迁移、初始化数据和启动服务。这套流程我自己在各个环境里跑过很多次按下面顺序操作一般不会出问题。4.1 环境准备与依赖安装建议用 Python 3.8 到 3.11 之间的版本太新的版本偶尔会遇到第三方库还没有预编译 wheel 的问题。先建虚拟环境再装依赖python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install -r requirements.txtrequirements.txt里主要的依赖有这些Django3.2,4.0 PyMySQL1.0.2 Pillow9.0.0 django-redis5.2.0 gunicorn20.1.0PyMySQL是让 Django 连 MySQL 的驱动Pillow是图片处理库django-redis用于缓存和 Session 存储gunicorn是生产环境的 WSGI 服务器。本地调试时可以用 Django 自带的开发服务器但依赖里先装好 Gunicorn后面部署直接用不用再装一遍。4.2 数据库配置与初始化默认配置里数据库用的是 SQLite方便本地零配置启动。如果你要接 MySQL在config/settings.py里改成这样DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: novel_site, USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, CONN_MAX_AGE: 60, } }注意用 PyMySQL 时一般还需要在项目的__init__.py里加一句import pymysql pymysql.install_as_MySQLdb()否则 Django 会去找 MySQLdb而 MySQLdb 在 Python 3 环境下安装比较麻烦。这个细节是初学者最容易忽略的报错往往长这样ModuleNotFoundError: No module named MySQLdb。初始化数据库和创建超级管理员python manage.py makemigrations python manage.py migrate python manage.py createsuperusermakemigrations会扫描 Model 和已有迁移文件的差异生成新的迁移文件migrate把这些迁移应用到数据库。首次执行时Django 会帮我们建好所有内置表auth、sessions、admin 等和业务表。如果migrate时报外键相关错误通常是数据库字符集或引擎问题后面避坑部分会专门讲。4.3 导入测试数据和启动为了让你能看到效果源码包里的scripts/generate_data.py可以生成一批测试小说和章节数据python scripts/generate_data.py --novels 50 --chapters-per-novel 30这个脚本做了几件事创建分类玄幻、都市、历史、科幻等、为每个分类生成随机小说、为每本小说生成指定数量的章节内容。生成的内容是假的但结构是真的足够你体验完整功能。然后启动开发服务器python manage.py runserver 0.0.0.0:8000浏览器打开http://127.0.0.1:8000就能看到小说首页。后台管理地址是http://127.0.0.1:8000/admin/用刚才创建的超级管理员账号登录。4.4 启动后优先检查的三件事第一步跑通之后不要急着改代码先做三个检查第一打开小说详情页看看封面图片能否正常显示。如果显示不了很可能是MEDIA_URL和STATIC_URL没配对。开发环境里 Django 不会帮你托管 media 文件需要在项目的urls.py里临时加一段from django.conf import settings from django.conf.urls.static import static if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)第二注册一个新账号测试收藏和阅读历史功能。如果登录状态失效或者 CSRF 报错检查settings.py里的CSRF_TRUSTED_ORIGINS开发环境下一般是空列表没问题但如果你用了别的主机名访问需要把它加进去。第三清空一次浏览器缓存确认静态文件CSS、JS正常加载。跑起来之后页面光秃秃没有样式99% 是STATIC_URL配置和模板里静态文件引用的路径对不上这个问题在生产部署的踩坑里还会再遇到一次。5. 从本地到线上生产部署和性能优化本地跑通只是第一步。小说网站一旦上线就会面临静态文件托管、并发连接、数据库连接耗尽、缓存策略这些问题。这套源码不只是在本地能跑按照下面的方式部署到云服务器上也能撑住日常流量。5.1 静态文件和媒体文件的处理开发环境下 Django 自己托管静态文件但生产环境绝不能这么做正确姿势是用collectstatic把静态文件收集到一个目录交给 Nginx 托管。python manage.py collectstatic --noinput在settings.py里DEBUG False ALLOWED_HOSTS [your_domain.com, www.your_domain.com] STATIC_URL /static/ STATIC_ROOT BASE_DIR / staticfiles MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / mediaNginx 配置的关键片段location /static/ { alias /path/to/novel_site/staticfiles/; } location /media/ { alias /path/to/novel_site/media/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }这里有个很常见的坑本地DEBUGTrue时媒体文件正常显示一旦切到DEBUGFalseDjango 不再处理媒体文件如果 Nginx 没配好/media/的 alias所有用户上传的封面就会全部 404。所以上线前一定要先检查 Nginx 的媒体文件路由。5.2 Gunicorn 与反向代理WSGI 服务器我选 Gunicorn多进程模型稳定可靠。启动命令建议写成gunicorn config.wsgi:application \ --bind 0.0.0.0:8000 \ --workers 3 \ --threads 2 \ --timeout 60 \ --access-logfile -workers数量没有绝对标准一般建议2 x CPU 核数 1。如果服务器是 2 核3 个 worker 是比较合理的起点。threads可以让每个 worker 内再跑几个线程适合 IO 密集型的 Django 应用。timeout设置成 60 秒防止某些慢接口被 Gunicorn 提前杀掉。还有个容易踩的坑Django 默认每请求创建一个数据库连接流量稍微上来MySQL 的max_connections很容易被打满。所以数据库配置里CONN_MAX_AGE建议设置成 60 到 600 之间的值让连接在进程内复用减少频繁创建和断开连接的开销。但要注意设了CONN_MAX_AGE之后如果 MySQL 的wait_timeout比较短空闲连接会被 MySQL 服务端断开Django 在检测到连接失效后会重连这个机制是自动的只是在日志里偶尔能看到MySQL server has gone away不用慌。5.3 Redis 缓存和页面级缓存小说站的访问热点非常集中首页、分类列表页、小说详情页。这些页面每次请求都查数据库即使加索引QPS 高了之后还是会有压力。我的做法是引入 Redis 做两层缓存。第一层是 Django 的视图缓存。以小说详情页为例一本书的信息基本是固定的只有点击量会变。点击量其实没必要实时写数据库可以先用 Redis 计数定期批量回写。详情页缓存可以设置几分钟的过期时间from django.views.decorators.cache import cache_page urlpatterns [ path(novel/int:pk/, cache_page(60 * 5)(NovelDetailView.as_view()), namenovel_detail), ]第二层是从数据库查询结果缓存。列表页的分页数据比如“玄幻分类按更新时间排序第 3 页”这种查询结果可以缓存 10 分钟左右只要 KEY 设计合理包含分类 id、页码、排序方式就能显著减少数据库压力。Redis 缓存配置CACHES { default: { BACKEND: django_redis.cache.RedisCache, LOCATION: redis://127.0.0.1:6379/1, OPTIONS: { CLIENT_CLASS: django_redis.client.DefaultClient, }, TIMEOUT: 300, } }页面级缓存最适合小说详情、列表这种读多写少的场景。对于阅读页因为每个用户看到的章节内容是一样的也可以做但要注意登录用户的阅读记录还是会实时更新的所以要么把阅读记录接口和阅读页分开要么对未登录用户做缓存、登录用户跳过缓存这个需要根据实际情况取舍。5.4 数据库慢查询和索引优化项目里我提前在几个高频查询字段上加了db_indexTrue小说的name、author章节的sort。这些字段在搜索、排序、翻页时都会用到加了索引之后查询计划会走索引扫描而不是全表扫描。如果你后续自己加字段可以用 Django 的connection.queries或数据库慢查询日志来定位问题。MySQL 下可以开慢查询日志slow_query_log ON slow_query_log_file /var/log/mysql/slow.log long_query_time 1long_query_time设置成 1 秒超过 1 秒的 SQL 会被记录。Django 的 ORM 生成的 SQL 虽然可读性一般但通过日志能大致定位是哪张表、哪个条件的查询慢再针对性加索引或者改写查询方式。6. 实际踩过的坑和二次开发建议最后这部分我把实际使用过程中踩过的一些坑按“问题现象、原因、解决方案”的方式列出来。这些坑不是我编的而是这套源码在多个环境、多个版本组合下真实遇到过的。你如果跑的时候遇到类似问题直接对照排查会快很多。6.1 时区问题发布时间差 8 小时如果你在settings.py里看到USE_TZ True、TIME_ZONE UTC然后发现后台创建的小说时间比本地北京时间慢 8 小时这就是时区配置的典型问题。原因很简单Django 在USE_TZTrue时数据库里存的是 UTC 时间模板渲染时会根据TIME_ZONE转成当地时区。如果TIME_ZONE没设为Asia/Shanghai展示出来的就是 UTC 时间。解决方案USE_TZ True TIME_ZONE Asia/Shanghai注意USE_TZTrue和TIME_ZONE是配合工作的比较稳妥的做法是数据库统一存 UTC展示层统一转北京时间。直接在应用层用datetime.now()拿本地时间反而会在跨时区部署时出问题。6.2 MySQL 保存大章节内容失败小说章节正文是TextField一本书如果章节很多、单章字数很大保存的时候容易遇到Packet too large或者max_allowed_packet相关的报错。这是 MySQL 的一个硬限制默认max_allowed_packet通常是 4MB 或 64MB如果单章内容超过这个值写入就会被拒绝。查一下当前值SHOW VARIABLES LIKE max_allowed_packet;一般建议改成 64MB 或 128MB在 MySQL 配置文件里设置[mysqld] max_allowed_packet 128M character-set-server utf8mb4 collation-server utf8mb4_unicode_ciutf8mb4也值得单独说一句Django 在 MySQL 下默认用的字符集如果不对中文可能存不了生僻字所以连接串里最好显式指定OPTIONS: {charset: utf8mb4}。6.3 collectstatic 后样式丢失本地开发一切正常collectstatic之后部署到服务器页面 CSS 全部失效。这个坑几乎每个人都踩过。排查思路先确认 Nginx 的/static/路径是否能访问到STATIC_ROOT目录下的文件再确认STATIC_ROOT和STATIC_URL是否配对最后看模板里是用{% static css/base.css %}还是硬编码/static/css/base.css前者在STATIC_URL改变时能自动适配后者就是写死路径换配置就失效。6.4 迁移文件冲突这套源码如果被人 fork 之后各自改 Model再合并的时候很容易出现“多个迁移文件修改同一个字段”的冲突。Django 迁移框架能处理简单的依赖但遇到冲突时需要用python manage.py makemigrations --merge生成一个合并迁移。我的建议是如果你在已有数据的基础上改 Model最好不要直接删迁移文件重建。迁移文件一旦应用到数据库就和库里的django_migrations表对应上了删了重建往往会让migrate认为迁移没有执行导致重复建表报错。正确的做法是新建一个迁移文件让它只包含本次的变更。6.5 二次开发建议这套源码最合适的用法是当基底而不是当终点。按我现在做内容站的经验以下几个方向是最常被问到的第一加评论和评分模块。小说站天然需要读者互动评论模块可以复用 Django 自带用户体系新增一个Comment模型关联小说和用户。要注意的是防刷和内容过滤至少做个简单的频率限制比如同一个用户 30 秒内只能评论一次。第二接入第三方登录。当前只支持用户名密码注册对普通用户来说门槛偏高。接入django-allauth是成本很低的做法支持微信、QQ、微博等国内常用方式配置文档也很完善。第三改造成 API 接口供小程序或 App 使用。目前源码是模板渲染如果你想做个小说 App可以只保留 Model 层和业务逻辑用 Django REST Framework 重写视图返回 JSON 数据。开发时注意不要为了 DRF 而 DRF模板渲染和 API 的适用场景不同数据量小、以 Web 为主的话模板渲染反而简单直接。第四部署到 NAS 或内网服务器。现在很多人喜欢在内网 NAS 上跑一个个人阅读站Django 项目完全可以做到。因为 NAS 的 CPU 通常不强部署时可以只跑一个 Gunicorn worker把数据库换成 SQLite 也够用。要留意的是 Python 版本和系统依赖NAS 厂商自带的 Python 环境往往比较旧建议用 Docker 跑把 Python 版本、依赖、代码全部打成镜像迁移起来会省很多心。我实际跑这套源码到现在最大的体会是看源码和学习框架是两回事。源码给你的是完整的上下文从 Model 到 URL 到模板是一个闭环比零散看文档更容易建立全局观。拿到源码之后建议先不要急着跑把models.py从头读一遍理顺表之间的关系再对照页面看每个功能是怎么从数据库里拿数据的。这个过程走完你对 Django 的理解会比刷十篇教程都深。如果你在这个基础上有自己的改造思路照着上面的方向去扩展就行代码结构已经给你留好了余地。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号