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)