blog_project_django
blog_project
1.新建项目,设置项目的一些简单配置
-
使用pycharm 创建一个新的项目
-
利用 python manage.py startapp app 创建一个新的 app
-
将 app 添加到 项目目录下 同项目名文件夹下的settings.py 中的 INSTALLED_APPS 中, (注册应用)
-
urls 模块化, 在新创建的 app 下新建一个 urls.py ,在这个文件中写url 和视图的映射关系,在总的urls.py 中 使用 include 包含这个 app 中的urls, 在新建的 url.py 为 app 指定一个命名空间。
-
这个项目的前端页面是使用 bootstraps5 来实现的, 要在项目目录下创建一个 static 文件夹,存放静态文件。
-
使用 静态文件需要在 相关html 中 使用 {% load static %} 标签 或者 在 settings.py 中的 TEMPLATES 配置中加入一行, 如下图:
![image-20250602175428003]()
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', ], 'builtins': ["django.templatetags.static"] }, }, ] -
在 setting.py 中设置静态文件的加载路径
![image-20250602180849618]()
# 静态文件加载路径
STATICFILES_DIRS = [
BASE_DIR / 'static'
]
2.使用 bootstrap 实现导航条
-
进入 bootstrap官网,下载相关的 css 和 js 文件, 加载到 html 文件中。
# bootstrap官网地址: https://getbootstrap.com/![image-20250602182458511]()
<html lang="en"> <head> <meta charset="UTF-8"> <title>博客</title> <link rel="stylesheet" href="{% static 'bootstrap-5.3.6-dist/css/bootstrap.css' %}"> <script src="{% static 'bootstrap-5.3.6-dist/js/bootstrap.js' %}"></script> </head> -
进入 bootstrap 官网,Examples页面 选择需要的前端效果
![image-20250602183029416]()
-
右键页面->检查->定位到需要的前端效果->copy->copy outerHTML (复制相关的前端效果代码)->复制到 index.html 中
![image-20250602183745678]()
-
对导航条的代码做一些调整,使其符合预期的效果。参考 bootstrp 官方文档进行调整。
<!DOCTYPE html> {#{% load static %}#} <html lang="en"> <head> <meta charset="UTF-8"> <title>博客</title> <link rel="stylesheet" href="{% static 'bootstrap-5.3.6-dist/css/bootstrap.css' %}"> <script src="{% static 'bootstrap-5.3.6-dist/js/bootstrap.js' %}"></script> </head> <body> <header class="p-3 text-bg-light border-bottom"> <div class="container"> <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start"> <a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none"> <img src="{% static 'img/blog.png' %}" alt="" height="40"> </a> <ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0"> <li><a href="#" class="nav-link px-2 text-secondary">首页</a></li> <li><a href="#" class="nav-link px-2 text-secondary">发布博客</a></li> </ul> <form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search"><input type="search" class="form-control" placeholder="搜索" aria-label="Search"></form> <div class="text-end"> <button type="button" class="btn btn-outline-primary me-2">登录</button> <button type="button" class="btn btn-primary">注册</button> </div> </div> </div> </header> </body> </html>效果如下

3.使用 bootstrap 实现首页布局
-
将body 的背景颜色改成灰色, 在static 文件夹下创建一个 css 的文件夹, 在里面写一个base.css
/* base.css 将 body 标签的颜色修改成灰色 body{ background-color: rgba(0, 0, 0, 0.1); }在index.html 中引入 这个 css 文件
<link rel="stylesheet" href="{% static "css/base.css" %}">效果如下
![image-20250602201450477]()
-
在 body 中添加 博客列表 的 container
<main class="container bg-white"> <h1>博客列表</h1> </main>![image-20250602203141445]()
博客列表和 导航条的 距离太近了, 可以填加一些距离
可以在 导航条 header 上 添加一个下边距, 在 header 标签中 加一个 属性 mb-3
这个 mb-3 是 bootstrap 中定义的, 具体可以参考bootstrp 文档, 效果如下:
<header class="p-3 text-bg-light border-bottom mb-3"> ... </header>![image-20250602203623446]()
给博客列表的container 添加一些内边距, main 标签 添加一个 p-3 属性
<main class="container bg-white p-3"> <h1>博客列表</h1> </main>![image-20250602203849344]()
博客列表这个四边型的角是方角, 不好看, 可以设置成圆角 , main 标签添加一个 属性 rounded
<main class="container bg-white p-3 rounded"> <h1>博客列表</h1> </main>![image-20250602204214506]()
-
在 main 标签中添加每个 blog 的 信息展示窗口,可以使用 bootstrap 中的 card 实现, 参考 bootstrap 官方文档。
![image-20250602204811372]()
# 在pycharm中 书写 html 的 时候, 写一个 标签.属性 按tab 键, 会自动生成标签,且会把点后面的 内容放到class 中 # div.row --> <div class="row"></div>这里设置每一行展示两列的博客信息 在 row 中加入 属性 row-cols-2
<div class="row row-cols-2"> <div class="col"> <!-- 下面是从bootstrap 粘贴的 card模版代码, 如上图 --> <div class="card text-center"> <div class="card-header"> Featured </div> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> <div class="card-footer text-body-secondary"> 2 days ago </div> </div> <!--上面是从bootstrap 粘贴的 card模版代码 --> </div> </div>![image-20250602205931442]()
![image-20250602210215767]()
row-cols-2 属性限制每行显示两列,超过两列就展示到下一行了,如下图:
![image-20250602210344947]()
行与行之前紧挨着,不美观,在行与行之前添加一些边距,在行 标签中添加属性 row-gap-4,效果如下图:
<div class="row row-cols-2 row-gap-4"> </div>![image-20250602210706286]()
card-body div 的 高度 会随着 内容的改变而变窄,可以设置 style height 为固定值
<div class="card-body" style="height: 100px"> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> </div>![image-20250602211608806]()
如上图,一个盒子中包含了两个小盒子,每个盒子展示在自己的一边,可以使用bootstrap 中的 flex 布局。
![image-20250602212019570]()
总的 div 可以使用如下属性, 在这个div 中添加两个 div, 一个 div 会展示在左边, 一个div 会展示在右边
<div class="d-flex justify-content-between">...</div>在 左边的div 中添加 头像使用 img 标签,设置圆角用 rounded-circle 属性, 防止图片太大, 给标签设置一下高度和宽度。width=30 height =30
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt="">flex 布局代码总体如下
<div class="card-footer text-body-secondary d-flex justify-content-between"> <div> <img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt=""> 小明 </div> <div> 2025/6/2 21:36 </div> </div>效果图
![image-20250602214137741]()
<!DOCTYPE html> {#{% load static %}#} <html lang="en"> <head> <meta charset="UTF-8"> <title>博客</title> <link rel="stylesheet" href="{% static 'bootstrap-5.3.6-dist/css/bootstrap.css' %}"> <script src="{% static 'bootstrap-5.3.6-dist/js/bootstrap.js' %}"></script> <link rel="stylesheet" href="{% static "css/base.css" %}"> </head> <body> <header class="p-3 text-bg-light border-bottom mb-3"> <div class="container"> <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start"> <a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none"> <img src="{% static 'img/blog.png' %}" alt="" height="40"> </a> <ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0"> <li><a href="#" class="nav-link px-2 text-secondary">首页</a></li> <li><a href="#" class="nav-link px-2 text-secondary">发布博客</a></li> </ul> <form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search"><input type="search" class="form-control" placeholder="搜索" aria-label="Search"></form> <div class="text-end"> <button type="button" class="btn btn-outline-primary me-2">登录</button> <button type="button" class="btn btn-primary">注册</button> </div> </div> </div> </header> <main class="container bg-white p-3 rounded"> <h1>博客列表</h1> <div class="row row-cols-2 row-gap-4"> <div class="col"> <div class="card"> <div class="card-header"> <a href="#"> django 基础</a> </div> <div class="card-body" style="height: 100px"> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> </div> <div class="card-footer text-body-secondary d-flex justify-content-between"> <div> <img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt=""> 小明 </div> <div> 2025/6/2 21:36 </div> </div> </div> </div> <div class="col"> <div class="card text-center"> <div class="card-header"> Featured </div> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> <div class="card-footer text-body-secondary"> 2 days ago </div> </div> </div> <div class="col"> <div class="card text-center"> <div class="card-header"> Featured </div> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> <div class="card-footer text-body-secondary"> 2 days ago </div> </div> </div> </div> </main> </body> </html>
4.使用 bootstrap 实现详情页布局
博客详情页的导航条和 首页的是一样的,复制 首页的代码进行修改。
效果图如下:

代码
<!DOCTYPE html>
{#{% load static %}#}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客</title>
<link rel="stylesheet" href="{% static 'bootstrap-5.3.6-dist/css/bootstrap.css' %}">
<script src="{% static 'bootstrap-5.3.6-dist/js/bootstrap.js' %}"></script>
<link rel="stylesheet" href="{% static "css/base.css" %}">
</head>
<body>
<header class="p-3 text-bg-light border-bottom mb-3">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
<img src="{% static 'img/blog.png' %}" alt="" height="40">
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="#" class="nav-link px-2 text-secondary">首页</a></li>
<li><a href="#" class="nav-link px-2 text-secondary">发布博客</a></li>
</ul>
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search"><input type="search"
class="form-control"
placeholder="搜索"
aria-label="Search"></form>
<div class="text-end">
<button type="button" class="btn btn-outline-primary me-2">登录</button>
<button type="button" class="btn btn-primary">注册</button>
</div>
</div>
</div>
</header>
<main class="container bg-white p-3 rounded">
<h1>django 基础</h1>
<hr>
<div class="mt-2">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt="">
<span class="ms-2">小明</span>
<span class="ms-2">于</span>
<span class="ms-2">2025/6/3 8:21</span>
</div>
<hr>
<div class="py-2">
博客详情
</div>
<hr>
<div class="mt-2">
<h3>评论:(10)</h3>
<form action="" mehtod="POST">
<div class="mt-2">
<input type="text" class="form-control" placeholder="请输入评论">
</div>
<div class="text-end mt-2">
<button type="button" class="btn btn-primary">评论</button>
</div>
</form>
</div>
<div class="mt-2">
<ul class="list-group list-group-flush">
<li class="list-group-item mt-3">
<div class="d-flex justify-content-between text-body-secondary">
<div class="user-info">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="40" height="40" alt="">
<span class="ms-2">小明</span>
</div>
<div class="create-time" style="line-height: 40px">2025/6/3 8:46</div>
</div>
<div class="mt-2">评论内容</div>
</li>
<li class="list-group-item mt-3">
<div class="d-flex justify-content-between text-body-secondary">
<div class="user-info">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="40" height="40" alt="">
<span class="ms-2">小明</span>
</div>
<div class="create-time" style="line-height: 40px">2025/6/3 8:46</div>
</div>
<div class="mt-2">评论内容</div>
</li>
</ul>
</div>
</main>
</body>
</html>
5.使用 bootstrap 实现发布页面布局
发布页面的一部分代码和 首页相同,使用首页的代码修改,这里涉及到一个富文本编辑器的实现, 参考文档:https://www.wangeditor.com/
效果如下:

代码
<!DOCTYPE html>
{#{% load static %}#}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客</title>
<link rel="stylesheet" href="{% static 'bootstrap-5.3.6-dist/css/bootstrap.css' %}">
<script src="{% static 'bootstrap-5.3.6-dist/js/bootstrap.js' %}"></script>
<link rel="stylesheet" href="{% static "css/base.css" %}">
<!-- 下面这两文件 从wangeditor 官网下载-->
<link rel="stylesheet" href="{% static "wangeditor/css/style.css" %}">
<script src="{% static 'wangeditor/index.js' %}"></script>
<script src="{% static 'js/pub_blog.js' %}"></script>
<style>
#editor—wrapper {
border: 1px solid #ccc;
z-index: 100; /* 按需定义 */
}
#toolbar-container {
border-bottom: 1px solid #ccc;
}
#editor-container {
height: 500px;
}
</style>
</head>
<body>
<header class="p-3 text-bg-light border-bottom mb-3">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
<img src="{% static 'img/blog.png' %}" alt="" height="40">
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="#" class="nav-link px-2 text-secondary">首页</a></li>
<li><a href="#" class="nav-link px-2 text-secondary">发布博客</a></li>
</ul>
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search"><input type="search"
class="form-control"
placeholder="搜索"
aria-label="Search"></form>
<div class="text-end">
<button type="button" class="btn btn-outline-primary me-2">登录</button>
<button type="button" class="btn btn-primary">注册</button>
</div>
</div>
</div>
</header>
<main class="container bg-white p-3 rounded">
<h1>发布博客</h1>
<div class="mt-3">
<form action="" method="POST">
<div class="mb-3">
<label class="form-label" for="">标题</label>
<input type="text" name="title" class="form-control">
</div>
<div class="mb-3">
<label class="form-label" for="">分类</label>
<select name="category" class="form-select">
<option value="1">python</option>
<option value="2">前端</option>
</select>
</div>
<div class="mb-3">
<label class='form-label' for="">内容</label>
<div id="editor—wrapper">
<div id="toolbar-container"><!-- 工具栏 --></div>
<div id="editor-container"><!-- 编辑器 --></div>
</div>
</div>
<div class="mb-3 text-end">
<button class="btn btn-primary">发布</button>
</div>
</form>
</div>
</main>
</body>
</html>
// 参考上述富文本编辑官网,使用 说明代码
window.onload = function(){
const { createEditor, createToolbar } = window.wangEditor
const editorConfig = {
placeholder: 'Type here...',
onChange(editor) {
const html = editor.getHtml()
console.log('editor content', html)
// 也可以同步到 <textarea>
},
}
const editor = createEditor({
selector: '#editor-container',
html: '<p><br></p>',
config: editorConfig,
mode: 'default', // or 'simple'
})
const toolbarConfig = {}
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default', // or 'simple'
})
}
6.使用 bootstrap 实现登录和注册页面布局
登录页面效果

