Vue+DRF搭建博客之后端篇(二)

文章目录

  • 一、任务
  • 二、设计博客文章的数据库模型(ER关系图)
  • 三、将数据库模型映射到Django模型类
    • 编写模型类的虚拟基类
    • 编写文章和标签模型
  • 四、使用DRF完成基本的增删改查
    • 1.接口定义
      • 标签接口
      • 文章接口
    • 2.编写序列化器
      • 文章序列化器
      • 标签序列化器
    • 3.编写视图
      • 文章视图
      • 标签视图
    • 4.配置路由
    • 5.测试

一、任务

  1. 设计博客文章的数据库模型(ER关系图)
  2. 将数据库模型映射到Django模型类
  3. 使用DRF完成基本的增删改查

二、设计博客文章的数据库模型(ER关系图)

这里编辑模型使用navicat自带的编辑工具:
在这里插入图片描述
这个工具也算是我尝试过用起来最为舒服的数据库模型设计工具了。

模型暂时设计成这样,后面再有其他的需求再添加字段:
Vue+DRF搭建博客之后端篇(二)_第1张图片
tb_article是文章表,其中的title,abstract和content分别对应标题、摘要和内容,tb_tag_of_article是文章的标签,一个文章可以有多个标签,一个标签也可以对应多篇文章,故它们之间是多对多的关系,所以需要一张中间表将它们联系起来(这只是在建模时候需要,将模型映射到Django ORM时可以直接使用多对多关系不用显示创建中间表)

三、将数据库模型映射到Django模型类

编写模型类的虚拟基类

因为基本所有的记录都需要createTime和updateTime这两个字段(如文章记录需要文章创建时间,更改时间等等),所以我们在虚拟基类BaseModel中写入这两个字段,其他的模型类继承BaseModel即可。

代码编写:
先创建一个模块(app),命名为blog,专门负责处理关于博客信息的请求和存储。

> manage.py startapp blog

添加到settings中

然后在项目配置文件夹中添加一个utils包,并新建models.py文件:
Vue+DRF搭建博客之后端篇(二)_第2张图片
写入:

from django.db import models


class BaseModel(models.Model):
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')

    class Meta:
        abstract = True #这句代码表示抽象继承

编写文章和标签模型

# /blog/models.py
class Article(BaseModel):
    article_title = models.CharField(max_length=255, verbose_name="文章标题")
    article_abstract = models.CharField(max_length=255, verbose_name="文章摘要")
    article_content = models.TextField(verbose_name="文章内容")
    article_tags = models.ManyToManyField('TagOfArticle', verbose_name='文章标签', related_name='tag_articles')

    def __str__(self):
        return self.article_title

    class Meta:
        db_table = 'tb_article'
        verbose_name = '文章'
        verbose_name_plural = verbose_name


class TagOfArticle(BaseModel):
    tag_name = models.CharField(max_length=255, verbose_name="文章标签")

    def __str__(self):
        return self.tag_name
	
	def related_article_num(self):
        return Article.objects.filter(article_tags=self).count()

    class Meta:
        db_table = 'tb_tag_of_article'
        verbose_name = '文章标签'
        verbose_name_plural = verbose_name

进行迁移

> manage.py makemigrations
> manage.py migrate

可以看到数据库中产生了我们需要的表:
Vue+DRF搭建博客之后端篇(二)_第3张图片

四、使用DRF完成基本的增删改查

1.接口定义

标签接口

方式 url 功能
get /blog/article_tags/ 提供所有标签记录
post /blog/article_tags/ 新增一个标签
get /blog/article_tags// 提供指定id的标签
put /blog/article_tags// 修改指定id的标签
delete /blog/article_tags// 删除指定id的标签

接受的json数据

{
    "tag_name": 标签名
}

文章接口

方式 url 功能
get /blog/articles/ 提供所有文章记录
post /blog/articles/ 新增一篇文章
get /blog/articles// 提供指定id的文章
put /blog/articles// 修改指定id的文章
delete /blog/articles// 删除指定id的文章
post /blog/articles//article_op_tag/ * 为指定文章添加列表中的标签
delete /blog/articles//article_op_tag/ * 为指定文章删除列表中的标签

接受的json数据
一般:

{
    "article_title": 文章标题,
    "article_abstract": 文章摘要,
    "article_content": 文章内容
},

为文章添加和修改标签:

{
    "tag_ids": [标签id, "标签名", ...]
}

