|
|
|
from rest_framework import serializers
|
|
|
|
from article.models import Article, Category, Tag, Avatar
|
|
|
|
from user_info.serializers import UserDescSerializer
|
|
|
|
from comment.serializers import CommentSerializer
|
|
|
|
|
|
|
|
|
|
|
|
class CategorySerializer(serializers.ModelSerializer):
|
|
|
|
"""所有分类的序列化器"""
|
|
|
|
# 将路由间的表示转换为超链接
|
|
|
|
# category-detail是自动注册路由时, Router默认帮你设置的详情页面的名称
|
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='category-detail')
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = Category
|
|
|
|
fields = '__all__'
|
|
|
|
# 创建时间不能修改
|
|
|
|
read_only_fields = ['created']
|
|
|
|
|
|
|
|
|
|
|
|
class AvatarSerializer(serializers.ModelSerializer):
|
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='avatar-detail')
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = Avatar
|
|
|
|
fields = '__all__'
|
|
|
|
|
|
|
|
|
|
|
|
class ArticleBaseSerializer(serializers.HyperlinkedModelSerializer):
|
|
|
|
"""将原来的ArticleSerializer抽象出一个父类"""
|
|
|
|
# 添加文章id
|
|
|
|
id = serializers.IntegerField(read_only=True)
|
|
|
|
author = UserDescSerializer(read_only=True)
|
|
|
|
# 希望文章接口不仅仅只返回分类的id而已,所以需要显式指定category,将其变成一个嵌套数据,
|
|
|
|
# 分类的嵌套序列化字段
|
|
|
|
category = CategorySerializer(read_only=True)
|
|
|
|
# 显示指定category的id字段,用于创建/更新category外键
|
|
|
|
category_id = serializers.IntegerField(write_only=True, allow_null=True, required=False)
|
|
|
|
# 新增tag字段, 直接显示Tag的text字段
|
|
|
|
tags = serializers.SlugRelatedField(
|
|
|
|
queryset=Tag.objects.all(),
|
|
|
|
many=True,
|
|
|
|
required=False,
|
|
|
|
slug_field='text',
|
|
|
|
)
|
|
|
|
|
|
|
|
# 图片字段
|
|
|
|
avatar = AvatarSerializer(read_only=True)
|
|
|
|
avatar_id = serializers.IntegerField(
|
|
|
|
write_only=True,
|
|
|
|
allow_null=True,
|
|
|
|
required=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
# 自定义错误信息
|
|
|
|
default_error_messages = {
|
|
|
|
'incorrect_avatar_id': 'Avatar with id {value} not exists.',
|
|
|
|
'incorrect_category_id': 'Category with id {value} not exists.',
|
|
|
|
'default': 'No more message here..'
|
|
|
|
}
|
|
|
|
|
|
|
|
def check_obj_exists_or_fail(self, model, value, message='default'):
|
|
|
|
if not self.default_error_messages.get(message, None):
|
|
|
|
message = 'default'
|
|
|
|
# 不为None但是不存在该id返回错误信息
|
|
|
|
if not model.objects.filter(id=value).exists() and value is not None:
|
|
|
|
# 若不存在则调用钩子方法fail()引发错误
|
|
|
|
self.fail(message, value=value)
|
|
|
|
|
|
|
|
# 验证图片id是否正确
|
|
|
|
def validate_avatar_id(self, value):
|
|
|
|
self.check_obj_exists_or_fail(
|
|
|
|
model=Avatar,
|
|
|
|
value=value,
|
|
|
|
message='incorrect_avatar_id'
|
|
|
|
)
|
|
|
|
return value
|
|
|
|
|
|
|
|
# 验证category_id是否正确
|
|
|
|
def validate_category_id(self, value):
|
|
|
|
self.check_obj_exists_or_fail(
|
|
|
|
model=Category,
|
|
|
|
value=value,
|
|
|
|
message='incorrect_category_id'
|
|
|
|
)
|
|
|
|
return value
|
|
|
|
|
|
|
|
def to_internal_value(self, data):
|
|
|
|
tags_data = data.get('tags')
|
|
|
|
if isinstance(tags_data, list):
|
|
|
|
for text in tags_data:
|
|
|
|
# 不存在该标签则创建它
|
|
|
|
if not Tag.objects.filter(text=text).exists():
|
|
|
|
Tag.objects.create(text=text)
|
|
|
|
return super().to_internal_value(data)
|
|
|
|
|
|
|
|
|
|
|
|
class ArticleSerializer(ArticleBaseSerializer):
|
|
|
|
"""文章序列化器"""
|
|
|
|
class Meta:
|
|
|
|
model = Article
|
|
|
|
fields = '__all__'
|
|
|
|
# body字段只可写不可见
|
|
|
|
extra_kwargs = {'body': {'write_only': True}}
|
|
|
|
|
|
|
|
|
|
|
|
class ArticleDetailSerializer(ArticleBaseSerializer):
|
|
|
|
id = serializers.IntegerField(read_only=True)
|
|
|
|
comments = CommentSerializer(many=True, read_only=True)
|
|
|
|
# 渲染后的正文
|
|
|
|
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__'
|
|
|
|
|
|
|
|
|
|
|
|
class ArticleCategoryDetailSerializer(serializers.ModelSerializer):
|
|
|
|
"""分类详情的嵌套序列化器"""
|
|
|
|
url = serializers.HyperlinkedIdentityField(view_name='article-detail')
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = Article
|
|
|
|
fields = [
|
|
|
|
'url',
|
|
|
|
'title',
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
class CategoryDetailSerializer(serializers.ModelSerializer):
|
|
|
|
"""具体的分类详情页不显示url"""
|
|
|
|
# 显示某个分类下的所有文章
|
|
|
|
articles = ArticleCategoryDetailSerializer(many=True, read_only=True)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = Category
|
|
|
|
fields = [
|
|
|
|
'id',
|
|
|
|
'title',
|
|
|
|
'created',
|
|
|
|
'articles',
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
class TagSerializer(serializers.ModelSerializer):
|
|
|
|
"""所有标签序列化器"""
|
|
|
|
# 显示url
|
|
|
|
# url = serializers.HyperlinkedIdentityField(view_name='tag-detail')
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = Tag
|
|
|
|
fields = '__all__'
|
|
|
|
|
|
|
|
# 创建或者更新前检查是否存在该tag
|
|
|
|
def check_tag_obj_exists(self, validated_data):
|
|
|
|
text = validated_data.get('text')
|
|
|
|
if Tag.objects.filter(text=text).exists():
|
|
|
|
raise serializers.ValidationError('Tag with text {} exists.'.format(text))
|
|
|
|
|
|
|
|
def create(self, validated_data):
|
|
|
|
self.check_tag_obj_exists(validated_data)
|
|
|
|
return super().create(validated_data)
|
|
|
|
|
|
|
|
def update(self, instance, validated_data):
|
|
|
|
self.check_tag_obj_exists(validated_data)
|
|
|
|
return super().update(instance, validated_data)
|
|
|
|
|
|
|
|
|
|
|
|
|