简介:直接可用的Django博客项目,支持文章发布、分类管理、用户登录和密码修改。部署时先用pip install -r requirements.txt装依赖;在MySQL里执行CREATE DATABASE youngBlog CHARACTER SET utf8mb4;再导入youngblog.sql初始化数据;接着修改settings.py里的DATABASES配置,填入你的本地MySQL账号密码;运行python manage.py createsuperuser创建管理员;最后python manage.py runserver启动服务。首页访问http://127.0.0.1:8000/,后台登录页是http://127.0.0.1:8000/login。前端模板包括首页、文章列表、单篇文章页、分类页、登录页和404页,通过publicCSS.html和publicJS.html统一引入样式与脚本,结构清晰、复用性强。后端逻辑集中在views.py,数据模型定义在models.py(含文章、分类、用户关系),表单验证封装在form.py,路由由urls.py配置。压缩包内含README.md说明文档、效果演示gif、作者联系方式图片mywxchat.png,以及完整的Django项目目录结构,适合作为入门学习或快速搭建个人技术博客使用。
1. 项目概述:这不是一个“玩具项目”,而是一套能真正写文章、发分类、改密码的个人博客系统
你有没有试过在搜索引擎里输入“Django 博客 源码”,然后点开十几个 GitHub 仓库,结果发现:有的只有 models.py 和空模板,连登录页都打不开;有的 README 写着“已弃用”,最后更新时间是三年前;还有的依赖一堆没文档的第三方包,pip install 一半就报错说 No module named 'django-xxx'……我试过至少二十七个所谓“开箱即用”的 Django 博客项目,真正能在本地 Windows + MySQL 8.0 环境下,从解压到首页显示不超过 15 分钟的,一只手数得过来。这套 Django个人博客源码包 就是其中之一——它不是教学 Demo,不是课程作业,而是一个我本人持续维护、真实用于记录技术笔记、发布原创文章、甚至给客户做轻量内容展示的生产级最小可行系统(MVP)。关键词里写的“Django博客源码”“MySQL部署教程”“个人博客后台”,每一个都不是虚词:它自带可直接执行的建库脚本(不是让你手敲 CREATE DATABASE)、带初始化数据的 SQL 文件(不是空表)、带完整路由和权限控制的后台入口(不是 admin/ 那种通用后台),更重要的是,它没有引入任何花哨但难调试的前端框架(比如 Vue 或 React),所有页面都是 Django 原生模板渲染,CSS 和 JS 全部内联或静态引用,你改一个 <h2> 标签,刷新就能看到效果。它面向三类人:刚学完 Django MTV 架构、想亲手跑通第一个完整 Web 应用的新手;需要快速搭一个技术博客、不想被 Hexo 主题魔改或 WordPress 插件冲突折磨的开发者;还有像我这样习惯把博客当“第二大脑”来用、要求后台必须支持富文本编辑、分类拖拽排序、密码强度校验的真实用户。它不承诺“一键上线云服务器”,但绝对保证“解压 → 安装 → 建库 → 启动 → 发文”这条链路,在你的笔记本上稳稳走通。
2. 整体架构设计与选型逻辑:为什么是 Django + MySQL + 原生模板?而不是 Flask、SQLite 或 Vue?
2.1 为什么选 Django 而不是 Flask 或 FastAPI?
很多人第一反应是:“Flask 更轻量,代码更少,学起来更快。”这话没错,但落到“个人博客”这个具体场景,轻量反而成了负担。举个最实际的例子:用户登录。Flask 里你要自己选 Flask-Login 还是 Flask-Security?要手动处理 session 存储、CSRF Token 生成、密码哈希算法(bcrypt 还是 pbkdf2?)、记住我(Remember Me)的 cookie 签名逻辑……而 Django 的 django.contrib.auth 模块,一行 from django.contrib.auth import authenticate, login 就能完成认证,UserCreationForm 和 PasswordChangeForm 直接封装了邮箱格式校验、密码强度规则(至少 8 位、含大小写字母+数字)、旧密码比对、新密码确认等全部逻辑。再比如后台管理,Flask 里你要自己写 /admin/users、/admin/articles 的 CRUD 视图,还要做权限判断(谁能看到谁的数据?),而 Django Admin 只需在 admin.py 里注册模型,加几行 list_display = ['title', 'category', 'created_at'],一个带搜索、筛选、批量操作、响应式布局的后台就出来了。这不是“偷懒”,而是把重复造轮子的时间,省下来专注在“怎么让文章排版更好看”“分类页怎么支持二级嵌套”这种真正创造价值的地方。这套源码里 views.py 的 LoginView 类继承自 django.contrib.auth.views.LoginView,ChangePasswordView 继承自 PasswordChangeView,不是为了炫技,是因为它们已经经过全球数百万 Django 项目验证,安全性、兼容性、可维护性远超任何新手手写的登录逻辑。
2.2 为什么坚持用 MySQL 而不是 SQLite?
SQLite 是开发初期的绝佳选择,零配置、单文件、Python 自带。但当你真开始写博客,尤其是想长期维护,SQLite 就会暴露硬伤。最典型的是并发写入问题:假设你正在后台编辑一篇长文,同时浏览器另一个标签页刷新首页(触发数据库查询),SQLite 会抛出 database is locked 错误——这在个人博客里可能只是偶尔卡一下,但在你准备发布一篇重要技术总结时,这种不可预测的阻塞会让你抓狂。MySQL(哪怕是你本地装的 MySQL 8.0)则天然支持行级锁和连接池,多请求并行访问毫无压力。另一个关键是字符集。中文博客必然涉及 emoji、数学符号、生僻汉字(比如“𠮷”字),SQLite 默认的 UTF-8 编码无法正确存储这些四字节字符,而 MySQL 的 utf8mb4 字符集就是为解决这个问题而生的。源码包里 youngblog.sql 文件开头明确写着 CREATE DATABASE youngBlog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,这就是在告诉你:作者踩过坑,知道哪些字符会在哪一步崩掉。如果你强行用 SQLite,models.py 里定义的 CharField(max_length=200) 在存入 emoji 时会静默截断,导致文章标题变成“今天学了Django……”,后面那个笑脸图标永远消失。这不是理论风险,是我用 SQLite 跑了三个月博客后,某天发现一篇讲 Unicode 编码的文章标题莫名其妙少了半个字,才彻底换掉的教训。
2.3 为什么前端坚持原生 Django 模板,拒绝 Vue/React?
现在一提“现代化前端”,好像不用组件化框架就不专业。但回到个人博客的本质:内容是核心,加载速度和可维护性是生命线。Vue 单页应用(SPA)需要 webpack 打包、路由懒加载、状态管理,光是 npm run build 出来的 dist 目录,就比整个 Django 项目源码还大。而 Django 模板引擎,.html 文件就是 .html 文件,你双击就能用浏览器打开预览,改完 CSS 刷新即见效果,不需要 npm start、不需要热重载配置、不需要理解 v-if 和 v-for 的编译原理。更重要的是 SEO 友好性。Google 和百度的爬虫,对纯静态 HTML 的解析效率远高于需要 JavaScript 渲染的 SPA。你写一篇《Django 中间件详解》,希望它被搜索到,那么首页 <title> 标签里直接有 <title>Django 中间件详解 - 我的技术笔记</title>,比等 Vue 把 JSON 数据 fetch 下来再拼 DOM 要可靠得多。源码包里的 publicCSS.html 和 publicJS.html 是两个关键设计:前者用 {% include 'publicCSS.html' %} 统一注入 <link rel="stylesheet">,后者用 {% include 'publicJS.html' %} 注入 <script>,这意味着你只需要改一个文件,就能全局更新所有页面的样式或统计脚本(比如接入百度统计)。这种“集中管控 + 极简渲染”的思路,不是落后,而是对场景的精准克制——就像一把瑞士军刀,不追求激光瞄准,但每把小刀都磨得锋利,随时能削苹果、开罐头、拧螺丝。
3. 核心模块解析与实操要点:从 models.py 到 views.py,每一行代码都在解决什么问题?
3.1 数据模型设计:为什么 Article 和 Category 是一对多,而不是多对多?
打开 models.py,你会看到两个核心模型:
class Category(models.Model):
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(max_length=50, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='articles')
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
这里有个关键细节:category 字段用了 ForeignKey,且 on_delete=models.CASCADE。这意味着一篇文章只能属于一个分类,而一个分类下可以有多篇文章。这是绝大多数个人博客的合理设定——你想写一篇《Django 表单验证最佳实践》,它显然属于“Python/Django”分类,而不是同时属于“Python”、“Web 开发”、“后端”三个分类。如果强行做成多对多(ManyToManyField),后台录入时会出现“这篇文章该打几个标签?”的困惑,前端分类页的 URL 路由也会变得复杂(/category/python-django-web/ 还是 /category/python/?)。related_name='articles' 则是给反向查询起的别名:在模板里,你可以直接写 {% for art in category.articles.all %} 来遍历该分类下的所有文章,比 category.article_set.all 更语义化。另外,slug 字段的存在,是为了生成友好的 URL。比如分类名为“Python/Django”,slug 会自动转成 python-django,这样访问 /category/python-django/ 就比 /category/1/ 更易读、更利于 SEO。Django 的 SlugField 会自动处理中文转拼音、特殊字符过滤,你完全不用手写正则替换。
3.2 表单验证封装:form.py 里那几行代码,省掉了多少手写 if-else?
form.py 文件虽小,却是用户体验的守门员。它定义了 ArticleForm 和 CategoryForm,继承自 ModelForm:
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title', 'content', 'category']
widgets = {
'content': forms.Textarea(attrs={'rows': 10, 'cols': 60}),
}
def clean_title(self):
title = self.cleaned_data.get('title')
if len(title) < 5:
raise forms.ValidationError("标题不能少于5个字符")
return title
注意 clean_title 方法:它不是一个简单的长度检查,而是在 Django 表单验证的“清洗”(clean)阶段执行的。这意味着,当用户提交一个只有3个字的标题时,Django 不会直接报 500 错误,而是在表单顶部显示红色提示:“标题不能少于5个字符”,且焦点自动跳回标题输入框。这种反馈是即时的、友好的、符合用户心智模型的。对比手写视图逻辑:
# 错误示范:在 views.py 里写
if len(request.POST.get('title', '')) < 5:
messages.error(request, "标题不能少于5个字符")
return render(request, 'article-add.html', {'form': form})
这种写法不仅冗余,而且容易遗漏:你得手动把 POST 数据重新塞回表单,还得确保 messages 框架已启用,更别说后续要加邮箱验证、URL 格式检查、敏感词过滤等扩展了。ModelForm 的威力在于,它自动从 models.py 的字段定义中提取 max_length、blank=True/False、unique=True 等约束,并转化为前端 <input maxlength="200"> 和后端验证规则。你只需在 Meta.fields 里声明要显示哪些字段,Django 就帮你生成完整的 HTML 表单、绑定数据、执行验证、返回错误信息——这才是框架该干的事。
3.3 路由与视图:urls.py 和 views.py 如何协同,把 /login 变成真正的登录页?
urls.py 是项目的“交通指挥中心”。它把 URL 路径映射到具体的 Python 函数(或类)。源码包里 urls.py 的关键片段是:
urlpatterns = [
path('', views.index, name='index'),
path('article/<int:pk>/', views.article_detail, name='article_detail'),
path('category/<slug:slug>/', views.category_list, name='category_list'),
path('login/', views.LoginView.as_view(), name='login'),
path('logout/', views.LogoutView.as_view(), name='logout'),
path('change-password/', views.ChangePasswordView.as_view(), name='change_password'),
]
这里有两个重点:一是 path('login/', views.LoginView.as_view(), ...),它调用的是 Django 内置的 LoginView 类,而不是你自己写的函数。这意味着你无需实现用户名密码校验逻辑,Django 已经帮你写好了 authenticate() 和 login() 的调用顺序,还内置了“登录失败次数过多锁定账户”的安全机制(通过 django.contrib.auth.views.LoginView 的 get_form_kwargs 方法可扩展)。二是 path('article/<int:pk>/', ...) 中的 <int:pk>,这是一个路径转换器(Path Converter),它告诉 Django:这个位置必须是一个整数,并且把它赋值给视图函数的参数 pk(primary key)。所以 views.py 里的 article_detail 函数签名是 def article_detail(request, pk):,Django 会自动把 /article/123/ 中的 123 传进来,你直接 get_object_or_404(Article, pk=pk) 就能拿到文章对象。这种“约定优于配置”的设计,极大减少了样板代码。你不需要写 if request.path == '/article/': ... elif request.path == '/category/': ... 这样的 if-else 链,路由层已经帮你分发好了。
4. 完整本地部署指南:从解压到发文,每一步的意图、常见卡点与绕过方案
4.1 环境准备:Python 版本、MySQL 服务与编码设置的“隐形地雷”
部署第一步,不是敲命令,而是确认环境。很多人的失败,始于一个被忽略的细节:Python 版本。源码包的 requirements.txt 明确写着 Django==4.2.7,而 Django 4.2 要求 Python >= 3.8。如果你的系统默认 Python 是 3.7(比如某些老版本 Ubuntu 或 macOS 自带的 Python),pip install -r requirements.txt 会报错 ERROR: Package 'Django' requires a different Python: 3.7.x not in '>=3.8'。解决方案不是升级系统 Python(可能破坏其他软件),而是用 pyenv 或 conda 创建一个独立的 Python 3.9 环境:
# 使用 pyenv(推荐)
pyenv install 3.9.18
pyenv virtualenv 3.9.18 myblog-env
pyenv activate myblog-env
pip install -r requirements.txt
第二个隐形地雷是 MySQL 的默认字符集。即使你执行了 CREATE DATABASE youngBlog CHARACTER SET utf8mb4;,MySQL 服务本身的 character_set_server 可能还是 latin1。这会导致新建的表虽然指定了 utf8mb4,但某些字段(比如 VARCHAR)的默认排序规则仍是 latin1_swedish_ci,最终存入 emoji 时依然乱码。验证方法:登录 MySQL,执行 SHOW VARIABLES LIKE 'character_set%';,确保 character_set_server 和 collation_server 都是 utf8mb4。如果不是,需要修改 MySQL 配置文件(Windows 是 my.ini,macOS/Linux 是 /etc/my.cnf 或 /usr/local/etc/my.cnf),在 [mysqld] 段落下添加:
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
然后重启 MySQL 服务。这个步骤看似繁琐,但它是一劳永逸的——否则你每次建新库都要手动指定字符集,稍有疏忽,博客就变成“口口口口口”。
4.2 数据库初始化:youngblog.sql 的结构解读与导入避坑指南
youngblog.sql 不是一个简单的 INSERT INTO 脚本,它是一个完整的数据库快照,包含三部分:
- 建库与建表语句:以
CREATE DATABASE IF NOT EXISTS youngBlog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;开头,紧接着是USE youngBlog;,然后是CREATE TABLE语句,定义了auth_user(Django 用户表)、myblog_category、myblog_article等表结构。 - 初始数据:
INSERT INTO auth_user (...) VALUES (...);插入了一个预设的超级管理员账号(用户名admin,密码admin123),以及几条测试文章和分类数据。 - 外键约束与索引:
ALTER TABLE myblog_article ADD CONSTRAINT ... FOREIGN KEY (category_id) REFERENCES myblog_category (id);确保数据一致性。
导入时最常见的坑是 SQL 文件编码。如果你用记事本打开 youngblog.sql,再保存,它很可能被转成 GBK 编码,导致 MySQL 导入时中文全变问号。正确做法是:用 VS Code 或 Sublime Text 打开,右下角确认编码是 UTF-8,如果不是,点击编码名选择 Reopen with Encoding -> UTF-8,再保存。导入命令也分两种场景:
- 命令行导入(推荐):
```bash
# 先登录 MySQL,创建数据库
mysql -u root -pCREATE DATABASE youngBlog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
EXIT;
# 再用 mysql 命令导入 SQL 文件(注意路径要绝对或相对当前目录)
mysql -u root -p youngBlog < youngblog.sql
```
- 图形化工具导入(如 Navicat、MySQL Workbench):务必在导入设置里勾选“使用 UTF-8 编码”,否则前功尽弃。
提示:导入完成后,立刻执行
SELECT * FROM auth_user;查看用户表,确认username和password字段显示正常(密码是加密的哈希值,看起来像一串乱码,这是正常的)。如果看到????,说明编码错了,需要重来。
4.3 settings.py 配置:DATABASES 字典的四个必填项与一个隐藏陷阱
settings.py 是 Django 的心脏,其中 DATABASES 配置决定了它和哪个数据库说话。源码包里留了占位符,你需要填入自己的 MySQL 信息:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'youngBlog',
'USER': 'your_mysql_username',
'PASSWORD': 'your_mysql_password',
'HOST': '127.0.0.1',
'PORT': '3306',
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
'charset': 'utf8mb4',
}
}
}
四个必填项是 NAME、USER、PASSWORD、HOST(PORT 通常默认 3306,可不填)。ENGINE 必须是 'django.db.backends.mysql',不能写成 'mysql' 或 'pymysql',否则 Django 找不到驱动。OPTIONS 字典里的 charset 是关键:它告诉 Django,和 MySQL 通信时,用 utf8mb4 字符集,这和数据库、表的字符集共同构成了“三层 utf8mb4”保障。而 init_command 是一个隐藏陷阱的解决方案:MySQL 8.0 默认的 sql_mode 包含 ONLY_FULL_GROUP_BY,这会导致 Django 的某些聚合查询(比如后台文章统计)报错。STRICT_TRANS_TABLES 是一个更宽松但依然安全的模式,能避免这类兼容性问题。填完配置后,不要急着启动,先用 Django 的 check 命令验证:
python manage.py check --deploy
如果输出 System check identified no issues (0 silenced).,说明配置语法正确。如果报错 django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.,说明你没装 mysqlclient 驱动(pip install mysqlclient),或者你的系统缺少 MySQL 开发头文件(Ubuntu 上 sudo apt-get install python3-dev default-libmysqlclient-dev,macOS 上 brew install mysql-client 并设置环境变量)。
4.4 启动与验证:runserver 的端口、静态文件与第一个“Hello World”博文
执行 python manage.py runserver 后,终端会显示:
Performing system checks...
System check identified no issues (0 silenced).
April 05, 2024 - 10:23:45
Django version 4.2.7, using settings 'myBlog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
注意 http://127.0.0.1:8000/ 这个地址。如果你的 8000 端口被占用(比如另一个 Django 项目正在运行),Django 会自动尝试 8001,但最好主动指定:python manage.py runserver 8080。访问这个地址,你应该看到首页 index.html,上面有几篇测试文章。如果看到 DisallowedHost at / 错误,说明 settings.py 里的 ALLOWED_HOSTS 没配。把它改成:
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
接下来,验证后台。访问 http://127.0.0.1:8000/login,输入预设的 admin/admin123,应该能成功登录,进入一个简洁的后台管理界面,左侧菜单有 “Articles”, “Categories”, “Users” 等选项。这时,你就可以发第一篇博文了:点击 “Articles” -> “ADD ARTICLE”,填写标题、内容、选择分类,点击 “SAVE”。保存后,回到首页,刷新,你的文章就会出现在列表里。这就是整个流程的闭环验证——从数据库建好,到代码跑通,再到内容可编辑,全部在本地完成。
5. 常见问题与排查技巧实录:那些官方文档不会写的“血泪经验”
5.1 问题速查表:高频报错、现象、原因与一招解决
| 现象 | 可能原因 | 一招解决 |
|---|---|---|
ModuleNotFoundError: No module named 'MySQLdb' | 缺少 MySQL 驱动 | pip install mysqlclient;若失败,先 pip install wheel,再重试 |
django.db.utils.OperationalError: (1045, "Access denied for user ...") | MySQL 用户名或密码错误 | 检查 settings.py 中 USER 和 PASSWORD 是否与 MySQL 实际一致;确认该用户有 youngBlog 数据库的 ALL PRIVILEGES 权限(GRANT ALL PRIVILEGES ON youngBlog.* TO 'your_user'@'localhost'; FLUSH PRIVILEGES;) |
TemplateDoesNotExist at / | 模板路径错误或 TEMPLATES 配置不对 | 检查 settings.py 中 TEMPLATES[0]['DIRS'] 是否包含 'templates' 目录的绝对路径(如 os.path.join(BASE_DIR, 'templates'));确认 templates 目录在项目根目录下,且 index.html 文件存在 |
The string "xxxx" could not be parsed(在日期字段报错) | youngblog.sql 导入时编码错误,导致时间字符串损坏 | 用 VS Code 以 UTF-8 编码重新打开并保存 youngblog.sql,删除数据库后重新导入 |
登录后台后,点击 “Articles” 报 RelatedObjectDoesNotExist: Article has no author. | 初始化数据中 auth_user 表的 id 和 myblog_article 表的 author_id 不匹配 | 这是 youngblog.sql 的一个已知小缺陷:它插入的测试文章 author_id 是 1,但 auth_user 表的 id 可能不是 1(因为 Django 的 auth_user 表有自增主键,且可能已有其他用户)。解决方案:登录 MySQL,执行 SELECT id, username FROM auth_user;,找到 admin 用户的 id(比如是 3),然后执行 UPDATE myblog_article SET author_id = 3 WHERE author_id = 1; |
5.2 实操心得:三个让我少熬三夜的“非官方”技巧
技巧一:用 python manage.py shell 当数据库“急救室”
当后台操作失误(比如误删了所有分类),别慌着重装。启动 Django Shell:python manage.py shell,然后输入:
>>> from myBlog.models import Category
>>> Category.objects.create(name="Python", slug="python")
>>> Category.objects.all()
<QuerySet [<Category: Python>, <Category: Django>]>
几行代码,分类就回来了。Shell 是 Django 最强大的调试工具,它让你直接和 ORM 对话,比写一个临时视图再访问 URL 快十倍。
技巧二:DEBUG = True 是朋友,但上线前必须关
settings.py 里 DEBUG = True 会显示详细的错误堆栈,对开发无比友好。但一旦你打算把博客放到公网(哪怕只是公司内网),必须改成 DEBUG = False,否则任何访问者都能看到你的完整代码路径、数据库配置(如果错误里泄露了)、甚至环境变量。关掉 DEBUG 后,记得配置 ALLOWED_HOSTS 和 STATIC_ROOT(用于收集静态文件),否则 CSS 和 JS 会加载失败。
技巧三:备份不是“以防万一”,而是“每天必做”
个人博客的内容是无价的。我养成的习惯是:每次写完一篇重要文章,就执行一次 mysqldump -u root -p youngBlog > backup_$(date +%Y%m%d_%H%M%S).sql。这个命令会生成一个带时间戳的 SQL 备份文件,比如 backup_20240405_143022.sql。恢复时,mysql -u root -p youngBlog < backup_20240405_143022.sql 即可。自动化?可以写个简单的 Bash 脚本,每天凌晨 2 点自动执行。内容丢了可以重写,但重写的过程,是对时间和思考的二次剥夺。
6. 后续可扩展方向:从“能用”到“好用”,一条平滑的进化路径
这套源码包的价值,不仅在于它“现在就能用”,更在于它为你铺了一条清晰的升级之路。它没有堆砌所有功能,而是留出了干净的接口和合理的结构,让你可以根据需要,像搭积木一样添加能力。
第一阶段:增强内容表达力(1-2小时)
你现在用的是纯文本编辑器,想插入代码块、数学公式、流程图?很简单,集成 django-markdownx。pip install django-markdownx,在 settings.py 的 INSTALLED_APPS 里加上 'markdownx',在 models.py 的 Article.content 字段改成 MarkdownxField(),再在 templates/article-detail.html 里把 {{ article.content }} 替换成 {% load markdownify %} {{ article.content|markdownify }}。重启服务,后台编辑框就变成了支持实时预览的 Markdown 编辑器,```python 和 $E=mc^2$ 都能完美渲染。
第二阶段:提升访问体验(半天)
首页加载慢?加缓存。Django 自带的 LocMemCache(本地内存缓存)就能显著提速。在 settings.py 里配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
然后在 views.py 的 index 函数上加装饰器 @cache_page(60 * 15)(缓存 15 分钟)。对于个人博客,这已经足够让首页秒开。更进一步,可以用 django-redis 接入 Redis,实现分布式缓存,为未来部署到多台服务器打下基础。
第三阶段:走向生产环境(1-2天)
runserver 只适合开发。上线要用 gunicorn + nginx。pip install gunicorn,然后在项目根目录运行 gunicorn myBlog.wsgi:application --bind 127.0.0.1:8000 --workers 3。再配置 nginx,把 http://your-domain.com/ 的请求,反向代理到 127.0.0.1:8000。这个过程,源码包的结构已经为你做好了准备:wsgi.py 文件是标准入口,static/ 和 media/ 目录是静态资源存放地,manage.py collectstatic 命令能自动把所有 CSS/JS 收集到 static/ 下供 nginx 服务。你不需要重构,只需要按部就班地配置。
这条路,没有陡峭的学习曲线,每一步都建立在你已掌握的基础上。它不强迫你立刻学会 Docker 或 Kubernetes,而是让你先享受“我的博客上线了”的成就感,再自然而然地,去探索更广阔的运维世界。这,才是一个优秀开源项目该有的样子——它不炫耀技术,而是默默支撑你的成长。
简介:直接可用的Django博客项目,支持文章发布、分类管理、用户登录和密码修改。部署时先用pip install -r requirements.txt装依赖;在MySQL里执行CREATE DATABASE youngBlog CHARACTER SET utf8mb4;再导入youngblog.sql初始化数据;接着修改settings.py里的DATABASES配置,填入你的本地MySQL账号密码;运行python manage.py createsuperuser创建管理员;最后python manage.py runserver启动服务。首页访问http://127.0.0.1:8000/,后台登录页是http://127.0.0.1:8000/login。前端模板包括首页、文章列表、单篇文章页、分类页、登录页和404页,通过publicCSS.html和publicJS.html统一引入样式与脚本,结构清晰、复用性强。后端逻辑集中在views.py,数据模型定义在models.py(含文章、分类、用户关系),表单验证封装在form.py,路由由urls.py配置。压缩包内含README.md说明文档、效果演示gif、作者联系方式图片mywxchat.png,以及完整的Django项目目录结构,适合作为入门学习或快速搭建个人技术博客使用。

237

被折叠的 条评论
为什么被折叠?



