搭建个人博客网站
前期主要涉及框架设计,并不会涉及代码开发。因为搭建博客网站,跟完成其他项目一样,需要设计数据库的结构和模块(类)的作用、数量等,所以前期会先完成上述设计。
模块
一个博客网站,应该涉及一下模块:首页、文章部分信息(标题、摘要——自撰或自动截取前100字、作者、发布时间、标签等)、导航(上方)、分页(或瀑布式呈现内容)、下方有友情链接、右侧有标签。在一篇文章里面,除了正常显示内容外,还有一些常规功能,如点赞、收藏、分享到微信、分享到微博等和评论。同时,也需要有一些防范机制,比如防止有人刷评论或者无意义评论,此时可以增加检验机制,包括图片检验、验证码之类,不能太难(减退粉丝),也不能太容易(爬虫容易绕过)。在文章管理处,这要支持增删改查,排序(按时间)和置顶功能。
所以具体有以下模块的划分:文章、分类、标签、友情链接(增加被搜索到的机会和增加流量)、评论(对文章本身的评论,和对评论的评论)、侧边栏、用户管理。
以下是以infoq.cn的首页为例,
文章(Article)
- ID
- 标题
- 作者:如果非多用户的网站,那么作者就是自己本身了;
- 分类(一对一,一篇文章有一个分类,但是最一个分类下有多篇文章)
- 标签(多对多)
- 摘要
- 正文
- 状态(草稿、发布、删除):以flag形式标明状态
- 发布时间/更新时间
分类(Category)
- ID
- 名称:name
- 状态:status (如果需要隐藏多篇文章,只需要隐藏分类即可)
- 作者:owner 多用户网站,就会有一个多用户(作者)多分类的情况
- 创建时间: create_time
- 是否置顶: is_nav
标签(Tag)
- ID
- 名称
- 状态
- 作者
- 创建时间
友情链接(Link)
- ID
- 网站名称
- 链接
- 作者(可选)
- 状态:是否显示
- 创建时间
- 权重:链接显示的前后
评论(Comment)
- ID
- 文章(一对多:一篇文章多个评论)
- 用户:可自填
- 邮箱(可选)
- 网站地址(可选)
- 内容
- 创建时间
- 作者
侧边栏(Sidebar)
- ID
- 标题
- 类型
- 最新文章
- 最热文章
- 最近评论
- 内容
- 创建时间
用户(User)
- ID
- 成就
- 头像
- 邮箱
通过UML,描述模块之间的表结构关系(属性及方法)
通过思维导图描述各功能之间的关系
模块划分与代码编写逻辑
将有关联的模块都放到同一个APP中,所以
- 要把文章、标签和分类放在一个APP里;
- 虽然评论是跟随文章的,但是也有可能跟插件(网上有插件,可以完成盖楼的评论)有关,所以要从文章单独剥离出来,以便扩展;
- 友情链接和侧边栏属于相对独立的板块,可以分别单独的放到一个APP里;
所以,将会有以下APP
- blog APP: article
- blog_config APP: links and sidebar
- comments APP
通过以下代码,创建上述APP(由于blog APP已经有了,就不再创建),cd到project目录下(此前要先激活虚拟环境)
python3 manage.py startapp blog_config
python3 manage.py startapp comments
然后要将新建的APP添加到settings的INSTALLED_APPS中,
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
'blog_config',
'comment'
]
编写blog APP的内容
根据“blog里包含category、tags和articles”,编写blog中models的内容。
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Category(models.Model):
'''分类'''
status_items = (
(1, '正常'),
(0, '删除')
)
name = models.CharField(max_length=50, verbose_name='名称')
status = models.IntegerField(choices=status_items, default=1, verbose_name='状态')
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者')
# on_delete=models.CASCADE: 用户删除的时候,其相关信息也要删掉
is_nav = models.BooleanField(default=False, verbose_name='是否为导航')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
def __str__(self) -> str:
return self.name
# 以名称作为数据返回
class Meta:
verbose_name = verbose_name_plural = '分类'
class Tag(models.Model):
"标签"
status_items = (
(1, '正常'),
(0, '删除')
)
name = models.CharField(max_length=50, verbose_name='名称')
status = models.IntegerField(choices=status_items, default=1, verbose_name='状态')
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者')
# on_delete=models.CASCADE: 用户删除的时候,其相关信息也要删掉
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
def __str__(self) -> str:
return self.name
# 以名称作为数据返回
class Meta:
verbose_name = verbose_name_plural = '标签'
class Article(models.Model):
"文章"
status_items = (
(0, '删除'),
(1, '正常'),
(2, '草稿')
)
title = models.CharField(max_length=255, verbose_name='标题')
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者')
category = models.ForeignKey(Category, verbose_name="分类", on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, verbose_name='标签')
desc = models.CharField(max_length=1024,verbose_name='摘要')
content = models.TextField(verbose_name='正文')
status = models.IntegerField(choices=status_items, default=2, verbose_name='状态')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
def __str__(self) -> str:
return self.title
# 以标题作为数据返回
class Meta:
verbose_name = verbose_name_plural = '文章'
然后在blog APP中的admin.py,登记此处变更,且控制分类(Category)的呈现内容。
from django.contrib import admin
# Register your models here.
from .models import Category, Tag, Article
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'status', 'owner', 'is_nav', 'created_time')
# 控制已保存内容的展现内容和字段
fields = ('owner', 'name', 'status', 'is_nav')
# 控制可填写的内容和字段是否展现
# 此处必须显示owner,因为owner字段在定义的时候与外键的User连接,而User是django的内置字段(类),所以必须不为空,否则会影响id等内容的创建
def post_count(self, obj):
nums = obj.article_set.count()
# obj 相当于CategoryAdmin下的表的内容,会有所有字段信息
# article_set: 类_set
# print(nums)
return f'{nums}篇文章'
post_count.short_description = '文章数量'
def save_model(self, request, obj, form, change):
# 无需输入owner,返回作为当前登录的人
# obj: object, 表示这个数据的对象,即owner
# request:页面返回的请求,包含真实的登录用户
# 下方的super指代 admin.ModelAdmin
obj.owner = request.user
return super(CategoryAdmin, self).save_model(request, obj, form, change)
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
pass
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
# 只能填写已经创建好的字段
list_display = ('category', 'title', 'owner', 'status')
exclude = ('owner', ) # 不显示owner
actions_on_top = True
# 操作栏在上方
actions_on_bottom = True
# 编辑文章后,当编写的内容过多(列表过长),操作栏在下方也可以让操作更方便
save_on_top = True
# 编辑文章时,操作按钮在上方
search_fields = ('title', 'category__name')
# 外键的话:外键名__name
def save_model(self, request, obj, form, change):
obj.owner = request.user
return super(ArticleAdmin, self).save_model(request, obj, form, change)
编写blog_config APP的内容
在blog_config的models中填入以下内容,创建Link和Sidebar的内容
from django.contrib.admin.decorators import display
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Link(models.Model):
'''友情链接'''
status_items = (
(1, '正常'),
(0, '删除')
)
title = models.CharField(max_length=50, verbose_name='网站名称')
href = models.URLField(verbose_name='链接')
owner = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE)
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
status = models.IntegerField(choices=status_items, default=1, verbose_name='状态')
weight = models.IntegerField(choices=zip(range(1, 11), range(1, 11)), default=1, verbose_name='权重')
def __str__(self) -> str:
return self.title
# 以名称作为数据返回
class Meta:
verbose_name = verbose_name_plural = '友情链接'
class Sidebar(models.Model):
'''侧边栏'''
title = models.CharField(max_length=50, verbose_name='标题')
type_items = (
(1, 'HTML'),
(2, '最新文章'),
(3, '最热文章'),
(4, '最近评论')
)
display_type = models.IntegerField(choices=type_items, default=1, verbose_name='展示类型')
status_items = (
(1, '展示'),
(0, '隐藏')
)
status = models.IntegerField(choices=status_items, default=1, verbose_name='状态')
content = models.CharField(max_length=500, verbose_name='内容')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
owner = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE)
def __str__(self) -> str:
return self.title
# 以名称作为数据返回
class Meta:
verbose_name = verbose_name_plural = '侧边栏'
同时,在blog_config下的admin中确定加入以上的内容
from django.contrib import admin
# Register your models here.
from .models import Link, Sidebar
@admin.register(Link)
class LinkAdmin(admin.ModelAdmin):
# 只能填写已经创建好的字段
pass
@admin.register(Sidebar)
class SideBarAdmin(admin.ModelAdmin):
# 只能填写已经创建好的字段
pass
编写comment APP的内容
在comment的models.py中输入以下内容
from django.db import models
from blog.models import Article
# Create your models here.
class Comment(models.Model):
'''评论'''
target = models.ForeignKey(Article, verbose_name='评论目标', on_delete=models.CASCADE)
nickname = models.CharField(max_length=50, verbose_name='昵称')
email = models.EmailField(verbose_name='邮箱')
website = models.URLField(verbose_name='网站地址')
content = models.TextField(verbose_name='评论内容')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
def __str__(self) -> str:
return f"{self.target} By {self.nickname}"
class Meta:
verbose_name = verbose_name_plural = '评论'
然后在comment app下的admin.py中注册Comment
from .models import Comment
# Register your models here.
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
pass
同步数据库
之所以数据类型都用models.CharField之类的,是因为便于迁移数据库、修改字段等操作,也不用到数据库里创建表单、字段。所以要到该项目下,运行
python3 manage.py makemigrations
当看到如下情况的时候,
然后就可以输入,
python3 manage.py migrate
然后,上述的blog、blog_config、comment就可以在后台显示了。