前言
Django基础我也是学了点基础,现在边学边做,所以无法一步到位实现完整的网站,一开始只是一点简陋界面实现一点简单的功能,越到后面越来越完善。
今天先切换MySQL数据库,后面就不打算继续用Sqlite3数据库了;然后是models.py建立基础数据表,数据表目前也不是最全的,后续慢慢跟进项目接着完善。
1. 配置MySQL数据库
1.1 安装MySQL数据库并启动服务
这里就不多赘述了,安装完成后注意启动MySQL服务。
然后命令行进入到MySQL,创建一个名为blog的数据库。
1.2 修改settings.py配置
注释掉原来的Sqlite3配置,添加新的配置如下:
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'test.db'),
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'blog',
'USER': 'root',
'PASSWORD': '123456789',
'HOST': 'localhost',
'PORT': '3306',
}
}
其中USER是MySQL账号名,NAME是数据库名,PASSWORD是MySQL密码,其他不用修改。
然后在/myblog/myblog/__init__.py文件中写入以下两行代码:
import pymysql
pymysql.install_as_MySQLdb()
1.3 创建数据表和超级用户
在命令行移动到项目路径下/myblog,输入以下命令创建数据表和超级用户:
python manage.py migrate
python manage.py createsuperuser
1.4 删除原来生成的db.sqlite3文件
由于Django默认连接Sqlite3,所以上次执行了migrate命令后会生成db.sqlite3,连接了MySQL之后这个就没用了,可以删除掉。
2. 创建博客项目的数据表
2.1 导入封装库
首先是导入相关库
from django.db import models
from django.utils import timezone
from django.template.defaultfilters import slugify
from django.urls import reverse
from unidecode import unidecode
from django.contrib.auth.models import User
models是模型标准库;
- timezone为时区库,用于获取时间,这个时间跟settings.py中设置的时区有关;
- reverse用于模型创建时跳转到对应的视图函数所用,在models.py中一般跟get_absolute_url()函数一起用, 比如在创建一个博客对象后,需要自动跳转到对应的博客页面中去,这时就可以使用reverse和get_absolute_url()了。
- slugify库用来生成slug,本项目根据标题字段生成的slug,放在url中可以使博客内容与url关联起来,比如标题为“如何学习”, 生成的slug就是“ru-he-xue-xi”。假设我们的url原来是/post/id/xx.md,用slug作为url的一部分可以是/post/id/ru-he-xue-xi.md, 是不是对比就出来了,后者可以很容易想起是哪篇博客;
- unidecode库用来将中文解码为unicode编码,因为slugify库不接受中文输入,所以需要用到这个库来转码。
- User类是Django自带的auth用户验证,有五个字段:username、password、email、first_name和last_name。
2.2 分类表类Category
class Category(models.Model): # 分类表类
name = models.CharField('分类名', max_length=80, unique=True) # 类别名
slug = models.SlugField('slug', max_length=60, blank=True) # 保存slug,用于url
parent_category = models.ForeignKey('self', verbose_name="父级分类", blank=True, null=True, on_delete=models.CASCADE)
def __str__(self):
return self.name
def get_absolute_url(self): # 创建该类时跳转到对应的详情页面中
return reverse('blog:category_detail', args=[self.slug])
def has_child(self): # 判断是否存在子分类
if self.category_set.all().count() > 0:
return True
return False
class Meta:
ordering = ['name'] # 根据创建category的名称排序
verbose_name = "分类"
verbose_name_plural = verbose_name
这个模型是博客分类,有三个字段:name、slug和parent_category。其中parent_category是指父级分类,比如我有一篇博客, 它是属于技术分类下的后端分类的内容,那么这篇博客的分类就是后端分类,同时后端分类的父级分类是技术分类。这样一来我们 有必要创建一个函数来判断当前分类是否有子分类,方法就是查找父级分类的所有子分类,看存在与否,这就是has_child()的作用。 get_absolute_url()函数是在创建该类时,比如我们创建了一个分类,我们希望在新建分类的时候跳转到详情页面进行填写。
注:get_absolute_url()函数中的reverse函数第一个参数’blog:category_detail’,可以暂时不管,会在后面讲urls.py时说到, 其实reverse会将这个参数转化成app名称为blog、路由名称为category_detail的路径,然后通过路由分配到相应的视图函数中去。
2.3 标签表类Tag
class Tag(models.Model): # 标签表类
name = models.CharField('标签名', max_length=80, unique=True) # 标签名
slug = models.SlugField('slug', max_length=60, blank=True) # 保存slug,用于url
def __str__(self):
return self.name
def get_absolute_url(self): # 创建该类时跳转到对应的详情页面中
return reverse('blog:tag_detail', args=[self.slug])
def get_post_count(self): # 查询包含该标签的博客(已发表)
return Post.published.filter(tags__slug=self.slug).count()
class Meta:
ordering = ['name'] # 根据创建tag的名称排序
verbose_name = "标签"
verbose_name_plural = verbose_name
Tag表包含两个字段:name和slug,分别存储标签名和slug,同时限定了tag类的所有对象根据创建时间排序。 这里还写了一个get_post_count()用来查询包含这个标签的博客数。
2.4 自定义博客表类的筛选器PublishedManager
class PublishedManager(models.Manager):
def get_queryset(self):
return super(PublishedManager, self).get_queryset().filter(status='published')
这里定义了一个筛选器,可以筛选出所有字段status值为published的对象,这里是针对下面将要说的Post博客表类。
2.5 博客表类Post
class Post(models.Model): # 博客表类
choices = (
('d', '草稿'),
('p', '发表'),
)
title = models.CharField('标题', max_length=200, unique=True) # unique表示标题唯一
slug = models.SlugField('slug', max_length=60, blank=True) # 简短的标签,用于搜索引擎搜索时识别,主要用于url的设计中
author = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE,
related_name="author_posts") # 外键关联User类,别名为"作者",删除该对象时也删除与User的关联,related_name是支持反向搜索,允许通过作者搜索其所有文章
body = models.TextField('正文')
published_time = models.DateTimeField('发表时间', default=timezone.now, null=True) # 发布时间可以为空,默认为当前时间
created_time = models.DateTimeField('创建时间', auto_now_add=True) # 创建时间自动生成,并且仅生成一次
updated_time = models.DateTimeField('修改时间', auto_now=True) # 更新时间自动追加
status = models.CharField('文章状态', max_length=1, choices=choices,
default='p') # 表示文章发表或者是草稿状态,默认发表,choices是替换了显示该字段时的字符
category = models.ForeignKey('Category', verbose_name='分类', on_delete=models.CASCADE, blank=False, null=False) # 每篇博客只属于一类
tags = models.ManyToManyField('Tag', verbose_name='标签', blank=True) # 标签是多对多
views = models.PositiveIntegerField('浏览量', default=0) # 浏览量为正整数,从0开始
likenum = models.PositiveIntegerField('点赞', default=0) # 点赞数
objects = models.Manager() # 默认的筛选器
published = PublishedManager() # 自定义筛选器,调用这个对象时会得到已经发表了的博客
def __str__(self):
return self.title
def viewed(self): # 浏览量自加1的函数
self.views += 1
self.save(update_fields=['views'])
def like(self): # 点赞数自加1的函数
self.likenum += 1
self.save(update_fields=['likenum'])
def published(self): # 博客发表函数
self.status = 'p'
self.published_time = timezone.now()
self.save(update_fields=['status', 'published_time'])
def save(self, *args, **kwargs): # 保存时根据标题生成slug,以便放到url中,这样可以很容易从url看出是哪篇博客
if not self.id or not self.slug:
self.slug = slugify(unidecode(self.title)) # 根据标题生成slug
super().save(*args, **kwargs) # 调用父类save函数
def clean(self): # 草稿状态的博客没有发表时间,而发布状态的博客,发布日期为当前时间
if self.status == 'd' and self.published_time is not None:
self.published_time = None
if self.status == 'p' and self.published_time is None:
self.published_time = timezone.now()
def get_absolute_url(self):
return reverse('blog:post_detail', args=[str(self.id), self.slug]) # 创建对象时自动跳转到详细编辑的页面
class Meta:
ordering = ['-published_time'] # 按照发布时间从大到小排序
verbose_name = "博客" # 设置后台管理显示的字符
verbose_name_plural = verbose_name # 复数和单数显示的字符一致
这个类稍微复杂一点,choices是用于在显示status字段时的字符,做一个转换。基本上囊括了整个博客的内容:标题、简介、作者、正文、 发表时间、创建时间、修改时间、文章状态(草稿/发表)、分类、标签、浏览量和点赞数。其实还有评论,但是评论可以通过下面的Comment类来反向搜索。
- objects和published两个筛选器,一个是默认的,一个是上面设置的状态筛选器,之后再views.py中调用的话可以帮我们过滤掉草稿,只留下发表了的博客。
- viewed()函数是用于浏览量自加1,可以在views.py中调用。like()函数同理。
- published()函数用于发表博客,将draft草稿状态的博客发表出去。其实就是修改了status字段并添加published_time发表时间,更新到数据库。
- save()函数是Models类自带的,这里进行了重构,添加了保存前生成slug的功能。
- clean()函数用于:当博客处于草稿时,是没有发表时间的,clean()函数用于清除发表时间;当草稿发表时,设置发表时间。
- get_absolute_url()函数,我们希望在创建博客的时候自动跳到编辑博客的页面,这个函数就起到作用了。
- Meta参数中ordering设置博客默认按照发布时间从大到小排序。verbose_name是显示表类名为“博客”,verbose_name_plural表示该表类的对象超过1个时,也就是复数,显示表类名也为“博客”。
2.6 评论表类
# class Comment(models.Model): # 评论表类
# post = models.ForeignKey(Post, related_name='post_comments') # 支持反向搜索,通过博客内容搜索该博客下的所有评论
# name = models.CharField(max_length=80) # 评论用户的名称
# body = models.TextField() # 评论内容
# created_time = models.DateTimeField(auto_now_add=True) # 评论时间
# updated_time = models.DateTimeField(auto_now=True) # 评论更新时间
# active = models.BooleanField(default=True) # 评论激活状态,默认激活
#
# def __str__(self): # 显示由谁对什么博客的评论
# return 'Comment by {} on {}'.format(self.name, self.post)
#
# class Meta:
# ordering = ['-created_time']
这里设计的评论只能关联博客,也就是说只能发评论…没办法回复评论(技术有限,逻辑没有构思好,暂时注释,后续修改)。
后记
接下来先补充一下models模型的一些相关知识点稍微巩固一下吧哈哈哈~快忘完了。然后准备写一点views.py,构建基本的路由urls.py,同时创建templates文件夹,导入bootstrap和jQuery。
参考: