{{ post.excerpt }}
自己是一只工科狗,正在自学python,想找些项目去做做,就心血来潮的想搭个自己的博客,一路摸爬滚打终于搞出个最基本的版本,把这些心得分享出来,对于自己也是一个很好的回顾与总结的过程。
这个教程的前提环境是python3+vscode,两者的下载与配置搜索网上其他教程即可。
十分感谢以下三篇参考博客给我的莫大帮助:
最主要:python+django搭建博客;
vscode配置django环境;
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连接即可成功。
●建立虚拟环境
新建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,可以看到网页已经成功显示了出来。
在完成了准备工作后,现在开始正式的用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',
]
博客的文章包含了各种基本信息:标题、正文、作者、发布时间,它还应该有对应的分类和一些标签,初级版本就只囊括这些基本要素。其对应关系为:一篇文章只对应一个分类,但是可以有多个标签。接下来就要创建文章、分类、标签这三个大类。
创建不同数据库表的操作都在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是已经写好的用户模块。
先将数据库设置为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里找到。
接下来,实际上已经可以在其中读写数据了,此处不多赘述,有想做一些基本了解的,可以参考python+django搭建博客的4.3-4.4部分。
这一节大家只需要看看就好,因为之后会使用模板,写这一节的目的是让大家了解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编译
我们现在已经完成了对博客的基本搭建,但是这样构建的太过简单,为了实现一些华丽的视觉效果,我们需要用到django的模板系统。我们可以把大段的文本写到一个文件里,然后django自行读取并把内容传给HttpResponse。下一章我们将结合模板来做出我们的页面。
再次回忆如何基于django制作首页视图:配置url–>绑定url和视图函数–>在系统urls文件中引入–>基于模板编写视图函数–>返回HTTP响应。接下来我们基于这个思路去实现我们的首页视图。
首页的视图函数如下:
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对象。
博客模板采用网上的一套:点击下载。下载后把它放在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 %}
Black & White
{% static 'blog/css/bootstrap.min.css' %}" rel="external nofollow" rel="external nofollow" >
{% static 'blog/css/pace.css' %}" rel="external nofollow" rel="external nofollow" >
{% static 'blog/css/custom.css' %}" rel="external nofollow" >
顶部的{% load staticfiles%}就是加载静态文件的代码,此外,我们可以看到href标签中被{%%}包裹起来的部分,这样改才可以正确引入路径。
接下来我们需要在首页显示我们写的文章,具体做法就是使用之前传入的post_list变量,找到index.html的article标签,只留一个article标签,使用for循环把每篇文章都调用出来:
...
{% for post in post_list %}
{ { post.pk }}">
...
{% empty %}
暂时还没有发布的文章
{% endfor %}
...
pk是primary key的缩写,即每篇post所对应的索引值,我们无需显式定义,django会自动为我们添加。
对于标题,我们这样修改:
{{ post.title }}
对于其他span标签,即分类、发布时间、作者、评论和阅读量5个,做如下修改,评论和阅读不动,暂时无法更换:
摘要做如下修改:
{{ post.excerpt }}
"read-more cl-effect-14">
"#" class="more-link">继续阅读 "meta-nav">→
我们可以在django自带的Admin后台来发布我们自己的博客文章,只需要做一些设置即可。
首先在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/,就进入到后台页面,可以增加分类、标签和文章,自行尝试一下就好。
当然,我们可以对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)
这样可以看到它能够显示文章的详细信息。
做完首页后,我们可以继续制作详情页了,开发流程是一样的。
对于详情页来说,每篇文章都对应了一个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[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。
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,便于静态文件的编写。
把模板中的single.html改为detail.html,与index页面一样,需要进行一些改写。可以发现,如果我们有很多这样的页面,那么每次都修改一遍,会非常麻烦,但是我们可以采用模板继承的方法来消除重复操作。
我们发现,index.html和detail.html文件除了main标签包裹的部分不同外,其他地方都是相同的,那么我们就可以把相同部分都放在base.html中,这样就可以省略很多重复操作。我们在同级目录下新建base.html文件,并将index.html的内容都拷进来,然后删除main标签内的内容,替换为:
...
{% block main %}
{% endblock main %}
{% block toc %}
{% endblock toc %}
...
...
block的作用就是占位,想不明白的话把它想象成类的继承与重写就可以。这样,当子类(之后说到的index.html和detail.html)块内写了东西,就会按子类来,如果没有东西,就不会显示这两行。
接下来,在index.html顶部写上{% extends 'base.html' %}
,这个意思就是继承了base,我们就只需要填block内部的东西就可以了:
{% extends 'blog/base.html' %}
{% block main %}
{% for post in post_list %}
...
{% empty %}
暂时没有发布文章!
{% endfor %}
...
{% endblock main %}
detail.html同理,我们需要填写main部分和目录toc部分,不过目前目录只是占位,之后再实现从文章中自动摘取目录:
{% extends 'blog/base.html' %}
{% block main %}
...
...
{% endblock main %}
{% block toc %}
{% endblock toc %}
同时,修改article标签下的内容,让其显示文章的实际数据:
{ { post.pk }}">
{{ post.title }}
{{ post.body }}
这样,我们就完成了一个博客的基本创建。
到目前为止,我们已经实现了博客的基本搭建,但是还有很多功能没有完成,比如:1.阅读数和评论量;2.目录部分等。这些功能我以后会继续探索研究,最近太忙,也没有时间继续做下去了。
写完以后发现写一遍比做一遍要花更久的时间,不过在写的过程我又学习了一遍,感觉收获很大,也基本上理清楚那些教程所说的东西了。共勉!