Django基础(二)

今天是在models.py建立基础的数据表。由于我想直接上手MySQL,所以今天开始切换到MySQL数据库,其实除了配置,其他都是一样的,后面就不用sqlite3了。

Posted by K4ys0n on December 18, 2019

前言

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。

参考:

Django基础(6): 模型Models高级进阶必读。