配置个人博客

配置个人博客

下面是你手操第一版 MVP 博客的完整步骤。

第一版目标只做:

Django + SQLite
首页
文章列表
文章详情
Django Admin 添加文章
Markdown 正文渲染为 HTML
基础样式

暂时不做:

用户注册登录
文章前台发布/编辑/删除
评论
搜索
分类标签
Nginx/Gunicorn
Docker

第 0 步:准备项目目录

先新建项目文件夹:

mkdir django_blog_mvp
cd django_blog_mvp

创建虚拟环境:

Windows:

python -m venv .venv
.venv\Scripts\activate

macOS / Linux:

python3 -m venv .venv
source .venv/bin/activate

安装依赖:

pip install django markdown bleach python-dotenv

生成依赖文件:

pip freeze > requirements.txt

第 1 步:创建 Django 项目

在当前目录创建 Django 项目:

django-admin startproject config .

现在结构大概是:

django_blog_mvp
├── manage.py
├── config
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── asgi.py
│   └── wsgi.py
└── requirements.txt

运行测试:

python manage.py runserver

浏览器访问:

http://127.0.0.1:8000/

如果看到 Django 默认页面,说明项目创建成功。

第 2 步:创建 blog 应用

执行:

python manage.py startapp blog

现在结构变成:

django_blog_mvp
├── manage.py
├── config
├── blog
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── views.py
│   ├── tests.py
│   └── migrations
└── requirements.txt

然后打开 config/settings.py,找到 INSTALLED_APPS,加入 blog

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",

    "blog",
]

第 3 步:配置模板和静态文件目录

在项目根目录创建:

mkdir templates
mkdir static
mkdir static/css

项目结构:

django_blog_mvp
├── templates
├── static
│   └── css
├── config
├── blog
└── manage.py

修改 config/settings.py

先确认顶部有:

from pathlib import Path

找到 TEMPLATES 配置,把 DIRS 改成:

"DIRS": [BASE_DIR / "templates"],

完整大概是:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

settings.py 底部加入:

STATIC_URL = "static/"
STATICFILES_DIRS = [
    BASE_DIR / "static",
]

作用是:

templates/ 里放 HTML 模板
static/css/ 里放 CSS 样式

第 4 步:创建文章模型 Post

打开 blog/models.py,写:

from django.db import models
from django.urls import reverse


class Post(models.Model):
    title = models.CharField("标题", max_length=200)
    slug = models.SlugField("URL 标识", unique=True)
    summary = models.TextField("摘要", blank=True)
    content = models.TextField("正文 Markdown")
    created_at = models.DateTimeField("创建时间", auto_now_add=True)
    updated_at = models.DateTimeField("更新时间", auto_now=True)
    is_published = models.BooleanField("是否发布", default=True)

    class Meta:
        ordering = ["-created_at"]
        verbose_name = "文章"
        verbose_name_plural = "文章"

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse("blog:post_detail", kwargs={"slug": self.slug})

这个模型代表一篇文章。

字段含义:

title:文章标题
slug:文章 URL 标识,比如 first-post
summary:文章摘要
content:Markdown 正文
created_at:创建时间
updated_at:更新时间
is_published:是否发布

比如一篇文章:

title = 我的第一篇博客
slug = first-post

以后访问地址就是:

/blog/first-post/

第 5 步:执行数据库迁移

生成迁移文件:

python manage.py makemigrations

执行迁移:

python manage.py migrate

这一步会创建 SQLite 数据库文件:

db.sqlite3

并把 Post 模型变成数据库表。

第 6 步:把 Post 加入 Django Admin

打开 blog/admin.py

from django.contrib import admin
from .models import Post


@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ("title", "slug", "is_published", "created_at")
    list_filter = ("is_published", "created_at")
    search_fields = ("title", "summary", "content")
    prepopulated_fields = {"slug": ("title",)}

这里的作用是:

让你可以在 Django 后台添加、编辑、删除文章

创建管理员账号:

python manage.py createsuperuser

按提示输入用户名、邮箱、密码。

启动项目:

python manage.py runserver

访问后台:

http://127.0.0.1:8000/admin/

登录后,你应该能看到 文章 管理入口。

第 7 步:通过 Admin 添加第一篇文章

进入后台,添加文章。

示例:

标题:我的第一篇博客
URL 标识:first-post
摘要:这是我用 Django 创建的第一篇博客。
正文 Markdown:
# 我的第一篇博客

今天我开始学习 Django 博客开发。

## 当前完成的功能

- Django 项目创建
- Post 模型
- SQLite 数据库
- Django Admin 管理文章

```python
print("Hello Django")

是否发布:勾选


保存。

现在文章已经进入数据库。

---

# 第 8 步:创建 Markdown 渲染工具

在 `blog` 目录下新建:

```text
blog/utils.py

写入:

import markdown
import bleach


ALLOWED_TAGS = [
    "p", "br",
    "strong", "em",
    "ul", "ol", "li",
    "h1", "h2", "h3", "h4",
    "blockquote",
    "code", "pre",
    "a",
    "table", "thead", "tbody", "tr", "th", "td",
]

ALLOWED_ATTRIBUTES = {
    "a": ["href", "title", "rel", "target"],
    "code": ["class"],
    "pre": ["class"],
}


def render_markdown(text):
    """
    把 Markdown 文本转换成经过清理的 HTML。

    第一步:markdown.markdown 把 Markdown 转成 HTML。
    第二步:bleach.clean 清理不安全的标签和属性。
    """
    raw_html = markdown.markdown(
        text,
        extensions=[
            "extra",
            "fenced_code",
            "tables",
            "toc",
        ],
    )

    clean_html = bleach.clean(
        raw_html,
        tags=ALLOWED_TAGS,
        attributes=ALLOWED_ATTRIBUTES,
        strip=True,
    )

    return clean_html

这个文件负责:

Markdown 原文
  ↓
转成 HTML
  ↓
清理危险标签
  ↓
返回安全 HTML

第 9 步:写博客视图 views

打开 blog/views.py

from django.shortcuts import get_object_or_404, render
from .models import Post
from .utils import render_markdown


def home(request):
    latest_posts = Post.objects.filter(is_published=True)[:3]

    return render(
        request,
        "home.html",
        {
            "latest_posts": latest_posts,
        },
    )


def post_list(request):
    posts = Post.objects.filter(is_published=True)

    return render(
        request,
        "blog/post_list.html",
        {
            "posts": posts,
        },
    )


def post_detail(request, slug):
    post = get_object_or_404(Post, slug=slug, is_published=True)

    post.html_content = render_markdown(post.content)

    return render(
        request,
        "blog/post_detail.html",
        {
            "post": post,
        },
    )

三个函数分别负责:

home:首页,显示最近文章
post_list:博客列表页,显示所有文章
post_detail:文章详情页,根据 slug 查文章并渲染 Markdown

第 10 步:创建 blog 路由

blog 目录下新建:

blog/urls.py

写入:

from django.urls import path
from . import views

app_name = "blog"

urlpatterns = [
    path("", views.post_list, name="post_list"),
    path("<slug:slug>/", views.post_detail, name="post_detail"),
]

含义:

/blog/              → 文章列表
/blog/first-post/   → slug 为 first-post 的文章详情

然后修改 config/urls.py

from django.contrib import admin
from django.urls import include, path
from blog import views

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", views.home, name="home"),
    path("blog/", include("blog.urls")),
]

现在路由关系是:

/                 首页
/blog/            文章列表
/blog/<slug>/     文章详情
/admin/           后台管理

第 11 步:创建基础模板 base.html

templates 目录下新建:

templates/base.html

写入:

{% load static %}

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}我的 Django 博客{% endblock %}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
    <header class="site-header">
        <div class="container header-inner">
            <a class="logo" href="{% url 'home' %}">我的博客</a>

            <nav class="nav">
                <a href="{% url 'home' %}">首页</a>
                <a href="{% url 'blog:post_list' %}">博客</a>
                <a href="/admin/">后台</a>
            </nav>
        </div>
    </header>

    <main class="container main-content">
        {% block content %}{% endblock %}
    </main>

    <footer class="site-footer">
        <div class="container">
            <p>© 我的 Django 博客</p>
        </div>
    </footer>
</body>
</html>

这个模板是所有页面的公共外壳。

第 12 步:创建首页模板 home.html

templates 下新建:

templates/home.html

写入:

{% extends "base.html" %}

{% block title %}首页 - 我的 Django 博客{% endblock %}