<!DOCTYPE html>
{#{% load static %}#}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客</title>
<link rel="stylesheet" href="{% static 'bootstrap-5.3.6-dist/css/bootstrap.css' %}">
<script src="{% static 'bootstrap-5.3.6-dist/js/bootstrap.js' %}"></script>
<link rel="stylesheet" href="{% static "css/base.css" %}">
</head>
<body>
<header class="p-3 text-bg-light border-bottom mb-3">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
<img src="{% static 'img/blog.png' %}" alt="" height="40">
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="#" class="nav-link px-2 text-secondary">首页</a></li>
<li><a href="#" class="nav-link px-2 text-secondary">发布博客</a></li>
</ul>
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search"><input type="search"
class="form-control"
placeholder="搜索"
aria-label="Search"></form>
<div class="text-end">
<button type="button" class="btn btn-outline-primary me-2">登录</button>
<button type="button" class="btn btn-primary">注册</button>
</div>
</div>
</div>
</header>
<main class="container bg-white p-3 rounded">
<div style="max-width: 330px;" class="m-auto">
<h1>登陆</h1>
<form action="" method="POST">
<div class="mb-3">
<label for="">邮箱</label>
<input type="email" name="email" class="form-control" placeholder="邮箱">
</div>
<div class="mb-3">
<label for="">密码</label>
<input type="password" name="password" class="form-control" placeholder="密码">
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="remember" value="1" id="checkDefault">
<label class="form-check-label" for="checkDefault">
记住我
</label>
</div>
<div class="mb-3">
<button class="btn btn-primary w-100">立即登录</button>
</div>
</form>
</div>
</main>
</body>
</html>
注册页面

<!DOCTYPE html>
{#{% load static %}#}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>博客</title>
<link rel="stylesheet" href="{% static 'bootstrap-5.3.6-dist/css/bootstrap.css' %}">
<script src="{% static 'bootstrap-5.3.6-dist/js/bootstrap.js' %}"></script>
<link rel="stylesheet" href="{% static "css/base.css" %}">
</head>
<body>
<header class="p-3 text-bg-light border-bottom mb-3">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
<img src="{% static 'img/blog.png' %}" alt="" height="40">
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="#" class="nav-link px-2 text-secondary">首页</a></li>
<li><a href="#" class="nav-link px-2 text-secondary">发布博客</a></li>
</ul>
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search"><input type="search"
class="form-control"
placeholder="搜索"
aria-label="Search"></form>
<div class="text-end">
<button type="button" class="btn btn-outline-primary me-2">登录</button>
<button type="button" class="btn btn-primary">注册</button>
</div>
</div>
</div>
</header>
<main class="container bg-white p-3 rounded">
<div style="max-width: 330px;" class="m-auto">
<h1>注册</h1>
<form action="" method="POST">
<div class="mb-3">
<label for="">用户名</label>
<input type="text" name="username" class="form-control" placeholder="用户名">
</div>
<div class="mb-3">
<label for="">邮箱</label>
<input type="email" name="email" class="form-control" placeholder="邮箱">
</div>
<div class="mb-3">
<label for="">验证码</label>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="验证码" aria-label="Recipient’s username" aria-describedby="button-addon2">
<button class="btn btn-outline-secondary" type="button" >获取验证码</button>
</div>
</div>
<div class="mb-3">
<label for="">密码</label>
<input type="password" name="password" class="form-control" placeholder="密码">
</div>
<div class="mb-3">
<button class="btn btn-primary w-100">立即注册</button>
</div>
</form>
</div>
</main>
</body>
</html>
7.将html 文件改造成django 模版
使用django 中的模版继承, 把模版中相同的部分写到 base.html 中,不同的部分使用 block 占位
base.html
<!DOCTYPE html>
{#{% load static %}#}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
{% endblock %}
</title>
<link rel="stylesheet" href="{% static 'bootstrap-5.3.6-dist/css/bootstrap.css' %}">
<script src="{% static 'bootstrap-5.3.6-dist/js/bootstrap.js' %}"></script>
<link rel="stylesheet" href="{% static "css/base.css" %}">
{% block head %}
{% endblock %}
</head>
<body>
<header class="p-3 text-bg-light border-bottom mb-3">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
<img src="{% static 'img/blog.png' %}" alt="" height="40">
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="#" class="nav-link px-2 text-secondary">首页</a></li>
<li><a href="#" class="nav-link px-2 text-secondary">发布博客</a></li>
</ul>
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search"><input type="search"
class="form-control"
placeholder="搜索"
aria-label="Search"></form>
<div class="text-end">
<button type="button" class="btn btn-outline-primary me-2">登录</button>
<button type="button" class="btn btn-primary">注册</button>
</div>
</div>
</div>
</header>
<main class="container bg-white p-3 rounded">
{% block main %}
{% endblock %}
</main>
</body>
</html>
首页 index.html
{% extends 'base.html' %}
{% block title %}
博客首页
{% endblock %}
{% block main %}
<h1>博客列表</h1>
<div class="row row-cols-2 row-gap-4">
<div class="col">
<div class="card">
<div class="card-header">
<a href="#"> django 基础</a>
</div>
<div class="card-body" style="height: 100px">
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
</div>
<div class="card-footer text-body-secondary d-flex justify-content-between">
<div>
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt="">
小明
</div>
<div>
2025/6/2 21:36
</div>
</div>
</div>
</div>
<div class="col">
<div class="card text-center">
<div class="card-header">
Featured
</div>
<div class="card-body">
<h5 class="card-title">Special title treatment</h5>
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-body-secondary">
2 days ago
</div>
</div>
</div>
<div class="col">
<div class="card text-center">
<div class="card-header">
Featured
</div>
<div class="card-body">
<h5 class="card-title">Special title treatment</h5>
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-body-secondary">
2 days ago
</div>
</div>
</div>
</div>
{% endblock %}
登录页面,login.html
{% extends "base.html" %}
{% block title %}
登录
{% endblock %}
{% block main %}
<div style="max-width: 330px;" class="m-auto">
<h1>登陆</h1>
<form action="" method="POST">
<div class="mb-3">
<label for="">邮箱</label>
<input type="email" name="email" class="form-control" placeholder="邮箱">
</div>
<div class="mb-3">
<label for="">密码</label>
<input type="password" name="password" class="form-control" placeholder="密码">
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="remember" value="1" id="checkDefault">
<label class="form-check-label" for="checkDefault">
记住我
</label>
</div>
<div class="mb-3">
<button class="btn btn-primary w-100">立即登录</button>
</div>
</form>
</div>
{% endblock %}
注册页面 register.html
{% extends "base.html" %}
{% block title %}
注册
{% endblock %}
{% block main %}
<div style="max-width: 330px;" class="m-auto">
<h1>注册</h1>
<form action="" method="POST">
<div class="mb-3">
<label for="">用户名</label>
<input type="text" name="username" class="form-control" placeholder="用户名">
</div>
<div class="mb-3">
<label for="">邮箱</label>
<input type="email" name="email" class="form-control" placeholder="邮箱">
</div>
<div class="mb-3">
<label for="">验证码</label>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="验证码" aria-label="Recipient’s username" aria-describedby="button-addon2">
<button class="btn btn-outline-secondary" type="button" >获取验证码</button>
</div>
</div>
<div class="mb-3">
<label for="">密码</label>
<input type="password" name="password" class="form-control" placeholder="密码">
</div>
<div class="mb-3">
<button class="btn btn-primary w-100">立即注册</button>
</div>
</form>
</div>
{% endblock %}
发布博客页面 pub_blog.html
{% extends "base.html" %}
{% block title %}
发布博客
{% endblock %}
{% block head %}
<link rel="stylesheet" href="{% static "wangeditor/css/style.css" %}">
<script src="{% static 'wangeditor/index.js' %}"></script>
<script src="{% static 'js/pub_blog.js' %}"></script>
<style>
#editor—wrapper {
border: 1px solid #ccc;
z-index: 100; /* 按需定义 */
}
#toolbar-container {
border-bottom: 1px solid #ccc;
}
#editor-container {
height: 500px;
}
</style>
{% endblock %}
{% block main %}
<h1>发布博客</h1>
<div class="mt-3">
<form action="" method="POST">
<div class="mb-3">
<label class="form-label" for="">标题</label>
<input type="text" name="title" class="form-control">
</div>
<div class="mb-3">
<label class="form-label" for="">分类</label>
<select name="category" class="form-select">
<option value="1">python</option>
<option value="2">前端</option>
</select>
</div>
<div class="mb-3">
<label class='form-label' for="">内容</label>
<div id="editor—wrapper">
<div id="toolbar-container"><!-- 工具栏 --></div>
<div id="editor-container"><!-- 编辑器 --></div>
</div>
</div>
<div class="mb-3 text-end">
<button class="btn btn-primary">发布</button>
</div>
</form>
</div>
{% endblock %}
博客详情页面 blog_detail.html
{% extends "base.html" %}
{% block main %}
<h1>django 基础</h1>
<hr>
<div class="mt-2">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt="">
<span class="ms-2">小明</span>
<span class="ms-2">于</span>
<span class="ms-2">2025/6/3 8:21</span>
</div>
<hr>
<div class="py-2">
博客详情
</div>
<hr>
<div class="mt-2">
<h3>评论:(10)</h3>
<form action="" mehtod="POST">
<div class="mt-2">
<input type="text" class="form-control" placeholder="请输入评论">
</div>
<div class="text-end mt-2">
<button type="button" class="btn btn-primary">评论</button>
</div>
</form>
</div>
<div class="mt-2">
<ul class="list-group list-group-flush">
<li class="list-group-item mt-3">
<div class="d-flex justify-content-between text-body-secondary">
<div class="user-info">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="40" height="40" alt="">
<span class="ms-2">小明</span>
</div>
<div class="create-time" style="line-height: 40px">2025/6/3 8:46</div>
</div>
<div class="mt-2">评论内容</div>
</li>
<li class="list-group-item mt-3">
<div class="d-flex justify-content-between text-body-secondary">
<div class="user-info">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="40" height="40" alt="">
<span class="ms-2">小明</span>
</div>
<div class="create-time" style="line-height: 40px">2025/6/3 8:46</div>
</div>
<div class="mt-2">评论内容</div>
</li>
</ul>
</div>
{% endblock %}
8.邮件发送功能实现
通过企业邮箱或者个人邮箱 给注册用户发送邮件, 需要开启 邮箱的SMTP服务
qq 邮箱的SMTP 服务在如下图所示位置

