django-Vue搭建博客 :Markdown正文

教程来源 杜塞-django-vue系列
博客链接 传送门

博客文章通常是需要排版的,否则难以凸显标题、正文、注释等内容之间的区别。对于博客来说,主流的排版就是markdown语法了。

严格来说,Markdown 是一种排版标注规则。他将两个星号包裹的文字标注为重要文本(通常是粗体),比如原始文本中的**Money**,在Markdown语法中就被渲染为粗体,也就是Money,类似的还有斜体、代码块、表格、公式、标题等注释。

教程来源杜塞-django-vue系列

Markdown,可自行了解。

“渲染”Markdown也就是把原始的文本中的注释转化为前端中真正被用户看到的HTML排版文字。渲染过程可以在前端,也可以在后端吗,本文就学习使用后端渲染,以便理解DRF的相关知识。、

模型和视图

为了文章的Markdown正文渲染为html标签,首先给文章模型添加一个get_md()方法:

# article/models.py
# 博客文章
class Article(models.Model):
	def get_md(self):
        md = Markdown(
            extensions=[
                # 包含 缩写、表格等常用扩展
                'markdown.extensions.extra',
                # 语法高亮扩展
                'markdown.extensions.codehilite',
                # 目录扩展
                'markdown.extensions.toc',
            ]
        )
        md_body = md.convert(self.body)
        return md_body, md.toc

方法返回了两个元素,一个是已渲染完成的html正文和目录。

这些渲染后的数据,在文章详情页接口是需要提供的,但是在列表接口却没有必要,因此我们又要用到视图集根据请求方式动态获取序列化器的技术了:

# article/views.py
from .serializers import ArticleDetailSerializer
class ArticleViewSet(viewsets.ModelViewSet):
···
	def get_serializer_class(self):
        if self.action =='list':
            return ArticleSerializer
        else:
            return ArticleDetailSerializer

序列化器ArticleDetailSerializer我们还是放在下一步。

序列化器

因为文章列表接口文章详情接口只有一点点返回字段的区别,绝大多数功能还是一样的,所以,我们处于简洁代码考虑。我们选择继承。

# article/serializers.py
# 把原有的ArticleSerializer改为ArticleBaseSerialize移除class Meta:
class ArticleBaseSerializer(serializers.HyperlinkedModelSerializer):
    """文章序列化器"""

    author = ···
    tags =···
    category = ···
    category_id =···

    def vaildate_category_id(self, value):
		···
    def to_internal_value(self, data):
		···
# 新增ArticleDetailSerializer 继承ArticleBaseSerializer,添加class Meta:
class ArticleSerializer(ArticleBaseSerializer):
    class Meta:
        model = Article
        fields = '__all__'
        extra_kwargs = {'body':{'write_only':True}}

Django表单类似,你可以继承扩展和重写序列化器。就像上面代码一样,在父类上声明一组通用字段。然后在许多序列化器中使用他们。

但是内部类class Meta比较特殊,他不会隐式从父类继承,虽然有办法隐式继承,但不推荐,在子类中声明更为清晰。

另外,你如果觉得在列表接口连body字段也不需要的显示的话,你可以传入extra_kwargs使其变为尽可以写而不显示的字段。

把这些代码重构的准备做好了。那就可以开始新的ArticleDetailSerializer了:

# article/serializer.py

# 继承ArticleBaseSerializer
class ArticleDetailSerializer(ArticleBaseSerializer):
    # 渲染后的正文
    body_html = serializers.SerializerMethodField()
    # 渲染后的目录
    toc_html = serializers.SerializerMethodField()

    def get_body_html(self, obj):
        return obj.get_md()[0]

    def get_toc_html(self, obj):
        return obj.get_md()[1]

    class Meta:
        model = Article
        fields = "__all__"

body_htmltoc_html这两个渲染后的字段是经过加工处理后的数据,不存在与原始数据中。为了将这个类只读的附加字段添加进接口里,我们就用到了SerializerMethodField字段。比如说上面代码中的get_body字段,让就会自动去调用get_body_html()方法,并将其返回的结果作为序列化的数据。方法中的obj参数就是序列化化获取的mldel实例,叶菊是文章对象。

这样就完成了,接下来就是测试。

测试

>http http://127.0.0.1:8000/api/article/
HTTP/1.1 200 OK
···
{
    "count": 9,
    "next": "http://127.0.0.1:8000/api/article/?page=2",
    "previous": null,
    "results": [
        {
            "author": {
                "date_joined": "2021-06-13T14:58:00",
                "id": 3,
                "last_login": null,
                "username": "xianwei"
            },
            "category": null,
            "created": "2021-06-17T20:52:34.325383",
            "tags": [],
            "title": "category_11",
            "updated": "2021-06-17T20:54:35.888087",
            "url": "http://127.0.0.1:8000/api/article/16/"
        },
        {
            "author": {
                "date_joined": "2021-06-13T14:58:00",
                "id": 3,
                "last_login": null,
                "username": "xianwei"
            },
            "category": null,
            "created": "2021-06-17T20:51:53.437601",
            "tags": [
                "python",
                "java"
            ],
            "title": "category_1",
            "updated": "2021-06-18T17:16:01.303651",
            "url": "http://127.0.0.1:8000/api/article/15/"
        }
    ]
}

请求文章列表没有变化,这就是动态选取序列化器起到了作用。

我再请求文章详情:


>http http://127.0.0.1:8000/api/article/15/
HTTP/1.1 200 OK
···
{
    "author": {
        "date_joined": "2021-06-13T14:58:00",
        "id": 3,
        "last_login": null,
        "username": "xianwei"
    },
    "body": "aaa",
    "body_html": "<p>aaa</p>",
    "category": null,
    "created": "2021-06-17T20:51:53.437601",
    "tags": [
        "python",
        "java"
    ],
    "title": "category_1",
    "toc_html": "<div class=\"toc\">\n<ul></ul>\n</div>\n",
    "updated": "2021-06-18T17:16:01.303651",
    "url": "http://127.0.0.1:8000/api/article/15/"
}

我们可以发现,多了body_htmltoc_html两个字段。并且都是html标签,说明已经被渲染完成了,而且本身body字段依然存在,这就说明没有替换到原来额的内容,而是经过后端渲染了。

原有文章数据存在,就出现了一个问题,有时候你可能出于版权方面的考虑不愿意将原始的 Markdown 文章数据给任意用户,那么这里只要做一次鉴权,根据用户的权限选用不同的序列化器即可。(非管理员不返回原始文章数据)。

来自杜塞博主提醒:

记得原始文本应该用 Markdown 语法编写。成功的话 body_html 字段返回的是带有 html 标签的文本。
代码重构得太早可能会导致某些不必要的抽象,太晚又可能堆积太多”屎山“而无从下手。理想情况下的重构是随着项目的开发同时进行的,在合适的节点进行合适的抽象,看着代码逐渐规整,你也会相当有成就感。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值