文章目录
1. 写在前面
自己是一只工科狗,正在自学python,想找些项目去做做,就心血来潮的想搭个自己的博客,一路摸爬滚打终于搞出个最基本的版本,把这些心得分享出来,对于自己也是一个很好的回顾与总结的过程。
这个教程的前提环境是python3+vscode,两者的下载与配置搜索网上其他教程即可。
十分感谢以下三篇参考博客给我的莫大帮助:
最主要:python+django搭建博客;
vscode配置django环境;
mysql安装教程。
2. 基本环境配置
2.1 MySQL安装与配置
●官网下载最新版本
推荐MySQL community server 8版本。建议配合上述的MySQL安装教程一起使用。
●配置my.ini文件
采用解压的方式安装的MySQL是没有自动配置相关文件的,需要手动配置。在安装的根目录新建一个空白的my.ini文件,在其中写入如下代码,其中basedir和datadir是你安装的目录:
[mysqld]
port=3306
basedir=D:\mysql8
datadir=D:\mysql8\Data
max_connections=200
max_connect_errors=10
character-set-server=utf8mb4
default-storage-engine=INNODB
default_authentication_plugin=mysql_native_password
[mysql]
default-character-set=utf8mb4
[client]
port=3306
default-character-set=utf8mb4
●初始化MySQL
以管理员身份运行cmd(在C:\Windows\System 32中右键cmd),并打开MySQL安装的根目录,输入命令:mysqld --initialize --console
。系统会生成一段临时密码,注意保存这段临时密码(不包含首位空格),之后需要用到。
●安装+启动MySQL服务
安装:mysqld --install
启动:net start mysql
关闭:net stop mysql
●连接MySQL与修改密码
连接使用Navicat(中文破解版),新建一个连接,配置如下:
配置项 | 内容 |
---|---|
连接名 | 随便写 |
主机名 | localhost |
端口 | 3306 |
用户名 | root |
密码 | 刚才的那段临时密码 |
注意:在配置时可能会出现此报错:your password has expired.To log in you must change itusing a client that supports expired passwords。出现此问题的原因是MySQL的密码默认是有时限的,过了时间密码就会过期。解决方法如下:
打开cmd,进入MySQL的安装目录,输入mysql -uroot -p,系统会让你输入密码,输入临时密码后即进入MySQL中。
修改密码:ALTER USER 'root'@'localhost' IDENTIFIED BY ' ';
密码不过期:alter user 'root'@'localhost'password expire never;
退出:exit();
再用Navicat连接即可成功。
2.2 django安装与配置
●建立虚拟环境
新建myBlog文件夹,在cmd中打开该文件,输入:D:\python37\Scripts\virtualenv.exe django_env
前面的目录是你python的目录
●安装django
cd django_env\Scripts
打开环境激活目录
activate
激活
pip install django
安装django
●vscode配置
在myBlog文件下建立blog文件夹,在vscode中打开blog文件夹,点击配置项目,会生成两个配置文件:
launch.json 添加一个端口:
setting.json 添加python的启动地址:
注:上图中的地址并不是该项目的地址,按照自己的设置进行配置,且注意是双斜杠而不是单斜杠。
●测试
点击运行,成功后在网页中输入127.0.0.1:8080,可以看到网页已经成功显示了出来。
3. 建立博客应用
在完成了准备工作后,现在开始正式的用django进行博客的搭建了。
首先在blog文件夹中(在cmd中打开)建立项目与应用:
python manage.py startproject blog
建立项目
python manage.py startapp blogapp
建立应用
myBlog文件夹的目录结构如下:
在blog\setting.py中,找到INSTALLED_APPS,将新建的应用加入:
INSTALLED_APPS = [ 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blogapp',
]
4. 建立数据库模型
4.1 设计数据库表结构
博客的文章包含了各种基本信息:标题、正文、作者、发布时间,它还应该有对应的分类和一些标签,初级版本就只囊括这些基本要素。其对应关系为:一篇文章只对应一个分类,但是可以有多个标签。接下来就要创建文章、分类、标签这三个大类。
4.2 编写对应代码
创建不同数据库表的操作都在blogapp/models.py中。以分类为例:
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
Category就是分类的数据库表,它继承了models.Model类,CharField是django定义的一种数据类型,是字符型的一种,一般用来储存较短的字符串,max_length参数规定了允许存储的最大数量。
完整的建立如下:
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
class Tag(models.Model):
name = models.CharField(max_length=100)
class Post(models.Model):
title = models.CharField(max_length=70)
body = models.TextField()
created_time = models.DateTimeField()
modified_time = models.DateTimeField()
excerpt = models.CharField(max_length=200,blank=True)
category = models.ForeignKey(Category,on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag,blank=True)
author = models.ForeignKey(User,on_delete=models.CASCADE)
简单解释一下Post类中的代码:
Post类定义了(从上到下):标题、正文、创建时间、修改时间、摘要、分类、标签、作者;TextField相比于CharField可存储更多的文本,DateTimeField为存储时间的数据类型;
blank=True允许空值的出现;
ForeignKey为一对多或多对一的关联关系,一篇文章只有一个作者和一个分类,但是一个作者或一个分类可以对应很多文章,一般在’多’的类中定义’一’,on_delete参数代表在删除一模型对象时会对相关联的多对象执行什么操作,默认为级联删除models.CASCADE,表示当删除一时,对应的多都会被删除。ManyToManyField表示多对多的级联关系;
django.contrib.auth为django内置应用,用于处理网站用户注册登录等,User是已经写好的用户模块。
4.3 迁移数据库
先将数据库设置为MySQL,即更改blog/settings.py的配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django', #数据库名字
'USER': 'root', #账号
'PASSWORD': '123456', #密码
'HOST': '127.0.0.1', #IP
'PORT': '3306', #端口
}
}
同时在blogapp/init.py中更改:
import pymysql
pymysql.install_as_MySALdb()
执行迁移数据库命令,在cmd中打开myBlog/blog文件夹,分别运行:
python manage.py makemigrations
python manage.py migrate
第一个命令的作用会在blogapp/migrations文件夹下生成0001_initial.py文件,是django用来记录对模型做了哪些修改的文件。
第二个命令通过检验migrations文件夹下的内容来得知我们对数据库的操作,并转化为数据库语言作用于MySQL。在该命令中,我们可以发现它除了对我们自己写的应用进行了操作,还对其他内置应用进行了操作,具体地可以在之前地INSTALLED_APPS里找到。
4.4 对数据进行读取等操作
接下来,实际上已经可以在其中读写数据了,此处不多赘述,有想做一些基本了解的,可以参考python+django搭建博客的4.3-4.4部分。
5. 制作博客的首页视图
5.1 django处理HTTP请求
这一节大家只需要看看就好,因为之后会使用模板,写这一节的目的是让大家了解django的HTTP处理机理。
这一步主要包括:django如何接收HTTP请求;如何处理HTTP请求;如何生成HTTP响应。
1.绑定url和视图函数
django通过blogapp/urls.py(需要自己新建,注意不要和blog/urls.py混淆,这个是整个工程的配置文件)来接收并处理用户的HTTP请求,该文件中记录了不同网址及其所对应的处理函数。当用户访问某个网址时,django就会在这个文件中找对应的网址,如果找到了就会调用对应的处理函数(又被叫做视图函数)。具体做法如下:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$',views.index,name='index'),
]
urlpatterns中记录了各种网址及其对应的视图函数,url函数则将两者绑定在一起,第一个参数为正则表达式,对于我们的本地开服务器域名,如:http://127.0.0.1:8080,当输入后,django会将协议、域名和端口号都去掉,此时只剩一个空字符串,而该正则正是匹配空字符串的意思(以空字符串开头和结尾),二者匹配后就会调用views.index中的视图函数,name为视图函数定义了一个名字。
2. 编写视图函数
接下来就要编写上面说的views.index视图函数了,视图函数在blogapp/views.py中:
from django.shortcuts import HttpResponse
def index(request):
return HttpResponse('Welcome!')
这就是视图函数的基本机理:request参数是django为我们封装的HttpRequest类的一个实例,是HTTP请求;HttpResponse是HTTP响应的封装类,我们只需要传入想表达的文字就好。
3. 配置URL
之前我们创建了urls.py文件,也绑定了URL与index,接下来要让django知道我们执行了这些操作,在之前说的blog/urls.py中写下如下代码:
from django.contrib import admin
from django.conf.urls import url,include
urlpatterns = [
url('admin/',admin.site.urls),
url('',include('blogapp.urls')),
]
url函数的若干参数拼合起来就是完整的路径,因此就在系统urls中配置了blogapp下的urls。
4. 运行
两种方法都可以运行服务器:
运行python manage.py runserver
在vscode中打开myBlog/blog文件夹,按F5编译
5.2 使用django模板系统
我们现在已经完成了对博客的基本搭建,但是这样构建的太过简单,为了实现一些华丽的视觉效果,我们需要用到django的模板系统。我们可以把大段的文本写到一个文件里,然后django自行读取并把内容传给HttpResponse。下一章我们将结合模板来做出我们的页面。
6. 基于模板制作首页视图
再次回忆如何基于django制作首页视图:配置url–>绑定url和视图函数–>在系统urls文件中引入–>基于模板编写视图函数–>返回HTTP响应。接下来我们基于这个思路去实现我们的首页视图。
6.1 首页的视图函数
首页的视图函数如下:
from django.shortcuts import render
from .models import Post
def index(request):
post_list = Post.objects.all().order_by('-created_time')
return render(request,'blog/index.html',{'post_list':post_list})
objects是django内自定义的模型类的对象,数据类型是QuerySet,即查询集,这种数据类型能够提高查询效率。
第一行代码的意思就是通过all()从数据库获取了所有文章,再通过order_by进行排序,排序字段是created_time,同时负号代表逆序排列(一般来说博客文章列表是按照发布时间倒序排列的)。
第二行代码中,render()的作用是给定一个模板和上下文字典,返回一个模板渲染后的HttpResponse对象。
6.2 处理静态文件
博客模板采用网上的一套:点击下载。下载后把它放在blogapp/static/blog下(需要新建),我们要对它进行一系列的设置。
首先需要告诉django在哪里找模板,在settings.py中的TEMPLATES里,将’DIRS’补全:
...
'DIRS':[os.path.join(BASE_DIR,'blogapp','static')],
...
我们可以看到,把DIRS中的路径和上面render函数第二个参数的路径连起来就是我们模板实际的绝对路径了,这样django就可以找到模板在哪里了。
接下来我们需要更改静态文件的加载路径,否则浏览器中显示的样式会非常混乱。打开模板文件中的index.html文件,找到head标签包裹的内容(对这部分代码看不懂的同学可以看一下css的基本语法,最基本的不是很难),改为:
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
<title>Black & White</title>
<!-- meta -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- css -->
<link rel="stylesheet" href="{% static 'blog/css/bootstrap.min.css' %}" rel="external nofollow" rel="external nofollow" >
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" >
<link rel="stylesheet" href="{% static 'blog/css/pace.css' %}" rel="external nofollow" rel="external nofollow" >
<link rel="stylesheet" href="{% static 'blog/css/custom.css' %}" rel="external nofollow" >
<!-- js -->
<script src="{% static 'blog/js/jquery-2.1.3.min.js' %}"></script>
<script src="{% static 'blog/js/bootstrap.min.js' %}"></script>
<script src="{% static 'blog/js/pace.min.js' %}"></script>
<script src="{% static 'blog/js/modernizr.custom.js' %}"></script>
</head>
<body>
<!-- 其它内容 -->
<script src="{% static 'blog/js/script.js' %}"></script>
</body>
</html>
顶部的{% load staticfiles%}就是加载静态文件的代码,此外,我们可以看到href标签中被{%%}包裹起来的部分,这样改才可以正确引入路径。
6.3 修改模板
接下来我们需要在首页显示我们写的文章,具体做法就是使用之前传入的post_list变量,找到index.html的article标签,只留一个article标签,使用for循环把每篇文章都调用出来:
...
{% for post in post_list %}
<article class="post post-{{ post.pk }}">
...
</article>
{% empty %}
<div class="no-post">暂时还没有发布的文章</div>
{% endfor %}
...
pk是primary key的缩写,即每篇post所对应的索引值,我们无需显式定义,django会自动为我们添加。
对于标题,我们这样修改:
<h1 class="entry-title">
<a href="single.html" rel="external nofollow" >{{ post.title }}</a>
</h1>
对于其他span标签,即分类、发布时间、作者、评论和阅读量5个,做如下修改,评论和阅读不动,暂时无法更换:
<div class="entry-meta">
<span class="post-category"><a href="#">{{ post.category.name }}</a></span>
<span class="post-date"><a href="#"><time class="entry-date"
datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
<span class="post-author"><a href="#">{{ post.author }}</a></span>
<span class="comments-link"><a href="#">4 评论</a></span>
<span class="views-count"><a href="#">588 阅读</a></span>
</div>
摘要做如下修改:
<div class="entry-content clearfix">
<p>{{ post.excerpt }}</p>
<div class="read-more cl-effect-14">
<a href="#" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
</div>
</div>
7. 在django Admin后台发布文章
我们可以在django自带的Admin后台来发布我们自己的博客文章,只需要做一些设置即可。
7.1 注册模型
首先在cmd中输入python manage.py createsuperuser
,创建超级用户(具体操作可参照网上)。
之后需要在后台注册好我们创建的模型,即在blogapp/admin.py中输入:
from django.contrib import admin
from .models import Post,Category,Tag
admin.site.register(Post)
admin.site.register(Category)
admin.site.register(Tag)
之后就可以运行开发服务器了,输入127.0.0.1:8080/admin/,就进入到后台页面,可以增加分类、标签和文章,自行尝试一下就好。
7.2 定制Admin后台
当然,我们可以对admin有自定义的设置,比如希望它可以显示文章的详细信息,那么在admin.py添加如下代码:
from django.contrib import admin
from .models import Post,Category,Tag
class PostAdmin(admin.ModelAdmin):
list_display = ['title','created_time','modified_time','category','author']
admin.site.register(Post,PostAdmin)
admin.site.register(Category)
admin.site.register(Tag)
这样可以看到它能够显示文章的详细信息。
8. 博客文章详情页
做完首页后,我们可以继续制作详情页了,开发流程是一样的。
8.1 设置详情页url
对于详情页来说,每篇文章都对应了一个URL。即网站域名/post/1就是第一篇文章内容,以此类推,以这个规则在blogapp/urls.py中绑定URL和视图函数(这些操作是固定搭配,一般直接拿来用就好):
from django.conf.urls import url
from . import views
app_name = 'blogapp'
urlpatterns = [
url(r'^$',views.index,name='index'),
url(r'^post/(?P<pk>[0-9]+)/$',views.detail,name='detail'),
]
第二个url函数中的正则表达式,表示以post/开头,后面至少跟一个一位的数字,并且以/符号结尾。同时括号内表示命名捕获组,作用是把url中的指定字符串捕获并传给detail函数。
app_name则是视图函数命名空间,用来区分不同的视图函数。
为了能够按照规则生成每篇文章的url,我们可以在models.py中对Post类增加一个方法,具体为:
from django.urls import reverse
class Post(models.Model):
...
def get_absolute_url(self):
return reverse('blogapp:detail',kwargs={'pk':self.pk})
reverse函数的作用是解析视图函数的URL,第一个参数指定了视图函数,由于我们在detail函数中指定了替换准则,因此每篇文章的id会被pk所替换,从而生成自己的URL。
8.2 编写detail视图函数
detail视图函数在blogapp/views.py中编写:
from django.shortcuts import get_object_or_404
...
def detail(request,pk):
post = get_object_or_404(Post,pk=pk)
return render(request,'blog/detail.html',{'post':post})
detail视图函数和index函数类似,很容易理解。也就是说,详情页的流程是这样的:当用户输入一个网址时,url函数将pk值捕获,传入视图函数,查找数据库中是否存在这样的pk值所对应的网页,如果有则显示页面,没有则返回404页面。同时,在Post类中定义一个获得全路径的方法get_absolute_url,便于静态文件的编写。
8.3 编写详情页模板
把模板中的single.html改为detail.html,与index页面一样,需要进行一些改写。可以发现,如果我们有很多这样的页面,那么每次都修改一遍,会非常麻烦,但是我们可以采用模板继承的方法来消除重复操作。
8.4 模板继承
我们发现,index.html和detail.html文件除了main标签包裹的部分不同外,其他地方都是相同的,那么我们就可以把相同部分都放在base.html中,这样就可以省略很多重复操作。我们在同级目录下新建base.html文件,并将index.html的内容都拷进来,然后删除main标签内的内容,替换为:
...
<main class="col-md-8">
{% block main %}
{% endblock main %}
</main>
<aside class="col-md-4">
{% block toc %}
{% endblock toc %}
...
</aside>
...
block的作用就是占位,想不明白的话把它想象成类的继承与重写就可以。这样,当子类(之后说到的index.html和detail.html)块内写了东西,就会按子类来,如果没有东西,就不会显示这两行。
接下来,在index.html顶部写上{% extends 'base.html' %}
,这个意思就是继承了base,我们就只需要填block内部的东西就可以了:
{% extends 'blog/base.html' %}
{% block main %}
{% for post in post_list %}
<article class="post post-1">
...
</article>
{% empty %}
<div class="no-post">暂时没有发布文章!</div>
{% endfor %}
<!-- 简单分页效果 <div class="pagination-simple"> <a href="#">上一页</a>
<span class="current">第 6 页 / 共 11 页</span>
<a href="#">下一页</a>
</div>
-->
<div class="pagination">
...
</div>
{% endblock main %}
detail.html同理,我们需要填写main部分和目录toc部分,不过目前目录只是占位,之后再实现从文章中自动摘取目录:
{% extends 'blog/base.html' %}
{% block main %}
<article class="post post-1">
...
</article>
<section class="comment-area">
...
</section>
{% endblock main %}
{% block toc %}
<div class="widget widget-content">
<h3 class="widget-title">文章目录</h3>
<ul>
<li>
<a href="#" >教程特点</a>
</li>
<li>
<a href="#">谁适合这个教程</a>
</li>
<li>
<a href="#">在线预览</a>
</li>
<li>
<a href="#">资源列表</a>
</li>
<li>
<a href="#">获取帮助</a>
</li>
</ul>
</div>
{% endblock toc %}
同时,修改article标签下的内容,让其显示文章的实际数据:
<article class="post post-{{ post.pk }}">
<header class="entry-header">
<h1 class="entry-title">{{ post.title }}</h1>
<div class="entry-meta">
<span class="post-category"><a href="#">{{ post.category.name }}</a></span>
<span class="post-date"><a href="#"><time class="entry-date" datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
<span class="post-author"><a href="#">{{ post.author }}</a></span> <span class="comments-link"><a href="#">4 评论</a></span>
<span class="views-count"><a href="#">588 阅读</a></span>
</div>
</header>
<div class="entry-content clearfix">
{{ post.body }}
</div>
</article>
这样,我们就完成了一个博客的基本创建。
9. 总结
到目前为止,我们已经实现了博客的基本搭建,但是还有很多功能没有完成,比如:1.阅读数和评论量;2.目录部分等。这些功能我以后会继续探索研究,最近太忙,也没有时间继续做下去了。
写完以后发现写一遍比做一遍要花更久的时间,不过在写的过程我又学习了一遍,感觉收获很大,也基本上理清楚那些教程所说的东西了。共勉!