{% block content %}
<section class="hero">
    <p class="eyebrow">Django Blog MVP</p>
    <h1>我的第一个 Django 博客</h1>
    <p>
        这是一个最小可行版本博客,用来学习 Django、SQLite、ORM、Template 和 Markdown 渲染。
    </p>

    <a class="button" href="{% url 'blog:post_list' %}">查看博客文章</a>
</section>

<section class="section">
    <h2>最近文章</h2>

    {% if latest_posts %}
        <div class="post-grid">
            {% for post in latest_posts %}
                <article class="post-card">
                    <p class="post-date">{{ post.created_at|date:"Y-m-d" }}</p>
                    <h3>
                        <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
                    </h3>
                    <p>{{ post.summary }}</p>
                </article>
            {% endfor %}
        </div>
    {% else %}
        <p>还没有发布文章。</p>
    {% endif %}
</section>
{% endblock %}

第 13 步:创建博客列表模板

创建目录:

mkdir templates/blog

创建:

templates/blog/post_list.html

写入:

{% extends "base.html" %}

{% block title %}博客列表 - 我的 Django 博客{% endblock %}

{% block content %}
<section class="page-header">
    <h1>博客文章</h1>
    <p>这里记录我的学习笔记和项目实践。</p>
</section>

<section class="post-list">
    {% if posts %}
        {% for post in posts %}
            <article class="post-card">
                <p class="post-date">{{ post.created_at|date:"Y-m-d" }}</p>
                <h2>
                    <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
                </h2>
                <p>{{ post.summary }}</p>
                <a href="{{ post.get_absolute_url }}">阅读全文 →</a>
            </article>
        {% endfor %}
    {% else %}
        <p>暂时没有文章。</p>
    {% endif %}
</section>
{% endblock %}

这个页面负责从数据库显示所有文章。

第 14 步:创建文章详情模板

创建:

templates/blog/post_detail.html

写入:

{% extends "base.html" %}

{% block title %}{{ post.title }} - 我的 Django 博客{% endblock %}

{% block content %}
<article class="article">
    <p class="post-date">{{ post.created_at|date:"Y-m-d H:i" }}</p>

    <h1>{{ post.title }}</h1>

    {% if post.summary %}
        <p class="summary">{{ post.summary }}</p>
    {% endif %}

    <div class="article-content">
        {{ post.html_content|safe }}
    </div>
</article>

<p class="back-link">
    <a href="{% url 'blog:post_list' %}">← 返回博客列表</a>
</p>
{% endblock %}

这里最关键的是:

{{ post.html_content|safe }}

意思是:

把 Markdown 转换后的 HTML 输出到页面

因为你已经用 bleach 清理过,所以这里可以用 safe

第 15 步:添加 CSS 样式

创建:

static/css/style.css

写入:

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: Arial, "Microsoft YaHei", sans-serif;
    color: #1f2937;
    background: #f9fafb;
    line-height: 1.7;
}

