众所周知,Python是最容易上手的语言
众所周知,Django是Python功能最全的Web框架之一
Django一开始其实就是专门为了CMS而开发出来的,这就意味着用Django开发一个博客将变得非常容易。
本文就带大家快速的实现一个简单的博客系统:
本系统将包含博客的三个核心功能模块:文章管理,用户注册登录管理以及评论管理。
废话不多说,下面直接开始:
工欲善其事必先利其器,首先我们先准备下开发软件和环境。
开发环境准备
- 服务端:Python 3.8
- Web框架:Django 3.2
- 数据库:MySQL mysql-8.0.13-winx64
- 开发工具: Pycharm
创建Django博客项目
1.新建一个空白Pycharm项目文件夹比如:E:\PycharmProjects
2.打开Pycharm,从Pycharm打开我们刚刚创建的空白文件夹:PycharmProjects
3.在Pycharm的命令行输入界面输入创建Django项目的命令:
django-admin startproject DjangoBlog
?至此我们创建一个新的项目: DjangoBlog
数据库创建和连接配置
1. 数据库创建
Django只能操作到数据表级别,不能操作到数据库级别,所以需要手工创建一个数据库:djangoblog
我们可以通过命令行创建一个数据库:
1) 进入mysql安装文件夹的bin 子文件夹目录:
比如:D:\Program Files\mysql-8.0.13-winx64\bin
2) 连接数据库:
mysql -u root -p Enter password:******
3) 连接登录成功后通过命令创建一个数据库:?djangoblog
CREATE DATABASE IF NOT EXISTS?djangoblog?DEFAULT CHARSET utf8;
或者通过其他的数据库管理工具,比如SQLlog工具创建一个数据库: djangoblog
创建完成后可以通过SQLlog看到对应数据库。
?
2.?Django数据库连接配置
Django使用MySQL需要mysql 驱动,如果你没安装 mysql 驱动,可以执行以下命令安装:
pip install pymysql
?1) 进入DjangoBlog 项目下的DjangoBlog文件夹,打开setting.py 文件,找到DATABASES配置项:
2) 修改DATABSES配置项为如下内容:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'djangoblog', # 数据库名称
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'USER': 'root', # 数据库用户名
'PASSWORD': '123456', # 数据库密码
}
}
3) Django 使用 pymysql 模块连接 mysql 数据库:
在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()
?至此,我们创建了一个Django项目DjangoBlog用于我们后续的博客系统开发的程序编写,同时为此项目创建了一个MySQL数据库:djangoblog用于我们程序开发过程中的数据存放和处理。
实现文章管理功能
文章管理包括如下几个功能:
- 文章列表显示
- 文章详情查看
- 文章的创建
- 文章的更新和删除
创建APP
在Django中的一个app代表一个功能模块,Django 规定,如果要使用模型,必须要创建一个 app。
在命令行中输入python manage.py startapp article指令,创建名为article的app:
python manage.py startapp article
用于实现文章管理功能
?
注册APP
在 settings.py 中找到INSTALLED_APPS这一项,将新创建的article添加到项目的App列表,如下:
?
定义模型
我们之前创建了一个空白的数据库djangoblog,这一步我们通过Django的模型来完成数据库表的创建.
因为Django对模型和目标数据库之间有自身的映射规则,如果自己在数据库中创建数据表,可能不一定符合Django的建表规则,从而导致模型和目标数据库无法建立通信联系.
所以最好我们在Django项目中还是通过Django模型来创建对应数据库表
本步骤我们定义两个模型:
打开article/models.py文件,输入如下代码:
from django.db import models
# 导入内建的User模型。
from django.contrib.auth.models import User
# timezone 用于处理时间相关事务。
from django.utils import timezone
# 博客文章数据模型
class ArticlePost(models.Model):
# 文章作者。参数 on_delete 用于指定数据删除的方式
author = models.ForeignKey(User, on_delete=models.CASCADE)
# 文章标题。models.CharField 为字符串字段,用于保存较短的字符串,比如标题
title = models.CharField(max_length=100)
# 文章正文。保存大量文本使用 TextField
body = models.TextField()
# 文章创建时间。参数 default=timezone.now 指定其在创建数据时将默认写入当前的时间
created = models.DateTimeField(default=timezone.now)
# 文章更新时间。参数 auto_now=True 指定每次数据更新时自动写入当前时间
updated = models.DateTimeField(auto_now=True)
# 内部类 class Meta 用于给 model 定义元数据
class Meta:
# ordering 指定模型返回的数据的排列顺序
# '-created' 表明数据应该以倒序排列
ordering = ('-created',)
# 函数 __str__ 定义当调用对象的 str() 方法时的返回值内容
def __str__(self):
# return self.title 将文章标题返回
return self.title
接着在命令行中输入命令让 Django知道我们自定义模型有一些变更,并根据我们自定义app的模型生成创建数据表的脚本:
python manage.py makemigrations
最后通过命令创建app模型对应的数据库表:
python manage.py migrate
?执行成功后我们可以在数据库表中看到Django自动生成了数据库表如下图:
?至此,我们已经成功创建好了APP并完成了用于文章管理的数据库表创建,下面我们利用Django函数和模板分别实现文章管理的各个具体的功能。
Django 中视图的概念是「一类具有相同功能和模板的网页的集合。
比如,在一个博客应用中,你可能会创建如下几个视图:
博客首页:展示最近的几项内容。
内容“详情”页:详细展示某项内容。
评论处理器:用于响应为一项内容添加评论的操作。
这些需求都靠视图(View)来完成。
网页都是从视图派生而来。每一个视图表现为一个简单的Python函数,它必须要做的只有两件事:返回一个包含被请求页面内容的 HttpResponse对象,或者抛出一个异常,比如 Http404 。
视图函数中的request与网页发来的请求有关,里面包含get或post的内容、用户浏览器、系统等信息。Django调用article_list函数时会返回一个含字符串的 HttpResponse对象。
文章列表显示
定义视图函数
在article/views.py文件中创建article_list视图函数用于文章列表展示:
from django.shortcuts import render
# 导入数据模型ArticlePost
from .models import ArticlePost
def article_list(request):
# 取出所有博客文章
articles = ArticlePost.objects.all()
# 需要传递给模板(templates)的对象
context = { 'articles': articles }
# render函数:载入模板,并返回context对象
return render(request, 'article/list.html', context)
?配置访问路由URL
url可以理解为访问网站时输入的网址链接,配置好url后Django才知道怎样定位app。
打开DjangoBlog目录下的urls.py,增加以下代码:
from django.contrib import admin
# 记得引入include
from django.urls import path, include
# 存放映射关系的列表
urlpatterns = [
path('admin/', admin.site.urls),
# 新增代码,配置app的url
path('article/', include('article.urls', namespace='article')),
]
以上代码通过path将根路径为article的访问都分发给article这个app去处理。但是app通常有多个页面地址,因此还需要app自己也有一个路由分发。
接着在article文件夹中创建urls.py,在里面输入:
from django.urls import path
# 引入views.py
from . import views
# 正在部署的应用的名称
app_name = 'article'
urlpatterns = [
# path函数将url映射到视图
path('article-list/', views.article_list, name='article_list'),
]
?模板和前端静态资源准备及配置
本文前后端不分离,前端框架选用当前比较受欢迎的Bootstrap.
最新版本的Bootstrap :下载地址,并解压。
在项目根目录下新建一个文件夹static用于存放前端模板静态资源,同时将相关前端资源导入文件夹.
把刚才解压出来的css和js两个文件夹复制进去。
因为bootstrap.js依赖 jquery.js 和 popper.js 才能正常运行,因此这两个文件我们也需要一并下载保存。
附上官网下载链接(进入下载页面,复制粘贴代码到新文件即可):
jquery.js
popper.js
完成后Static文件夹结构如下图:
Django模板创建:
在根目录下的templates/中,新建三个文件:
- base.html是整个项目的模板基础,所有的网页都从它继承;
- header.html是网页顶部的导航栏;
- footer.html是网页底部的注脚。
分别编写三个静态HTML文件代码如下:
templates/base.html:
<!-- 载入静态文件-->
{% load static %}
<!DOCTYPE html>
<!-- 网站主语言 -->
<html lang="zh-cn">
<head>
<!-- 网站采用的字符编码 -->
<meta charset="utf-8">
<!-- 预留网站标题的位置 -->
<title>{% block title %}{% endblock %}</title>
<!-- 引入bootstrap的css文件 -->
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
</head>
<body>
<!-- 引入导航栏 -->
{% include 'header.html' %}
<!-- 预留具体页面的位置 -->
{% block content %}{% endblock content %}
<!-- 引入注脚 -->
{% include 'footer.html' %}
<!-- bootstrap.js 依赖 jquery.js 和popper.js,因此在这里引入 -->
<script src="{% static 'jquery/jquery-3.6.0.js' %}"></script>
<!--
popper.js 采用 cdn 远程引入,意思是你不需要把它下载到本地。
在实际的开发中推荐静态文件尽量都使用 cdn 的形式。
教程采用本地引入是为了让读者了解静态文件本地部署的流程。
-->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1-lts/dist/umd/popper.min.js"></script>
<!-- 引入bootstrap的js文件 -->
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
</body>
</html>
templates/header.html:
<!-- 定义导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<!-- 导航栏商标 -->
<a class="navbar-brand" href="#">我的博客</a>
<div class="form-inline">
<form role="search" class="navbar-form-custom" method="post" action="#">
<div class="form-group">
<input type="text" placeholder="Search..." class="form-control" name="top-search" id="top-search">
</div>
</form>
</div>
<!-- 导航入口 -->
<div>
<ul class="navbar-nav">
<!-- 条目 -->
<li class="nav-item">
<a class="nav-link" href="#">文章</a>
</li>
</ul>
</div>
</div>
</nav>
templates/header.html:
<!-- 定义导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<!-- 导航栏商标 -->
<a class="navbar-brand" href="#">我的博客</a>
<div class="form-inline">
<form role="search" class="navbar-form-custom" method="post" action="#">
<div class="form-group">
<input type="text" placeholder="Search..." class="form-control" name="top-search" id="top-search">
</div>
</form>
</div>
<!-- 导航入口 -->
<div>
<ul class="navbar-nav">
<!-- 条目 -->
<li class="nav-item">
<a class="nav-link" href="#">首页</a>
</li>
</ul>
</div>
</div>
</nav>
templates/footer.html:
{% load static %}
<!-- Footer -->
<div>
<br><br><br>
</div>
<footer class="py-3 bg-dark fixed-bottom">
<div class="container">
<p class="m-0 text-center text-white">Copyright © DjangoBlog 2021</p>
</div>
</footer>
上述三个文件是网站页面的通用组件模块,基本上每个页面都不会变,我们把他们独立出来。
我们编写Django模板时可以直接继承对应的通用模板组件。
下面我们编写一个博客列表的显示模板用于显示博客文章列表:
在templates文件夹中新建一个article文件夹用于存放文章展示相关的HTML模板,然后在article文件中创建一个list.html模板文件。
模板所在路径article/list.html 对应我们的之前在article/views中视图函数article_list定义的载入模板地址。
?模板article/list.html编写代码如下:
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
首页
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 定义放置文章标题的div容器 -->
<div class="container">
{% for article in articles %}
<div class="row mt-2">
<!-- 文章内容 -->
<div class="col-sm-12">
<!-- 卡片容器 -->
<div class="card h-100">
<!-- 标题 -->
<!-- <h4 class="card-header">{{ article.title }}</h4>-->
<!-- 摘要 -->
<div class="card-body">
<h4 class="card-title">{{ article.title }}</h4>
<p class="card-text">{{ article.body|slice:'100' }}...</p>
<a href="{% url 'article:article_detail' article.id %}" class="card-link">阅读本文</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock content %}
代码说明:
当我们通过url访问list.html时,顶部的{% extends "base.html" %}告诉Django:“这个文件是继承自base.html” Django会去渲染base.html文件: 其中的{% include 'header.html' %}表明这里需要加入header.html的内容 {% include 'footer.html' %}加入footer.html的内容 {% block content %}{% endblock content %}表明这里应该加入list.html中的对应块的内容
?
?博客文章新建功能
定义视图函数
在article/views.py文件中创建article_create视图函数用于新建文章:
def article_create(request):
if request.method == 'POST':
new_article_title = request.POST.get('title')
new_article_body = request.POST.get('body')
new_article_author = User.objects.get(id=1)
models.ArticlePost.objects.create(title=new_article_title, body=new_article_body,author=new_article_author)
return redirect("article:article_list")
# 如果用户请求获取数据
else:
return render(request, 'article/create.html')
配置访问路由URL
在article/urls.py 中增加写文章的路由
urlpatterns = [
# 写文章
path('article-create/', views.article_create, name='article_create'),
]
创建前端模板
首先我们新建一个create.html的文件用于新建文章,在此文件中我们将定义一个新建文章的表单,用于调用我们前面定义的创建文章的视图函数,同时将数据传递到数据库。
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %} {% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %} 写文章 {% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 写文章表单 -->
<div class="container">
<div class="row">
<div class="col-12">
<br>
<!-- 提交文章的表单 -->
<form method="post" action=".">
<!-- Django中需要POST数据的地方都必须有csrf_token -->
{% csrf_token %}
<!-- 文章标题 -->
<div class="form-group">
<!-- 标签 -->
<label for="title">文章标题</label>
<!-- 文本框 -->
<input type="text" class="form-control" id="title" name="title">
</div>
<!-- 文章正文 -->
<div class="form-group">
<label for="body">文章正文</label>
<!-- 文本区域 -->
<textarea type="text" class="form-control" id="body" name="body" rows="12"></textarea>
</div>
<!-- 提交按钮 -->
<button type="submit" class="btn btn-primary">完成</button>
</form>
</div>
</div>
</div>
{% endblock content %}
接着我们在导航栏对应的模板header.html中添加一个创建文章的入口
<!-- 导航入口 -->
<div>
??? <ul class="navbar-nav">
??????? <li class="nav-item">
??????????? <a class="nav-link" href="{% url 'article:article_create' %}">创作</a>
??????? </li>
??????? <li class="nav-item">
??????????? <a class="nav-link" href="{% url 'article:article_list' %}">首页</a>
??????? </li>
??? </ul>
</div>
?
博客文章详情显示功能
?定义视图函数
在article/views.py文件中创建 article_detail 视图函数用于显示文章:
# 文章详情
def article_detail(request, id):
# 取出相应的文章
article = ArticlePost.objects.get(id=id)
# 需要传递给模板的对象
context = { 'article': article }
# 载入模板,并返回context对象
return render(request, 'article/detail.html', context)
配置访问路由URL
在article/urls.py 中显示文章的路由
urlpatterns = [
# 写文章
path('article-create/', views.article_create, name='article_create'),
# 文章详情
path('article-detail/<int:id>/', views.article_detail, name='article_detail'),
]
创建前端模板
首先我们新建一个detail.html的文件用于文章详情显示。
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
文章详情
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 文章详情 -->
<div class="container">
<div class="row">
<!-- 标题及作者 -->
<h1 class="col-12 mt-4 mb-4">{{ article.title }}</h1>
<div class="col-12 alert alert-primary">
<a>作者:{{ article.author }}</a>
<a>{{ article.created|date:'Y-m-d H:i:s' }}</a>
</div>
<!-- 文章正文 -->
<div class="col-12">
<p>{{ article.body }}</p>
</div>
</div>
</div>
?博客文章更新和删除
?定义视图函数
在article/views.py文件中创建article_create视图函数用于显示文章:
# 删文章
def article_delete(request, id):
# 根据 id 获取需要删除的文章
article = ArticlePost.objects.get(id=id)
# 调用.delete()方法删除文章
article.delete()
# 完成删除后返回文章列表
return redirect("article:article_list")
# 更新文章
def article_update(request, id):
# # 获取需要修改的具体文章对象
article = ArticlePost.objects.get(id=id)
# 判断用户是否为 POST 提交表单数据
if request.method == "POST":
new_article_title = request.POST.get('title')
new_article_body = request.POST.get('body')
article.title = new_article_title
article.body = new_article_body
article.save()
# 完成后返回到修改后的文章中。需传入文章的 id 值
return redirect("article:article_detail", id=id)
else:
# 赋值上下文,将 article 文章对象也传递进去,以便提取旧的内容
context = {'article': article}
return render(request, 'article/update.html', context)
配置访问路由URL
在article/urls.py 中显示文章的路由
urlpatterns = [
url(r'^$', views.article_list),
# path函数将url映射到视图
path('article-list/', views.article_list, name='article_list'),
# 文章详情
path('article-detail/<int:id>/', views.article_detail, name='article_detail'),
# 写文章
path('article-create/', views.article_create, name='article_create'),
# 删除文章
path('article-delete/<int:id>/', views.article_delete, name='article_delete'),
# 更新文章
path('article-update/<int:id>/', views.article_update, name='article_update'),
]
创建前端模板
首先我们新建一个update.html的文件用于文章更新。
{% extends "base.html" %} {% load static %}
{% block title %} 更新文章 {% endblock title %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-12">
<br>
<form method="post" action=".">
{% csrf_token %}
<div class="form-group">
<label for="title">文章标题</label>
<!-- 在 value 属性中指定文本框的初始值为旧的内容,即 article 对象中的 title 字段 -->
<input type="text" class="form-control" id="title" name="title" value="{{ article.title }}">
</div>
<div class="form-group">
<label for="body">文章正文</label>
<!-- 文本域不需要 value 属性,直接在标签体中嵌入数据即可 -->
<textarea type="text" class="form-control" id="body" name="body" rows="12">{{ article.body }}</textarea>
</div>
<button type="submit" class="btn btn-primary">完成</button>
</form>
</div>
</div>
</div>
{% endblock content %}
更新和删除文章的入口都包含在Detail.html 中,我们修改Detail.html代码如下:
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
文章详情
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 文章详情 -->
<div class="container">
<div class="row">
<!-- 标题及作者 -->
<h1 class="col-12 mt-4 mb-4">{{ article.title }}</h1>
<div class="col-12 alert alert-primary">
<a>作者:{{ article.author }}</a>
<a>{{ article.created|date:'Y-m-d H:i:s' }}</a>
· <a href="#" data-toggle="modal" data-target="#myModal">删除文章</a>
· <a href="{% url "article:article_update" article.id %}">编辑文章</a>
</div>
<!-- 文章正文 -->
<div class="col-12">
<p>{{ article.body }}</p>
</div>
</div>
</div>
<!-- 模态框 -->
<div class="modal fade " id="myModal">
<div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content">
<!-- 模态框头部 -->
<div class="modal-header">
<h4 class="modal-title">确认删除</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- 模态框主体 -->
<div class="modal-body">
确认删除文章?
</div>
<!-- 模态框底部 -->
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" data-dismiss="modal"
onclick="confirm_delete()">
确定
</button>
</div>
</div>
</div>
</div>
<script>
// 删除文章的函数
function confirm_delete() {
location.href='{% url "article:article_delete" article.id %}'
}
</script>
{% endblock content %}
?
?至此,我们已经实现了博客管理系统文章的新建,更新,删除和展示功能。
接下来有时间我们继续更新用户注册,登录以及评论管理功能。
|