parent
947c5476c4
commit
55e722c017
9 changed files with 314 additions and 102 deletions
@ -0,0 +1,40 @@ |
||||
# Generated by Django 4.1.1 on 2022-09-23 16:26 |
||||
|
||||
from django.db import migrations, models |
||||
import django.db.models.deletion |
||||
import django.utils.timezone |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('article', '0002_article_author'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.CreateModel( |
||||
name='Category', |
||||
fields=[ |
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||
('title', models.CharField(max_length=100)), |
||||
('created', models.DateTimeField(default=django.utils.timezone.now)), |
||||
], |
||||
options={ |
||||
'ordering': ['-created'], |
||||
}, |
||||
), |
||||
migrations.AlterModelOptions( |
||||
name='article', |
||||
options={'ordering': ['-created']}, |
||||
), |
||||
migrations.AlterField( |
||||
model_name='article', |
||||
name='id', |
||||
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), |
||||
), |
||||
migrations.AddField( |
||||
model_name='article', |
||||
name='category', |
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='articles', to='article.category'), |
||||
), |
||||
] |
@ -0,0 +1,28 @@ |
||||
# Generated by Django 4.1.1 on 2022-09-23 18:05 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('article', '0003_category_alter_article_options_alter_article_id_and_more'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.CreateModel( |
||||
name='Tag', |
||||
fields=[ |
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||
('text', models.CharField(max_length=30)), |
||||
], |
||||
options={ |
||||
'ordering': ['-id'], |
||||
}, |
||||
), |
||||
migrations.AddField( |
||||
model_name='article', |
||||
name='tag', |
||||
field=models.ManyToManyField(blank=True, related_name='articles', to='article.tag'), |
||||
), |
||||
] |
@ -0,0 +1,18 @@ |
||||
# Generated by Django 4.1.1 on 2022-09-23 18:22 |
||||
|
||||
from django.db import migrations |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('article', '0004_tag_article_tag'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.RenameField( |
||||
model_name='article', |
||||
old_name='tag', |
||||
new_name='tags', |
||||
), |
||||
] |
@ -1,29 +1,128 @@ |
||||
from rest_framework import serializers |
||||
from article.models import Article |
||||
from article.models import Article, Category, Tag |
||||
from user_info.serializers import UserDescSerializer |
||||
|
||||
|
||||
# 返回文章列表或创建一篇文章 |
||||
class ArticleListSerializer(serializers.ModelSerializer): |
||||
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 ArticleBaseSerializer(serializers.HyperlinkedModelSerializer): |
||||
"""将原来的ArticleSerializer抽象出一个父类""" |
||||
author = UserDescSerializer(read_only=True) |
||||
# 使用article的url.py中的view --> detail |
||||
url = serializers.HyperlinkedIdentityField(view_name="article:detail") |
||||
# 希望文章接口不仅仅只返回分类的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', |
||||
) |
||||
|
||||
# 验证category_id是否正确 |
||||
def validate_category_id(self, value): |
||||
if not Category.objects.filter(id=value).exists() and value is not None: |
||||
raise serializers.ValidationError("Category with id {} not exist.".format(value)) |
||||
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): |
||||
# 渲染后的正文 |
||||
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', |
||||
'body', |
||||
'author', |
||||
'articles', |
||||
] |
||||
# read_only_fields = ['author'] |
||||
|
||||
# 返回文章详情 |
||||
|
||||
class TagSerializer(serializers.ModelSerializer): |
||||
# 显示url |
||||
# url = serializers.HyperlinkedIdentityField(view_name='tag-detail') |
||||
|
||||
"""所有标签序列化器""" |
||||
|
||||
class ArticleDetailSerializer(serializers.ModelSerializer): |
||||
class Meta: |
||||
model = Article |
||||
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(TagSerializer, self).create(validated_data) |
||||
|
||||
def update(self, instance, validated_data): |
||||
self.check_tag_obj_exists(validated_data) |
||||
return super(TagSerializer, self).update(instance, validated_data) |
||||
|
||||
|
||||
|
Binary file not shown.
@ -1,8 +1,17 @@ |
||||
from django.contrib import admin |
||||
from django.urls import path, include |
||||
from rest_framework.routers import DefaultRouter |
||||
from article import views |
||||
|
||||
router = DefaultRouter() |
||||
router.register(r'article', views.ArticleViewSet) |
||||
router.register(r'category', views.CategoryViewSet) |
||||
router.register(r'tag', views.TagViewSet) |
||||
|
||||
|
||||
urlpatterns = [ |
||||
path('admin/', admin.site.urls), |
||||
path('api-auth/', include('rest_framework.urls')), |
||||
path('api/article/', include('article.urls', namespace='article')), |
||||
path('api/', include(router.urls)) |
||||
# path('api/article/', include('article.urls', namespace='article')), |
||||
] |
||||
|
Loading…
Reference in new issue