a {
    color: #2563eb;
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

.container {
    width: min(1100px, 92%);
    margin: 0 auto;
}

.site-header {
    background: #ffffff;
    border-bottom: 1px solid #e5e7eb;
    position: sticky;
    top: 0;
}

.header-inner {
    height: 64px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.logo {
    color: #111827;
    font-size: 20px;
    font-weight: 700;
}

.nav {
    display: flex;
    gap: 20px;
}

.nav a {
    color: #374151;
    font-weight: 500;
}

.main-content {
    padding: 48px 0;
}

.hero {
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 18px;
    padding: 48px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
}

.eyebrow {
    color: #2563eb;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.hero h1 {
    font-size: 42px;
    line-height: 1.2;
    margin: 12px 0;
}

.button {
    display: inline-block;
    margin-top: 20px;
    padding: 10px 18px;
    background: #2563eb;
    color: white;
    border-radius: 10px;
    font-weight: 700;
}

.button:hover {
    text-decoration: none;
    background: #1d4ed8;
}

.section {
    margin-top: 48px;
}

.page-header {
    margin-bottom: 32px;
}

.page-header h1 {
    font-size: 36px;
    margin-bottom: 8px;
}

.post-grid,
.post-list {
    display: grid;
    gap: 18px;
}

.post-card {
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 16px;
    padding: 24px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
}

.post-card h2,
.post-card h3 {
    margin-top: 0;
}

.post-date {
    color: #6b7280;
    font-size: 14px;
}

.article {
    max-width: 780px;
    margin: 0 auto;
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 18px;
    padding: 36px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
}

.article h1 {
    font-size: 40px;
    line-height: 1.25;
    margin-top: 8px;
}

.summary {
    color: #4b5563;
    font-size: 18px;
    border-left: 4px solid #bfdbfe;
    padding-left: 16px;
}

.article-content h1,
.article-content h2,
.article-content h3 {
    margin-top: 32px;
}

.article-content pre {
    overflow-x: auto;
    background: #111827;
    color: #f9fafb;
    padding: 16px;
    border-radius: 12px;
}

.article-content code {
    background: #f3f4f6;
    padding: 2px 6px;
    border-radius: 6px;
}

.article-content pre code {
    background: transparent;
    padding: 0;
}

.back-link {
    max-width: 780px;
    margin: 24px auto 0;
}

.site-footer {
    border-top: 1px solid #e5e7eb;
    background: #ffffff;
    color: #6b7280;
    padding: 24px 0;
}

@media (max-width: 760px) {
    .header-inner {
        height: auto;
        padding: 16px 0;
        flex-direction: column;
        align-items: flex-start;
        gap: 12px;
    }

    .nav {
        flex-wrap: wrap;
        gap: 12px;
    }

    .hero {
        padding: 28px;
    }

    .hero h1 {
        font-size: 32px;
    }

    .article {
        padding: 24px;
    }

    .article h1 {
        font-size: 30px;
    }
}

第 16 步:启动并测试

运行:

python manage.py runserver

访问首页:

http://127.0.0.1:8000/

访问博客列表:

http://127.0.0.1:8000/blog/

访问文章详情:

http://127.0.0.1:8000/blog/first-post/

如果你能看到文章,并且 Markdown 显示成标题、列表、代码块,说明 MVP v1 成功。

第 17 步:你现在完成了哪些部分?

你现在已经完成:

Django 项目
blog app
SQLite 数据库
Post 模型
Django Admin 添加文章
文章列表页
文章详情页
Markdown 渲染
bleach 清理 HTML
Django Template 页面渲染
基础 CSS 样式

这就是博客第一版 MVP。

第 18 步:每一部分对应什么知识?

Django 项目

对应:

config/settings.py
config/urls.py
manage.py

你要理解:

项目如何启动
总路由在哪里
配置在哪里

blog app

对应:

blog/models.py
blog/views.py
blog/urls.py
blog/admin.py
blog/utils.py

你要理解:

一个功能模块如何组织
文章模型在哪里
文章页面逻辑在哪里

数据库

对应:

db.sqlite3
blog/models.py
migrations

你要理解:

Model 如何变成数据库表
makemigrations 和 migrate 的区别

模板

对应:

templates/base.html
templates/home.html
templates/blog/post_list.html
templates/blog/post_detail.html

你要理解:

Django 怎么把数据塞进 HTML
模板继承怎么减少重复代码

Markdown 渲染

对应:

blog/utils.py
blog/views.py
post_detail.html

你要理解:

数据库里存 Markdown
View 里转 HTML
模板里 safe 输出
bleach 清理危险 HTML

第 19 步:下一版应该加什么?

不要马上加所有功能。下一阶段建议加:

MVP v2:用户系统 + 文章作者关联

具体包括:

用户注册
用户登录
用户登出
Post 添加 author 字段
只有登录用户可以发布文章

再下一版:

MVP v3:文章 CRUD

包括:

前台发布文章
编辑文章
删除文章
权限控制

然后再加:

MVP v4:分类、标签、分页、搜索
MVP v5:评论系统
MVP v6:日志、错误处理、配置分离
MVP v7:Gunicorn + Nginx 部署

第一版完成标准

你检查这几个点即可:

1. python manage.py runserver 能启动
2. / 能打开首页
3. /blog/ 能看到文章列表
4. /admin/ 能添加文章
5. /blog/first-post/ 能看到文章详情
6. Markdown 标题、列表、代码块能渲染
7. 页面有基础样式
8. 所有文章数据来自 SQLite,不是写死在 HTML 里

做到这一步,你的第一版就完成了。

评论

wqwgzryhfm 2026-06-03 20:51

dvpujuxpgqdwxswgrhuikndskyvgly

发表评论