快速搭建Django博客系统
一、在 D 盘创建项目目录(手动/终端均可)
方式 1:手动操作
- 打开「此电脑」→ 进入 D 盘;
- 右键 → 新建文件夹 → 命名为
djangoblog;
方式 2:终端操作(推荐)
运行
# 打开CMD/PowerShell,执行:
d:
mkdir djangoblog
cd djangoblog
二、创建虚拟环境 + 安装 Django
运行
# 1. 创建虚拟环境
python -m venv venv
# 2. 激活虚拟环境(Windows)
venv\Scripts\activate
# 3. 安装Django(5.2.x版本)
pip install django==5.2.12
# 4. 验证 Django 是否安装成功
python -m django --version


三、新建 Django 项目 + 应用
运行
# 1. 新建项目(注意末尾的点,代表当前目录)
django-admin startproject djangoblog .
# 2. 新建blog应用
python manage.py startapp blog
四、配置
以下为所有核心文件的完整版代码
1. 项目配置文件(替换原有内容)
(1) D:\djangoblog\djangoblog\settings.py
运行
"""
Django settings for djangoblog project.
Generated by 'django-admin startproject' using Django 5.2.12.
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
SECRET_KEY = "django-insecure-nt*wq@1po!-jh944(%fpl9cas5m!_$hkwtnw7i6fq3rv6k&w)k"
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # 注册博客应用
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = "djangoblog.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "djangoblog.wsgi.application"
# Database
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
LANGUAGE_CODE = "zh-hans"
TIME_ZONE = "Asia/Shanghai"
USE_I18N = True
USE_TZ = True
# Static files
STATIC_URL = "static/"
# Default primary key field type
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
(2)D:\djangoblog\djangoblog\urls.py
运行
"""djangoblog URL Configuration"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')), # 博客首页路由
]
2. 博客应用文件(替换 / 新建)
(1)D:\djangoblog\blog\models.py
运行
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
# 分类模型
class Category(models.Model):
name = models.CharField('分类名', max_length=100)
parent = models.ForeignKey(
'self',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='children',
verbose_name='父分类'
)
class Meta:
verbose_name = '分类'
verbose_name_plural = '分类'
ordering = ['id']
def __str__(self):
return self.name
# 标签模型
class Tag(models.Model):
name = models.CharField('标签名', max_length=50, unique=True)
class Meta:
verbose_name = '标签'
verbose_name_plural = '标签'
ordering = ['id']
def __str__(self):
return self.name
# 文章模型
class Post(models.Model):
title = models.CharField('标题', max_length=200, unique=True)
content = models.TextField('内容')
excerpt = models.CharField('摘要', max_length=200, blank=True, help_text='可选,不填则自动截取内容前200字')
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name='posts',
verbose_name='分类'
)
tags = models.ManyToManyField(
Tag,
blank=True,
related_name='posts',
verbose_name='标签'
)
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='posts',
verbose_name='作者'
)
created_time = models.DateTimeField('创建时间', default=timezone.now)
updated_time = models.DateTimeField('更新时间', auto_now=True)
views = models.PositiveIntegerField('浏览量', default=0, editable=False)
class Meta:
verbose_name = '文章'
verbose_name_plural = '文章'
ordering = ['-created_time']
def __str__(self):
return self.title
# 自动生成摘要
def save(self, *args, **kwargs):
if not self.excerpt:
self.excerpt = self.content[:200].replace('\n', ' ').replace('\r', ' ')
super().save(*args, **kwargs)
# 增加浏览量
def increase_views(self):
self.views += 1
self.save(update_fields=['views'])
(2) D:\djangoblog\blog\admin.py
运行
from django.contrib import admin
from .models import Post, Category, Tag
# 自定义分类Admin
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'parent', 'id']
search_fields = ['name']
list_per_page = 10
# 自定义标签Admin
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ['name', 'id']
search_fields = ['name']
list_per_page = 10
# 自定义文章Admin
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'category', 'author', 'created_time', 'updated_time', 'views', 'id']
list_filter = ['category', 'tags', 'created_time']
search_fields = ['title', 'content']
autocomplete_fields = ['tags']
readonly_fields = ['updated_time', 'views']
fieldsets = [
('基础信息', {'fields': ['title', 'content', 'excerpt']}),
('分类与标签', {'fields': ['category', 'tags']}),
('作者与时间', {'fields': ['author', 'created_time', 'updated_time', 'views'], 'classes': ['collapse']})
]
list_per_page = 10
(3)D:\djangoblog\blog\views.py
运行
from django.views.generic import ListView, DetailView
from .models import Post, Category, Tag
# 博客首页(文章列表)
class IndexView(ListView):
model = Post
template_name = 'blog/index.html'
context_object_name = 'posts'
paginate_by = 10
def get_context_data(self, **kwargs):
context = super().get_context_data(** kwargs)
context['categories'] = Category.objects.all()
context['recent_posts'] = Post.objects.all()[:10]
context['hot_posts'] = Post.objects.all().order_by('-views')[:10]
return context
# 文章详情页
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post.html'
context_object_name = 'post'
def get_object(self, queryset=None):
obj = super().get_object(queryset)
obj.increase_views()
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(** kwargs)
context['categories'] = Category.objects.all()
context['recent_posts'] = Post.objects.all()[:10]
context['hot_posts'] = Post.objects.all().order_by('-views')[:10]
return context
(4) 新建 D:\djangoblog\blog\urls.py
运行
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('post/<int:pk>/', views.PostDetailView.as_view(), name='detail'),
]
3. 模板文件(全部新建)
(1) D:\djangoblog\blog\templates\blog\base.html
预览
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Django博客{% endblock %}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: "Microsoft YaHei", sans-serif; color: #333; line-height: 1.6; background-color: #f8f9fa; }
a { text-decoration: none; color: #2385bb; transition: color 0.2s; }
a:hover { color: #1a6899; }
ul { list-style: none; }
.container { max-width: 1200px; margin: 0 auto; padding: 0 20px; }
/* 头部样式 */
header { background-color: #fff; border-bottom: 1px solid #eee; padding: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
.header-content { display: flex; flex-direction: column; gap: 10px; }
.blog-title { font-size: 28px; font-weight: bold; color: #333; }
.blog-desc { font-size: 14px; color: #666; }
.nav { margin-top: 10px; }
.nav a { margin-right: 25px; font-size: 15px; color: #666; }
.nav a:hover { color: #2385bb; }
/* 主体内容区 */
.main-content { display: flex; gap: 30px; margin: 30px 0; }
.main { flex: 2; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
.sidebar { flex: 1; display: flex; flex-direction: column; gap: 20px; }
.sidebar-box { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
.sidebar-title { font-size: 16px; font-weight: bold; color: #666; padding-bottom: 10px; border-bottom: 1px solid #eee; margin-bottom: 15px; }
.sidebar-list li { margin-bottom: 10px; font-size: 14px; }
.sidebar-list a { color: #666; }
.sidebar-list a:hover { color: #2385bb; text-decoration: underline; }
/* 文章列表样式 */
.post-item { margin-bottom: 30px; padding-bottom: 20px; border-bottom: 1px solid #eee; }
.post-item:last-child { border-bottom: none; }
.post-title { font-size: 20px; margin-bottom: 10px; }
.post-comment { font-size: 13px; color: #999; margin-bottom: 8px; }
.post-excerpt { font-size: 14px; color: #666; margin-bottom: 10px; line-height: 1.8; }
.post-meta { font-size: 12px; color: #999; }
/* 详情页样式 */
.post-detail h1 { font-size: 24px; margin-bottom: 20px; color: #333; border-bottom: 1px solid #eee; padding-bottom: 10px; }
.post-detail-meta { font-size: 13px; color: #666; margin-bottom: 20px; }
.post-content { font-size: 15px; color: #333; line-height: 1.8; }
.post-content p { margin-bottom: 15px; }
/* 空数据样式 */
.empty-tip { text-align: center; padding: 50px 0; color: #999; font-size: 16px; }
</style>
</head>
<body>
<header>
<div class="container header-content">
<h1 class="blog-title">djangoblog</h1>
<p class="blog-desc">基于Django的极简博客系统</p>
<nav class="nav">
<a href="{% url 'blog:index' %}">首页</a>
<a href="#">我是父类目</a>
<a href="#">文章归档</a>
</nav>
</div>
</header>
<div class="container main-content">
<div class="main">
{% block content %}{% endblock %}
</div>
<div class="sidebar">
<!-- 热门文章 -->
<div class="sidebar-box">
<h3 class="sidebar-title">VIEWS</h3>
<ul class="sidebar-list">
{% for post in hot_posts %}
<li><a href="{% url 'blog:detail' post.pk %}">{{ post.title }} - {{ post.views }} views</a></li>
{% empty %}
<li>暂无热门文章</li>
{% endfor %}
</ul>
</div>
<!-- 分类目录 -->
<div class="sidebar-box">
<h3 class="sidebar-title">分类目录</h3>
<ul class="sidebar-list">
{% for cat in categories %}
<li><a href="#">{{ cat.name }}</a></li>
{% empty %}
<li>暂无分类</li>
{% endfor %}
</ul>
</div>
<!-- 近期文章 -->
<div class="sidebar-box">
<h3 class="sidebar-title">近期文章</h3>
<ul class="sidebar-list">
{% for post in recent_posts %}
<li><a href="{% url 'blog:detail' post.pk %}">{{ post.title }}</a></li>
{% empty %}
<li>暂无近期文章</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</body>
</html>
(2) D:\djangoblog\blog\templates\blog\index.html
预览
{% extends 'blog/base.html' %}
{% block title %}djangoblog - 首页{% endblock %}
{% block content %}
{% for post in posts %}
<div class="post-item">
<h2 class="post-title"><a href="{% url 'blog:detail' post.pk %}">{{ post.title }}</a></h2>
<p class="post-comment">评论</p>
<p class="post-excerpt">{{ post.excerpt }}</p>
<a href="{% url 'blog:detail' post.pk %}">Read more</a>
<div class="post-meta">
发布于 {{ post.category.name }} 并标记为
{% for tag in post.tags.all %}
{{ tag.name }}{% if not forloop.last %}, {% endif %}
{% empty %}
无标签
{% endfor %}
由 {{ post.author.username }} 在 {{ post.created_time|date:"Y-m-d" }}
</div>
</div>
{% empty %}
<div class="empty-tip">
暂无文章,快去Admin后台添加吧!<br>
<a href="{% url 'admin:index' %}" style="color: #2385bb;">点击进入Admin后台</a>
</div>
{% endfor %}
{% endblock %}
(3) D:\djangoblog\blog\templates\blog\post.html
预览
{% extends 'blog/base.html' %}
{% block title %}{{ post.title }} - djangoblog{% endblock %}
{% block content %}
<div class="post-detail">
<h1>{{ post.title }}</h1>
<div class="post-detail-meta">
分类:{{ post.category.name }} |
标签:{% for tag in post.tags.all %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% empty %}无标签{% endfor %} |
作者:{{ post.author.username }} |
发布时间:{{ post.created_time|date:"Y-m-d H:i" }} |
最后更新:{{ post.updated_time|date:"Y-m-d H:i" }} |
浏览量:{{ post.views }}
</div>
<div class="post-content">
{{ post.content|linebreaksbr }}
</div>
</div>
{% endblock %}
五、🚀执行项目初始化命令(按顺序)
运行
# 1. 确保在D:\djangoblog目录下,且虚拟环境已激活
d:
cd djangoblog
venv\Scripts\activate
# 2. 生成数据库迁移文件
python manage.py makemigrations
# 3. 执行数据库迁移
python manage.py migrate
# 4. 创建超级用户(按提示输入用户名、密码)
python manage.py createsuperuser
# 5. 启动开发服务器
python manage.py runserver
六、访问与测试
Admin 后台添加数据:访问 http://127.0.0.1:8000/admin/,登录超级用户:
- 先添加「分类」「标签」;
- 再添加「文章」(选择分类、标签、作者);



访问博客首页:http://127.0.0.1:8000/

访问文章详情:点击文章标题 → 进入详情页,浏览量自动 + 1。







🌟 完整目录结构(最终)
D:\djangoblog/ ← 项目根目录
├── venv/ # 虚拟环境目录(自动生成)
│ ├── Include/
│ ├── Lib/
│ ├── Scripts/
│ └── pyvenv.cfg
├── djangoblog/ # 项目配置目录(startproject生成)
│ ├── __init__.py # 空文件,标识Python包
│ ├── asgi.py # ASGI配置(默认生成,无需修改)
│ ├── settings.py # 项目核心配置(已提供完整版代码)
│ ├── urls.py # 项目级路由(已提供完整版代码)
│ └── wsgi.py # WSGI配置(默认生成,无需修改)
├── blog/ # 博客应用目录(startapp生成)
│ ├── __init__.py # 空文件,标识Python包
│ ├── admin.py # Admin后台配置(已提供完整版代码)
│ ├── apps.py # 应用配置(默认生成,无需修改)
│ ├── migrations/ # 数据库迁移文件(执行makemigrations后自动生成)
│ │ ├── __init__.py
│ │ └── 0001_initial.py # 模型迁移文件(自动生成)
│ ├── models.py # 数据模型(已提供完整版代码)
│ ├── tests.py # 测试文件(默认生成,可忽略)
│ ├── urls.py # 应用级路由(需新建,已提供代码)
│ ├── views.py # 视图逻辑(已提供完整版代码)
│ └── templates/ # 模板目录(需手动创建)
│ └── blog/ # 应用专属模板子目录(避免命名冲突)
│ ├── base.html # 基础布局模板(已提供完整版代码)
│ ├── index.html # 博客首页模板(已提供完整版代码)
│ └── post.html # 文章详情模板(已提供完整版代码)
├── db.sqlite3 # SQLite数据库文件(执行migrate后自动生成)
└── manage.py # Django管理脚本(startproject自动生成)
七、📌 总结(项目整体概述)
DjangoBlog 是基于 Django 5.2 开发的极简博客系统,实现了博客核心功能:文章发布 / 管理、分类 / 标签体系、文章详情展示、浏览量统计、侧边栏热门 / 近期文章展示等。项目遵循 Django 经典的 MVT(Model-View-Template)设计模式,结构清晰、易扩展,适合 Django 初学者学习和二次开发。
1. MVT 架构拆解(核心设计)
Django 摒弃了传统 MVC 的命名,采用 MVT 架构,三者分工明确:
(1)Model(模型):数据层(blog/models.py)
作用:定义数据结构,负责与数据库交互(增删改查),是整个项目的「数据基石」。
核心模型及功能:
Category(分类):管理博客文章分类,支持多级分类(父分类关联);Tag(标签):为文章添加标签,实现内容多维度归类;Post(文章):核心模型,包含标题、内容、摘要、分类、标签、作者、发布时间、浏览量等字段;- 自动生成摘要:未填写摘要时,截取内容前 200 字;
- 浏览量统计:访问详情页时自动 + 1;
- 按发布时间倒序排序:最新文章优先展示。
关键特性:
- 基于 Django ORM 实现数据库操作,无需手写 SQL;
- 通过
ForeignKey/ManyToManyField实现模型关联(文章 - 分类一对多、文章 - 标签多对多); - 内置
__str__方法、Meta类优化 Admin 后台展示和数据排序。
(2)View(视图):逻辑层(blog/views.py)
作用:处理用户请求,调用模型获取数据,传递给模板渲染,是「数据与视图的桥梁」。
核心视图及功能:
IndexView(首页视图):- 继承 Django 通用列表视图
ListView,批量获取文章数据; - 分页展示(每页 10 篇);
- 传递侧边栏数据(所有分类、近期文章、热门文章);
- 继承 Django 通用列表视图
PostDetailView(详情页视图):- 继承 Django 通用详情视图
DetailView,获取单篇文章数据; - 访问时自动增加文章浏览量;
- 传递侧边栏通用数据,保证页面布局统一。
- 继承 Django 通用详情视图
关键特性:
- 采用类视图(Class-Based View),简化重复代码;
- 重写
get_context_data传递额外数据,满足页面多维度展示需求; - 重写
get_object实现浏览量统计,逻辑与视图解耦。
(3)Template(模板):展示层(blog/templates/blog/)
作用:负责页面渲染,接收视图传递的数据,生成最终的 HTML 页面展示给用户。
核心模板及分工:
base.html(基础模板):- 定义博客全局布局(头部导航、主体容器、侧边栏);
- 封装通用样式和结构,其他模板通过
extends继承,减少重复代码; - 预留
{% block content %}块,供子模板填充个性化内容;
index.html(首页模板):- 继承
base.html,渲染文章列表; - 循环展示文章标题、摘要、发布信息,空数据时提示用户添加文章;
- 继承
post.html(详情页模板):- 继承
base.html,渲染单篇文章完整内容; - 展示文章元信息(分类、标签、发布时间、浏览量);
- 通过
linebreaksbr过滤器处理内容换行,保证排版美观。
- 继承
关键特性:
- 模板继承:实现布局复用,降低维护成本;
- 模板标签 / 过滤器:动态渲染数据(如
{% for %}循环、|date格式化时间); - 静态样式内联:保证页面独立运行,无需额外引入 CSS 文件。
2. 路由层(URLconf):请求分发(补充)
Django 中路由是 MVT 的「入口」,负责将用户请求分发到对应视图:
(1)项目级路由(djangoblog/urls.py):
- 配置 Admin 后台路由(
admin/); - 分发博客核心请求到应用级路由(
'' → include('blog.urls'));
(2)应用级路由(blog/urls.py):
- 首页:
'' → IndexView.as_view(); - 文章详情:
post/<int:pk>/ → PostDetailView.as_view(); - 定义命名空间
app_name = 'blog',避免路由命名冲突。
3. Admin 后台:数据管理(补充)
通过 blog/admin.py 自定义 Admin 后台,实现数据可视化管理:
- 注册
Category/Tag/Post模型,支持分类 / 标签 / 文章的增删改查; - 自定义列表展示字段、筛选条件、搜索功能,提升管理效率;
- 配置字段集(
fieldsets),优化文章编辑页面布局; - 只读字段(如浏览量、更新时间),防止误修改核心数据。
4. 核心功能总结
表格
| 模块 | 核心功能 |
|---|---|
| 数据层(M) | 分类 / 标签 / 文章数据建模、ORM 操作 |
| 逻辑层(V) | 文章列表 / 详情展示、浏览量统计、分页 |
| 展示层(T) | 响应式布局、数据动态渲染、样式美化 |
| 路由层 | 请求分发、URL 设计 |
| Admin 后台 | 数据可视化管理 |
5. 项目运行流程
-
用户访问
http://127.0.0.1:8000/→ 路由分发到IndexView; IndexView调用Post模型获取文章列表 + 侧边栏数据;- 视图将数据传递给
index.html模板,渲染生成 HTML 页面; - 用户点击文章标题 → 路由分发到
PostDetailView→ 获取单篇文章数据 → 渲染post.html; - 管理员访问
http://127.0.0.1:8000/admin/→ 管理分类 / 标签 / 文章数据。
该项目完整体现了 Django MVT 架构的「高内聚、低耦合」特性:模型专注数据、视图专注逻辑、模板专注展示,各模块独立且协作,符合 Django 「不要重复造轮子」的设计哲学。
【附页】
代码仓库
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐














所有评论(0)