设置发送邮箱的配置(在 settings.py 中书写)
# 发送邮件相关配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
# qq 邮箱 如果是 网易邮箱 'smtp.163.com'
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 587
# 开启 SMTP 服务的邮箱账号, 用来发送邮件
EMAIL_HOST_USER = 'xxxxxxx@qq.com'
# 开启 SMTP 服务时生成的授权码
EMAIL_HOST_PASSWORD = "xxxxxxxx"
# 开启 SMTP 服务的邮箱账号, 用来发送邮件
DEFAULT_FROM_EMAIL = 'xxxxxx@qq.com'
发送邮件的视图函数
# auth1(app)\views.py
from django.shortcuts import render
from django.http.response import JsonResponse
import string
import random
from django.core.mail import send_mail
def send_email_captcha(request):
# ?email=xxx 获取给哪个邮箱发送验证码
email = request.GET.get('email')
if not email:
return JsonResponse({"code": 400, "message": "必须传递邮箱!"})
# 生成验证码(取随机四位 阿拉伯数字)
captcha = "".join(random.sample(string.digits, 4))
send_mail("博客注册验证码", message=f"您的注册验证码是:{captcha}", recipient_list=[email], from_email=None)
return JsonResponse({"code":200, "message": "邮箱验证码发送成功!"})
9.数据库配置和验证码存储
把数据库的配置写在一个 配置文件中, 比如: my_database.cnf, 然后在 settings.py 中引入 配置文件。
# settings.py 中的 数据库配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
'read_default_file': 'my_database.cnf',
}
}
}
my_database.cnf
[client]
database=blog
user = root
password = root
default-character-set=utf8mb4
确认相关的app 在 settings.py 中注册, 如果app没有在settings.py 注册,那么执行数据库迁移命令的时候,无法把相关的 app models.py 中的模型映射到数据库。

