博客系统项目:编码篇-基础篇
1 背景
在上一篇中介绍了《博客系统项目:设计篇》从整体上介绍了博客系统的设计方法和思路,本篇围绕设计出具博客系统的具体编码,设计出具后紧接着就到编码阶段,编码阶段涉及到开发环境的选择、开发语言的选型、开发框架的选型、数据库选型、IDE的选型、以及软件架构模式选型。在本篇中会集中介绍,在系统中有一个最小系统法的原则,包括我们组装计算机和器械过程中都会用到这个原则,即用最少和必要的部件以最少的时间最快的速度组装出一台计算机,加电启动,确认启动正常输出硬件信息,既可以进行后面的完整安装。
2 环境
2.1 环境说明
项目 | 工具 |
---|---|
开发环境 | Ubuntu 18.04 TLS |
开发语言 | Python3.6.9 |
虚拟环境 | virtualenv15.1.0 |
开发框架 | Django3.2.9 |
数据库 | sqlite3 |
IDE集成开发环境 | vscode1.63.2 |
设计模式 | 模型-模板-视图 MTV |
2.2 模式说明
2.2.1 MTV模式
介绍一下设计模式,MTV模式是Django 的设计模式。其借鉴了MVC模式,将交互过程分为3个层次:
- Model:数据存储层,处理所有数据相关的业务,和数据库进行交互,并提供数据的增删改查;
- Template:模板层(也叫表现层)具体来处理页面的显示;
- View:业务逻辑层,处理具体的业务逻辑,它的作用是连通Model 层和
Template 。
-
用户通过浏览器对服务器发起 request 请求,服务器接收请求后,通过 View 的业务逻辑层进行分析,同时向 Model 层和Template 层发送指令;
-
Mole 层与数据库进行交互,将数据返回给 View 层;
-
Template层接收到指令后,调用相应的模板,并返回给 View 层;
-
层接收到模板与数据后,首先对模板进行渲染(即将相应的数据赋值给模板),然后组织成响应格式返回给浏览器,浏览器进行解析后并最终呈现给用户。
2.2.2 MVC
在开发领域,MVC模式是非常出门的一种设计模式,
MVC 是 Model-View-Controller 的缩写,其中每个单词都有其不同的含义:
- Modle 代表数据存储层,是对数据表的定义和数据的增删改查;
- View 代表视图层,是系统前端显示部分,它负责显示什么和如何进行显示;
- Controller 代表控制层,负责根据从 View 层输入的指令来检索 Model 层的数据,并在该层编写代码产生结果并输出。 PHP语言框架中 laravel 使用的是MVC模式
请求与响应过程描述如下:
- 用户通过浏览器向服务器发起 request 请求,Controller 层接受请求后,同时向 Model 层和 View 发送指令;
- Mole 层根据指令与数据库交互并选择相应业务数据,然后将数据发送给 Controller 层;
- View 层接收到Controller 的指令后,加载用户请求的页面,并将此页面发送给 Controller 层;
- Controller 层接收到 Model 层和 View 层的数据后,将它们组织成响应格式发送给浏览器,浏览器通过解析后把页面展示出来。
MVC 的 3 层之间紧密相连,但又相互独立,每一层的修改都不会影响其它层,每一层都提供了各自独立的接口供其它层调用,MVC 的设计模式降低了代码之间的耦合性(即关联性),增加了模块的可重用性,这就是 MVC 的设计模式。
Laravel框架采用了MVC模式,Django采用MTV模式。
3 前置工作
编码阶段包括虚拟环境的创建、框架的安装、项目创建、应用创建、模型编写、模板引用、视图编写、运行测试。
3.1 环境搭建
查看python版本
python3 --version
Python 3.6.9
安装虚拟环境
pip install virtualenv
pip install virtualenvwrapper
创建虚拟环境
cd /code/mycode/python/project
virtualenv venv
激活虚拟环境
source venv/bin/activate
退出虚拟环境
deactivate
安装框架
进入到虚拟环境中
pip install django==3.2.9
创建项目
django-admin startproject project
cd project
python manage.py runserver
浏览器中输入 http://127.0.0.1:8000/
退出运行模式Ctrl+c
创建应用
django-admin startapp blog
全局变量
使用Visual Studio Code工具打开project文件夹
4 编码实现
4.1 项目调整
4.1.1 修改语言时区
project/project/settings.py
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
调整为
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True
英文调整为中文,时区UTC调整为亚洲/上海。
4.1.2 添加应用路由
新增文件
project/blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name = 'index'),
]
4.1.3 添加项目路由
编辑文件
project/project/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
4.1.4 添加应用视图
编辑文件
project/blog/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello,You're at the blog index.")
运行查看
python manage.py runserver
浏览器中输入 http://127.0.0.1:8000/blog/
4.1.5 生成迁移文件
python manage.py migrate
4.1.6 项目中添加应用
编辑文件
project/project/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig',
]
4.2 模型
4.2.1 编辑项目模型
project/blog/models.py
from django.db import models
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=70)
created_date = models.DateTimeField('创建时间',auto_now_add=True)
updated_date = models.DateTimeField('修改时间',auto_now=True)
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=70)
created_date = models.DateTimeField('创建时间',auto_now_add=True)
updated_date = models.DateTimeField('修改时间',auto_now=True)
def __str__(self):
return self.name
class Article(models.Model):
STATUS_CHOICES = (
('d','Draft'),
('p','Published'),
)
title = models.CharField('文章标题',max_length=70)
description = models.CharField('摘要',max_length=255)
content = models.TextField('文章内容')
created_date = models.DateTimeField('创建时间',auto_now_add=True)
updated_date = models.DateTimeField('修改时间',auto_now=True)
author = models.CharField('文章作者',max_length=70)
views = models.PositiveIntegerField('浏览量',default=0)
likes = models.PositiveIntegerField('点赞数',default=0)
topped = models.BooleanField('置顶',default=False)
status = models.CharField('文章状态', max_length=1,choices=STATUS_CHOICES)
image = models.CharField('文章图片',max_length=255)
category = models.ForeignKey('Category',verbose_name='分类',null=True,on_delete=models.SET_NULL)
tag = models.ForeignKey('Tag',verbose_name='标签',null=True,on_delete=models.SET_NULL)
def __str__(self):
return self.title
class Meta:
ordering = ['-updated_date']
class Banner(models.Model):
text_title = models.CharField('图片标题',max_length=50,default='')
text_info = models.CharField('图片文本', max_length=255)
img = models.ImageField('轮播图', upload_to='banner/')
link_url = models.CharField('图片链接', max_length=255)
is_active = BooleanField('图片状态',default=False)
def __str__(self):
return self.text_title
class Meta:
verbose_name = '轮播图'
verbose_name_plural='轮播图'
4.2.2 生成模型文件
执行生成模型命令
(venv) j@T:~/code/python/project$ python manage.py makemigrations blog
Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Banner
- Create model Category
- Create model Tag
- Create model Article
查看模型文件
blog/migrations/0001_initial.py
执行数据迁移
(venv) j@TK:~/code/python/project$ python manage.py migrate blog
Operations to perform:
Apply all migrations: blog
Running migrations:
Applying blog.0001_initial... OK
查看生成的数据库表
(venv) j@T:~/code/python/project$ python manage.py sqlmigrate blog 0001
BEGIN;
--
-- Create model Banner
--
CREATE TABLE "blog_banner" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text_title" varchar(50) NOT NULL, "text_info" varchar(255) NOT NULL, "img" varchar(100) NOT NULL, "link_url" varchar(255) NOT NULL, "is_active" bool NOT NULL);
--
-- Create model Category
--
CREATE TABLE "blog_category" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(70) NOT NULL, "created_date" datetime NOT NULL, "updated_date" datetime NOT NULL);
--
-- Create model Tag
--
CREATE TABLE "blog_tag" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(70) NOT NULL, "created_date" datetime NOT NULL, "updated_date" datetime NOT NULL);
--
-- Create model Article
--
CREATE TABLE "blog_article" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(70) NOT NULL, "description" varchar(255) NOT NULL, "content" text NOT NULL, "created_date" datetime NOT NULL, "updated_date" datetime NOT NULL, "author" varchar(70) NOT NULL, "views" integer unsigned NOT NULL CHECK ("views" >= 0), "likes" integer unsigned NOT NULL CHECK ("likes" >= 0), "topped" bool NOT NULL, "status" varchar(1) NOT NULL, "img" varchar(100) NULL, "category_id" bigint NULL REFERENCES "blog_category" ("id") DEFERRABLE INITIALLY DEFERRED, "tag_id" bigint NULL REFERENCES "blog_tag" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "blog_article_category_id_7e38f15e" ON "blog_article" ("category_id");
CREATE INDEX "blog_article_tag_id_4c94be52" ON "blog_article" ("tag_id");
COMMIT;
(venv) j@T:~/code/python/project$
注册
from django.contrib import admin
from . models import Article, Tag, Banner, Category
# Register your models here.
class ArticleAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['title']}),
(None, {'fields': ['description']}),
(None, {'fields': ['content']}),
(None, {'fields': ['author']}),
(None, {'fields': ['topped']}),
(None, {'fields': ['img']}),
(None, {'fields': ['status']}),
]
admin.site.register(Article, ArticleAdmin)
4.3 视图
4.3.1 前端视图
project/blog/views.py
from blog.models import Article, Category, Tag
from django.views.generic import ListView
import markdown2
# Create your views here.
class IndexView(ListView):
template_name = "index.html"
context_object_name = "article_list"
def get_queryset(self):
article_list = Article.objects.filter(status='p')
for article in article_list:
article.content = markdown2.markdown(article.content,)
return article_list
def get_context_data(self,**kwargs):
kwargs['category_list'] = Category.objects.all().order_by('name')
return super(IndexView,self).get_context_data(**kwargs)
在views.py文章有一个index.html文件
在project/blog目录下创建templates(存放index.html/detail.html)文件夹和static(存储css和js)文件夹,在index.html和detail.html文件中引入css和js文件。
project/blog/templates/blog/index.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
</head>
<body>
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
</body>
</html>
编辑应用路由信息
/project/blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('',views.IndexView.as_view(), name='index'),
浏览器中输入http://127.0.0.1:8000/blog/
以上信息为静态模板信息,后面需要管理后台维护到数据库信息,然后页面读取
4.3.2 后台页面
文章维护页面
在维护页面中可以对文章的内容、分类、标签、轮播图进行新增、编辑的维护管理
project/blog/admin.py
from django.contrib import admin
from . models import Article, Tag, Banner, Category
# Register your models here.
class ArticleAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['title']}),
(None, {'fields': ['description']}),
(None, {'fields': ['content']}),
(None, {'fields': ['author']}),
(None, {'fields': ['img']}),
(None, {'fields': ['category','tag','status','topped']}),
]
admin.site.register(Article, ArticleAdmin)
class CategoryAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name']}),
]
admin.site.register(Category,CategoryAdmin)
class TagAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name']}),
]
admin.site.register(Tag,TagAdmin)
class BannerAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['text_title']}),
(None, {'fields': ['text_info']}),
(None, {'fields': ['img']}),
(None, {'fields': ['link_url']}),
(None, {'fields': ['is_active']}),
]
admin.site.register(Banner,BannerAdmin)
后台页面显示
执行
python manage.py runserver
浏览器中输入http://127.0.0.1:8000/admin/
输入创建的管理员账号
登录后首页
文章维护页面
分类页面
标签页面
轮播图页面
手动后台维护分类、标签和文章信息。
原计划把编码整个在一篇中介绍,写了一部分后篇幅原因,随一分为二,下一篇中着手介绍视图读取数据库信息。