2.编写序列化器

文章序列化器

通过TagSimpleSerializer来获取一篇文章对应的多个标签

# 为文章序列化器服务的标签序列化器
class TagSimpleSerializer(serializers.ModelSerializer):
    class Meta:
        model = TagOfArticle
        fields = ['id', 'tag_name']


# 文章序列化器
class ArticleSerializer(serializers.ModelSerializer):
    # 携带文章的标签
    article_tags = TagSimpleSerializer(many=True, read_only=True)

    class Meta:
        model = Article
        fields = '__all__'

标签序列化器

这里标签的序列化器我分成了两种

  • 一种是请求列表视图时使用的序列化器,里面包含一个新字段related_article_num,表示标着这个标签的文章数目
  • 另一种是详情序列化器,里面包含新字段tag_articles,即这个标签所对应的全部文章都使用ArticleSerializer进行序列化后返回文章的所有信息。
# 列表序列化器
class TagOfArticleListSerializer(serializers.ModelSerializer):
    # 获得当前标签的文章数目,只读字段
    related_article_num = serializers.CharField(read_only=True)

    class Meta:
        model = TagOfArticle
        fields = '__all__'


# 详情序列化器
class TagOfArticleDetailSerializer(serializers.ModelSerializer):
    tag_articles = ArticleSerializer(many=True, read_only=True)

    class Meta:
        model = TagOfArticle
        fields = '__all__'

即效果为

  • 列表视图:
[
    {
        "id": 1,
        "related_article_num": 1, <--除了获取标签的基本信息外,还可以获取该标签对应的文章数目
        "create_time": "2021-04-20T14:42:47.295950Z",
        "update_time": "2021-04-20T14:42:47.295950Z",
        "tag_name": "c++基础内容"
    },
    {
        "id": 2,
        "related_article_num": 1,
        ...
        "tag_name": "设计模式"
    }
]
  • 详情视图:
{
    "id": 1,
    "tag_articles": [ #在这里还返回了对应文章信息的列表
        {
            "id": 1,
            "article_tags": [
                "c++基础内容",
                "设计模式"
            ],
            ...
            "article_title": "第一篇文章",
            "article_abstract": "第一篇文章",
            "article_content": "第一篇文章的内容"
        }
    ],
    ...
    "tag_name": "c++基础内容"
}

3.编写视图

文章视图

class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

    @action(methods=['POST', 'DELETE'], detail=True)
    def article_op_tag(self, request, pk):
        article = self.get_object()
        data = request.data
        """
        {tag_id: 2}
        """
        tag_ids = data['tag_ids']
        for tag_id in tag_ids:
	        if request.method == 'POST' and isinstance(tag_id, str):
                new_tag = TagOfArticle.objects.create(tag_name=tag_id)
                tag_id = new_tag.id
            tag = get_object_or_404(TagOfArticle, pk=tag_id)
            if request.method == 'POST':
                article.article_tags.add(tag)
            else:
                article.article_tags.remove(tag)
        serializer = ArticleSerializer(instance=article)
        return Response(serializer.data)

标签视图

这里对请求的视图做了区分,如果为请求列表视图,则使用ListSerializer,而请求详情视图时使用DetailSerializer

class TagOfArticleViewSet(ModelViewSet):
    def get_queryset(self):
        return TagOfArticle.objects.all()

    def get_serializer_class(self):
        if self.action == 'list':
            return TagOfArticleListSerializer
        else:
            return TagOfArticleDetailSerializer

4.配置路由

# /blog/urls.py
from . import views
from rest_framework.routers import SimpleRouter

urlpatterns = [
]

router = SimpleRouter()
router.register(r'article_tags', views.TagOfArticleViewSet, basename='article_tags')
router.register(r'articles', views.ArticleViewSet, basename='articles')

urlpatterns += router.urls

主路由:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls'))
]

5.测试

  1. 文章列表
    Vue+DRF搭建博客之后端篇(二)_第4张图片

  2. 文章详情
    Vue+DRF搭建博客之后端篇(二)_第5张图片

  3. 标签列表
    Vue+DRF搭建博客之后端篇(二)_第6张图片

  4. 标签详情
    Vue+DRF搭建博客之后端篇(二)_第7张图片
    上一步:Vue+DRF搭建博客之后端篇(一)
    下一步:Vue+DRF搭建博客之前端篇(四)

你可能感兴趣的:(博客后端搭建,手动搭建博客,django,python)