创建存储 验证码和邮箱的模型类,在 auth1/models.py 中书写
from django.db import models
# Create your models here.
class CaptchaModel(models.Model):
email = models.EmailField(unique=True)
captcha = models.CharField(max_length=4)
create_time = models.DateTimeField(auto_now_add=True)
# 执行数据库迁移命令, 将models.py 中的模型类映射到数据库
# python manage.py makemigrations
# python manage.py migrate
修改 获取验证码的视图函数,将验证码存储到数据库中
def send_email_captcha(request):
# ?email=xxx 获取给那个邮箱发送验证码
email = request.GET.get('email')
if not email:
return JsonResponse({"code": 400, "message": "必须传递邮箱!"})
# 生成验证码(取随机四位 阿拉伯数字)
captcha = "".join(random.sample(string.digits, 4))
# 将邮箱和验证码存储到数据库中 ,如果找到了邮箱, 更新验证码, 如果没找到,创建并添加数据。
CaptchaModel.objects.update_or_create(email=email, defaults={'captcha': captcha})
send_mail("博客注册验证码", message=f"您的注册验证码是:{captcha}", recipient_list=[email], from_email=None)
return JsonResponse({"code":200, "message": "邮箱验证码发送成功!"})
10.获取验证码按钮倒计时功能实现
使用jQuey 给 发送验证码按钮绑定一个事件, 点击按钮 向获取验证码视图函数发送请求, 使用 ajax 请求
在 register.html 中导入 jquery 库, 使用自己写的 javascript脚步(register.js) 给 获取验证码按钮绑定事件。
{% block head %}
<script src="{% static 'jquery/jquery.3.6.min.js' %}"></script>
<script src="{% static 'js/register.js' %}"></script>
{% endblock %}
$(function(){
function bindCaptchaBtnClick(){
$("#captcha-btn").click(function(event){
// 将 按钮对象 转化成 jquery对象
let $this = $(this);
let email = $("input[name='email']").val();
if(!email){
alert("请先输入邮箱!")
return;
}
// 取消按钮的点击事件,
$this.off('click');
// 倒计时
let countdown = 6;
let timer = setInterval(function(){
if(countdown <= 0){
$this.text("获取验证码");
// 清除定时器
clearInterval(timer);
// 重新绑定点击事件
bindCaptchaBtnClick();
}else{
countdown--;
$this.text(countdown + "s");
}
}, 1000);
})
}
bindCaptchaBtnClick();
});
11.验证码发送功能完成
给按钮绑定发送 ajax 请求, register.js
$(function(){
function bindCaptchaBtnClick(){
$("#captcha-btn").click(function(event){
// 将 按钮对象 转化成 jquery对象
let $this = $(this);
let email = $("input[name='email']").val();
if(!email){
alert("请先输入邮箱!")
return;
}
// 取消按钮的点击事件,
$this.off('click');
// 发送 ajax 请求
$.ajax('/auth1/captcha?email=' + email, {
method:'GET',
success: function(result){
if(result['code'] == 200){
alert("验证码发送成功!");
}else{
alert(result['message']);
}
},
fail: function(error){
console.log(error);
}
})
// 倒计时
let countdown = 6;
let timer = setInterval(function(){
if(countdown <= 0){
$this.text("获取验证码");
// 清除定时器
clearInterval(timer);
// 重新绑定点击事件
bindCaptchaBtnClick();
}else{
countdown--;
$this.text(countdown + "s");
}
}, 1000);
})
}
bindCaptchaBtnClick();
});
12.注册功能实现
完善注册功能视图函数
# auth1/views.py
from django.shortcuts import render, redirect, reverse
from django.http.response import JsonResponse
import string
import random
from django.core.mail import send_mail
from .models import CaptchaModel
from django.views.decorators.http import require_http_methods
from .forms import RegisterForm
from django.contrib.auth import get_user_model
User = get_user_model()
@require_http_methods(['GET', 'POST'])
def register(request):
if request.method == "GET":
return render(request, "register.html")
else:
form = RegisterForm(request.POST)
if form.is_valid():
email = form.cleaned_data.get('email')
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
# 使用 django 内置模型的create_user 方法,可以将 password 加密后存储
User.objects.create_user(email=email, username=username, password=password)
return redirect(reverse('auth1:login'))
else:
print(form.errors)
return redirect(reverse("auth1:register"))
# return render(request, "register.html", context={"form": form})
注册表单的数据验证
# auth1/forms.py
from django import forms
from django.contrib.auth import get_user_model
from .models import CaptchaModel
# 这里使用 django 内部定义的user 模型
User = get_user_model()
class RegisterForm(forms.Form):
username = forms.CharField(max_length=20, min_length=2, error_message={
'required': "请传入用户名!",
'max_length': '用户名长度在2~20之间',
'min_length': '用户名长度在2~20之间',
})
email = forms.EmailField(error_messages={"required": "请输入邮箱", 'invalid': "请输入一个正确的邮箱!"})
captcha = forms.CharField(max_length=4, min_length=4)
password = forms.CharField(max_length=20, min_length=6)
# 对某个字段进行自定义验证 用 clean_字段名
def clean_email(self):
email = self.cleaned_data.get("email")
exists = User.objects.filter(email=email).exists()
if exists:
raise forms.ValidationError("邮箱已经被注册!")
return email
def clean_captcha(self):
captcha = self.cleaned_data.get('captcha')
email = self.cleaned_data.get('email')
captcha_model = CaptchaModel.objects.filter(email=email, captcha=captcha).first()
if not captcha_model:
raise forms.ValidationError("验证码和邮箱不匹配!")
captcha_model.delete()
return captcha
13.登录功能实现
登录表单验证
# auth1/forms.py
class LoginForm(forms.Form):
email = forms.EmailField(error_messages={"required": "请传入邮箱!", "invalid": "请传入一个正确的邮箱!"})
password = forms.CharField(max_length=20, min_length=6)
remember = forms.IntegerField(required=False)
登录视图函数
# auth1/views.py
from django.shortcuts import render, redirect, reverse
from django.http.response import JsonResponse
import string
import random
from django.core.mail import send_mail
from .models import CaptchaModel
from django.views.decorators.http import require_http_methods
from .forms import RegisterForm, LoginForm
from django.contrib.auth import get_user_model, login
User = get_user_model()
@require_http_methods(["GET", "POST"])
def login1(request):
if request.method == "GET":
return render(request, "login.html")
else:
form = LoginForm(request.POST)
if form.is_valid():
email = form.cleaned_data.get("email")
password = form.cleaned_data.get("password")
remember = form.cleaned_data.get("remember")
user = User.objects.filter(email=email).first()
# django 自定义的模型 check_password 会自动将加密密码和 密码对比
if user and user.check_password(password):
# 登录 这里有设置session 保持登录状态
login(request, user)
if not remember:
# 如果没有点击记住我,那么就要设置过期时间为0, 即浏览器关闭后就会过期
request.session.set_expiry(0)
# 如果点击了, 那么就什么也不做,使用默认的2周的过期时间
return redirect('/')
else:
print("邮箱或密码错误!")
# form.add_error("email", "邮箱或者验证码错误!")
# return render(request, 'login.html', context={'form': form})
return redirect(reverse("auth1:login"))
14.登录状态和非登录状态切换
导航条的代码是放在 base.html 中的,修改base.html 使点击登录按钮,跳转到登录页面, 点击 注册按钮,跳转到注册页面。
<!-- base.html-->
<!-- 基于 a 标签的 href 跳转-->
<div class="text-end">
<a href="{% url "auth1:login" %}" type="button" class="btn btn-outline-primary me-2">登录</a>
<a href="{% url "auth1:register" %}" type="button" class="btn btn-primary">注册</a>
</div>
如何判断用户是否登录,如果使用 django 内部提供的 user 模型创建的User 对象,可以使用 user.is_authenticated 来判断
如何在模版中使用user 对象, 在上下文处理器中的变量,可以直接在 模版中使用, django 在settings.py templates 的 context_processors 定义了一些变量,其中 user 对象被存储在 'django.contrib.auth.context_processors.auth' 中


在 base.html 中 根据用户是否登录进行判断
{% if user.is_authenticated %}
<div class="dropdown text-end">
<a href="#" class="d-block link-body-emphasis text-decoration-none dropdown-toggle"
data-bs-toggle="dropdown" aria-expanded="false">
<img src="{% static "img/avator.png" %}" alt="mdo" width="32" height="32" class="rounded-circle">
</a>
<ul class="dropdown-menu text-small" style="">
<li><a class="dropdown-item" href="#">Sign out</a></li>
</ul>
</div>
{% else %}
<div class="text-end">
<a href="{% url "auth1:login" %}" type="button" class="btn btn-outline-primary me-2">登录</a>
<a href="{% url "auth1:register" %}" type="button" class="btn btn-primary">注册</a>
</div>
{% endif %}
15.退出登录功能完成
写一个退出登录的视图函数
from django.contrib.auth import get_user_model, login, logout
def logout1(request):
logout(request)
return redirect("/")
base.html 点击退出登录,调用 logout 视图函数
<li><a class="dropdown-item" href="{% url "auth1:logout" %}">退出登录</a></li>
16.发布博客访问限制
点击 首页 和发布博客跳转到相关的页面, 修改 base.html
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="/" class="nav-link px-2 text-secondary">首页</a></li>
<li><a href="{% url 'blog:blog_pub' %}" class="nav-link px-2 text-secondary">发布博客</a></li>
</ul>
没有登录的情况下,不能够跳转到发布博客页面 使用 django 中 登录装饰器login_requried, 没有登录 跳转到登录页面
# blog(app)/views.py
from django.contrib.auth.decorators import login_required
# 使用 reverse_lazy 防止视图函数未加载,进行反转
from django.urls.base import reverse_lazy
# 1. @login_required(login_url=reverse_lazy("auth1:login"))
# 或者直接写 login视图函数对应的 url
# 2.@login_required(login_rul='/auth1/login')
# 直接写 login_required(), 在 settings.py 中写 LOGIN_URL = '/auth1/login'
# 3. login_required()
@login_required()
def pub_blog(request):
return render(request, 'pub_blog.html')
17.博客相关模型和表创建
创建 发布博客 类型的数据模型, 博客的数据模型, 评论的数据模型
# blog(app)/models.py
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
# Create your models here.
class BlogCategory(models.Model):
name = models.CharField(max_length=200)
class Blog(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
pub_time = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(BlogCategory, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class BlogComment(models.Model):
content = models.TextField()
pub_time = models.DateTimeField(auto_now_add=True)
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
# 将创建的 数据库模型 映射到 数据库中
# 确认 相关的app 在settings.py 中注册 , 然后使用数据库迁移命令
# python manage.py makemigrations
# python manage.py migrate
18.Admin系统使用讲解
django 初始写好了一个admin视图函数,可以进行后台管理
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# 包含 blog app 中的 url
path("", include("blog.urls")),
path("auth1/", include("auth1.urls"))
]
可以通过修改数据库中相关的字段,来提高用户权限,对admin 后台进行登录

更改 语言为 中文, settings.py 中 LANGUAGE_CODE = 'zh-hans'


想要使用 django admin 管理 app 下的 model 类, 需要在 相关的 app 中 的admin 中 写一些 admin 类来管理 相关的模型
# auth1(app)/ admin.py
from django.contrib import admin
from .models import BlogCategory, Blog, BlogComment
class BlogCategoryAdmin(admin.ModelAdmin):
list_display = ['name']
class BlogAdmin(admin.ModelAdmin):
list_display = ['title', 'content', 'pub_time', 'category', 'author']
class BlogCommentAdmin(admin.ModelAdmin):
list_display = ['content', 'pub_time', 'author', 'blog']
admin.site.register(BlogCategory, BlogCategoryAdmin)
admin.site.register(Blog, BlogAdmin)
admin.site.register(BlogComment, BlogCommentAdmin)

管理的模型类名字也可以设置, 在相关的 模型类中指定名字
# auth1(app) / models.py
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
# Create your models here.
class BlogCategory(models.Model):
name = models.CharField(max_length=200)
class Meta:
verbose_name = "分类"
# 复数 形式
verbose_name_plural = verbose_name
class Blog(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
pub_time = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(BlogCategory, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name = "博客"
verbose_name_plural = verbose_name
class BlogComment(models.Model):
content = models.TextField()
pub_time = models.DateTimeField(auto_now_add=True)
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name = "博客评论"
verbose_name_plural = verbose_name

修改每个模型里每个字段名为中文, 使用 verbose_name 起名
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
# Create your models here.
class BlogCategory(models.Model):
name = models.CharField(max_length=200, verbose_name="分类名称")
# admin 管理界面展示 name
def __str__(self):
return self.name
class Meta:
verbose_name = "分类"
# 复数 形式
verbose_name_plural = verbose_name
class Blog(models.Model):
title = models.CharField(max_length=200, verbose_name="标题")
content = models.TextField(verbose_name="内容")
pub_time = models.DateTimeField(auto_now_add=True, verbose_name="发布时间")
category = models.ForeignKey(BlogCategory, on_delete=models.CASCADE, verbose_name="分类")
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
def __str__(self):
return self.title
class Meta:
verbose_name = "博客"
verbose_name_plural = verbose_name
class BlogComment(models.Model):
content = models.TextField(verbose_name="内容")
pub_time = models.DateTimeField(auto_now_add=True, verbose_name="发布时间")
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, verbose_name="所属博客")
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
def __str__(self):
return self.content
class Meta:
verbose_name = "博客评论"
verbose_name_plural = verbose_name

可以在 admin 页面添加一些原始的数据
19.发布博客后端功能实现
完善 pub_blog 视图函数
from django.shortcuts import render
# from django.urls.base import reverse_lazy
from django.http.response import JsonResponse
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from .models import BlogCategory, Blog, BlogComment
from .forms import PubBlogForm
@require_http_methods(["GET", "POST"])
@login_required
def pub_blog(request):
if request.method == "GET":
categories = BlogCategory.objects.all()
return render(request, 'pub_blog.html', context={"categories": categories})
else:
form = PubBlogForm(request.POST)
if form.is_valid():
title = form.cleaned_data.get("title")
content = form.cleaned_data.get("content")
category_id = form.cleaned_data.get("category")
# 将发布博客存储到 数据库中
Blog.objects.create(title=title, content=content, category_id=category_id, author=request.user)
return JsonResponse({"code": 200, "message": "博客发布成功!"})
else:
print(form.errors)
return JsonResponse({"code": 400, "message": "参数错误!"})
定义发布博客的表单验证
# blog(app)/ forms.py
from django import forms
class PubBlogForm(forms.Form):
title = forms.CharField(max_length=200, min_length=2)
content = forms.CharField(min_length=2)
category = forms.IntegerField()
修改 pub_blog.html 显示分类信息
<select name="category" class="form-select">
{% for category in categories %}
<option value="{{ category.id }}">{{ category.name }}</option>
{% endfor %}
</select>

20.发布博客前端功能完成
在 pub_blog.js 中 使用 ajax 向 发布博客视图函数发送 ajax 请求
window.onload = function(){
const { createEditor, createToolbar } = window.wangEditor
const editorConfig = {
placeholder: 'Type here...',
onChange(editor) {
const html = editor.getHtml()
console.log('editor content', html)
// 也可以同步到 <textarea>
},
}
const editor = createEditor({
selector: '#editor-container',
html: '<p><br></p>',
config: editorConfig,
mode: 'default', // or 'simple'
})
const toolbarConfig = {}
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default', // or 'simple'
})
// 富文本内容提交
$("#submit-btn").click(function(event){
// 阻止按钮的默认行为
event.preventDefault();
let title = $("input[name='title']").val();
let category = $("#category-select").val();
// 主要是这里 需要用 复文本编辑器的 方法获取内容
let content = editor.getHtml();
let csrfmiddlewaretoken = $("input[name='csrfmiddlewaretoken']").val()
$.ajax('/blog/pub', {
method: "POST",
data:{title, category, content, csrfmiddlewaretoken},
success: function(result){
if(result['code'] == 200){
// 获取博客id
let blog_id = result['data']['blog_id'];
// 跳转到博客详情页面
window.location = '/blog/detail/' + blog_id;
}else{
alert(result['mesage']);
}
}
})
});
}

发布博客视图函数
# blog(app)/ views.py
from django.shortcuts import render
# from django.urls.base import reverse_lazy
from django.http.response import JsonResponse
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from .models import BlogCategory, Blog, BlogComment
from .forms import PubBlogForm
@require_http_methods(["GET", "POST"])
@login_required
def pub_blog(request):
if request.method == "GET":
categories = BlogCategory.objects.all()
return render(request, 'pub_blog.html', context={"categories": categories})
else:
form = PubBlogForm(request.POST)
if form.is_valid():
title = form.cleaned_data.get("title")
content = form.cleaned_data.get("content")
category_id = form.cleaned_data.get("category")
# 将发布博客存储到 数据库中
blog = Blog.objects.create(title=title, content=content, category_id=category_id, author=request.user)
return JsonResponse({"code": 200, "message": "博客发布成功!", 'data': {"blog_id": blog.id}})
else:
print(form.errors)
return JsonResponse({"code": 400, "message": "参数错误!"})
21.博客详情页数据动态展示
完善 博客详情页的视图函数
# blog(app)/ views.py
from django.shortcuts import render
from .models import BlogCategory, Blog, BlogComment
def blog_detail(request, blog_id):
try:
blog = Blog.objects.get(pk=blog_id)
except Exception as e:
blog = None
return render(request, "blog_detail.html", context={"blog": blog})
修改 blog_detail.html 中的内容
{% extends "base.html" %}
{% block title %}
{% endblock %}
{% block main %}
<h1>{{ blog.title }}</h1>
<hr>
<div class="mt-2">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt="">
<span class="ms-2">{{ blog.author.username }}</span>
<span class="ms-2">于</span>
<span class="ms-2">{{ blog.pub_time|date:"Y年m月d日 h时i分" }}</span>
</div>
<hr>
<div class="py-2">
{{ blog.content|safe }}
</div>
<hr>
<div class="mt-2">
<h3>评论:(10)</h3>
<form action="" mehtod="POST">
<div class="mt-2">
<input type="text" class="form-control" placeholder="请输入评论">
</div>
<div class="text-end mt-2">
<button type="button" class="btn btn-primary">评论</button>
</div>
</form>
</div>
<div class="mt-2">
<ul class="list-group list-group-flush">
<li class="list-group-item mt-3">
<div class="d-flex justify-content-between text-body-secondary">
<div class="user-info">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="40" height="40" alt="">
<span class="ms-2">小明</span>
</div>
<div class="create-time" style="line-height: 40px">2025/6/3 8:46</div>
</div>
<div class="mt-2">评论内容</div>
</li>
<li class="list-group-item mt-3">
<div class="d-flex justify-content-between text-body-secondary">
<div class="user-info">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="40" height="40" alt="">
<span class="ms-2">小明</span>
</div>
<div class="create-time" style="line-height: 40px">2025/6/3 8:46</div>
</div>
<div class="mt-2">评论内容</div>
</li>
</ul>
</div>
{% endblock %}
22.博客评论功能完成
完善后端视图函数
# blog(app)/ views.py
from django.views.decorators.http import require_http_methods, require_POST
from django.shortcuts import render, redirect, reverse
@require_POST
@login_required()
def pub_comment(request):
blog_id = request.POST.get("blog_id")
content = request.POST.get("content")
BlogComment.objects.create(content=content, blog_id=blog_id, author=request.user)
# 重新加载博客详情页
return redirect(reverse("blog:blog_detail", kwargs={'blog_id': blog_id}))
# blog(app)/ models.py
class BlogComment(models.Model):
content = models.TextField(verbose_name="内容")
pub_time = models.DateTimeField(auto_now_add=True, verbose_name="发布时间")
# 添加 related_name 可以通过 blog.comments 拿到一个 博客下的所有评论
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name="comments", verbose_name="所属博客")
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
修改blog_detail.html
{% extends "base.html" %}
{% block main %}
<h1>{{ blog.title }}</h1>
<hr>
<div class="mt-2">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt="">
<span class="ms-2">{{ blog.author.username }}</span>
<span class="ms-2">于</span>
<span class="ms-2">{{ blog.pub_time|date:"Y年m月d日 h时i分" }}</span>
</div>
<hr>
<div class="py-2">
{{ blog.content|safe }}
</div>
<hr>
<div class="mt-2">
<h3>评论:({{ blog.comments.all | length }})</h3>
<form action="{% url 'blog:pub_comment' %}" method="POST">
{% csrf_token %}
<input type="hidden" name="blog_id" value="{{ blog.id }}">
<div class="mt-2">
<input type="text" class="form-control" placeholder="请输入评论" name="content">
</div>
<div class="text-end mt-2">
<button type="submit" class="btn btn-primary">评论</button>
</div>
</form>
</div>
<div class="mt-2">
<ul class="list-group list-group-flush">
{% for comment in blog.comments.all %}
<li class="list-group-item mt-3">
<div class="d-flex justify-content-between text-body-secondary">
<div class="user-info">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="40" height="40" alt="">
<span class="ms-2">{{ comment.author.username }}</span>
</div>
<div class="create-time" style="line-height: 40px">{{ comment.pub_time | date:"Y年m月d日 h时i分" }}</div>
</div>
<div class="mt-2">{{ comment.content }}</div>
</li>
{% endfor %}
</ul>
</div>
{% endblock %}
23.代码高亮功能
使用 这个网站 https://highlightjs.org/ 里面的 js 代码, 选择需要的编程的高亮显示,下载


{% block head %}
<!-- 更换不同的高亮主题,可以更改 下面一行style 后的 css 文件-->
<link rel="stylesheet" href="{% static 'highlight/styles/default.min.css' %}">
<link rel="stylesheet" href="{% static 'highlight/styles/github-dark-dimmed.min.css' %}">
<script src="{% static 'highlight/highlight.min.js' %}"></script>
{% endblock %}
<!-- 如下代码放在html 代码的末尾, 是为了让所有代码加载完成后, 再执行 javascript 代码-->
<script>
hljs.highlightAll()
</script>
{% extends "base.html" %}
{% block head %}
<link rel="stylesheet" href="{% static 'highlight/styles/default.min.css' %}">
<script src="{% static 'highlight/highlight.min.js' %}"></script>
{% endblock %}
{% block main %}
<h1>{{ blog.title }}</h1>
<hr>
<div class="mt-2">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt="">
<span class="ms-2">{{ blog.author.username }}</span>
<span class="ms-2">于</span>
<span class="ms-2">{{ blog.pub_time|date:"Y年m月d日 h时i分" }}</span>
</div>
<hr>
<div class="py-2">
{{ blog.content|safe }}
</div>
<hr>
<div class="mt-2">
<h3>评论:({{ blog.comments.all | length }})</h3>
<form action="{% url 'blog:pub_comment' %}" method="POST">
{% csrf_token %}
<input type="hidden" name="blog_id" value="{{ blog.id }}">
<div class="mt-2">
<input type="text" class="form-control" placeholder="请输入评论" name="content">
</div>
<div class="text-end mt-2">
<button type="submit" class="btn btn-primary">评论</button>
</div>
</form>
</div>
<div class="mt-2">
<ul class="list-group list-group-flush">
{% for comment in blog.comments.all %}
<li class="list-group-item mt-3">
<div class="d-flex justify-content-between text-body-secondary">
<div class="user-info">
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="40" height="40" alt="">
<span class="ms-2">{{ comment.author.username }}</span>
</div>
<div class="create-time" style="line-height: 40px">{{ comment.pub_time | date:"Y年m月d日 h时i分" }}</div>
</div>
<div class="mt-2">{{ comment.content }}</div>
</li>
{% endfor %}
</ul>
</div>
<script>
hljs.highlightAll()
</script>
{% endblock %}


24.搜索功能和首页视图函数书写
在 模型类中指定 查找的时候 倒序排序
# blog(app)/ models.py
class Blog(models.Model):
title = models.CharField(max_length=200, verbose_name="标题")
content = models.TextField(verbose_name="内容")
pub_time = models.DateTimeField(auto_now_add=True, verbose_name="发布时间")
category = models.ForeignKey(BlogCategory, on_delete=models.CASCADE, verbose_name="分类")
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
def __str__(self):
return self.title
class Meta:
verbose_name = "博客"
verbose_name_plural = verbose_name
# 查找博客时, 根据发布时间倒序排序
ordering = ['-pub_time']
class BlogComment(models.Model):
content = models.TextField(verbose_name="内容")
pub_time = models.DateTimeField(auto_now_add=True, verbose_name="发布时间")
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name="comments", verbose_name="所属博客")
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
def __str__(self):
return self.content
class Meta:
verbose_name = "博客评论"
verbose_name_plural = verbose_name
# 评论根据评论时间倒序排序
ordering = ['-pub_time']
search 视图函数
# blog(app)/ vies.py
@require_GET
def search(request):
# /search?q=xxx
q = request.GET.get("q")
# 从博客的标题和内容中查找含有q 关键字的博客
if q:
blogs = Blog.objects.filter(Q(title__icontains=q) | Q(content__icontains=q)).all()
return render(request, 'index.html', context={"blogs": blogs})
else:
return redirect(reverse('blog:index'))
视图函数和url 的映射
from django.urls import path
from .import views
# 命名空间
app_name = "blog"
urlpatterns = [
path("", views.index, name="index"),
path('blog/detail/<int:blog_id>', views.blog_detail, name="blog_detail"),
path('blog/pub', views.pub_blog, name='blog_pub'),
path("blog/comment/pub", views.pub_comment, name="pub_comment"),
path("blog/search", views.search, name="search")
]
修改 base.html 中的search 按钮 action 提交的视图函数
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search" action="{% url 'blog:search' %}" method="GET">
<input type="search" class="form-control" name='q' placeholder="搜索" aria-label="Search">
</form>
首页视图函数完善
# blog(app)/ views.py
def index(request):
blogs = Blog.objects.all()
return render(request, "index.html", context={"blogs": blogs})
index.html 页面
{% extends 'base.html' %}
{% block title %}
博客首页
{% endblock %}
{% block main %}
<h1>博客列表</h1>
<div class="row row-cols-2 row-gap-4">
{% for blog in blogs %}
<div class="col">
<div class="card">
<div class="card-header">
<a href="{% url 'blog:blog_detail' blog_id =blog.id %}"> {{ blog.title }}</a>
</div>
<div class="card-body" style="height: 100px">
<p class="card-text">{{ blog.content | striptags | truncatechars:200 }}</p>
</div>
<div class="card-footer text-body-secondary d-flex justify-content-between">
<div>
<img src="{% static "img/avator.png" %}" class="rounded-circle" width="30" height="30" alt="">
{{ blog.author.username }}
</div>
<div>
{{ blog.pub_time | date: "Y年m月d日 h时i分" }}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}




















浙公网安备 33010602011771号