From 55e722c01753920edb9515905aa91feae767e09b Mon Sep 17 00:00:00 2001 From: barney <15270405776@163.com> Date: Fri, 23 Sep 2022 20:53:28 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=87=E6=BB=A4=E6=96=87=E7=AB=A0,=E5=88=86?= =?UTF-8?q?=E7=B1=BB,=E6=A0=87=E7=AD=BE,markdown=E6=AD=A3=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ticle_options_alter_article_id_and_more.py | 40 ++++++ article/migrations/0004_tag_article_tag.py | 28 ++++ .../0005_rename_tag_article_tags.py | 18 +++ article/models.py | 62 +++++++++ article/serializers.py | 123 ++++++++++++++++-- article/views.py | 123 +++++------------- db.sqlite3 | Bin 139264 -> 176128 bytes drf_vue_blog/settings.py | 11 +- drf_vue_blog/urls.py | 11 +- 9 files changed, 314 insertions(+), 102 deletions(-) create mode 100644 article/migrations/0003_category_alter_article_options_alter_article_id_and_more.py create mode 100644 article/migrations/0004_tag_article_tag.py create mode 100644 article/migrations/0005_rename_tag_article_tags.py diff --git a/article/migrations/0003_category_alter_article_options_alter_article_id_and_more.py b/article/migrations/0003_category_alter_article_options_alter_article_id_and_more.py new file mode 100644 index 0000000..65e3e10 --- /dev/null +++ b/article/migrations/0003_category_alter_article_options_alter_article_id_and_more.py @@ -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'), + ), + ] diff --git a/article/migrations/0004_tag_article_tag.py b/article/migrations/0004_tag_article_tag.py new file mode 100644 index 0000000..9c29c53 --- /dev/null +++ b/article/migrations/0004_tag_article_tag.py @@ -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'), + ), + ] diff --git a/article/migrations/0005_rename_tag_article_tags.py b/article/migrations/0005_rename_tag_article_tags.py new file mode 100644 index 0000000..e7f8ea9 --- /dev/null +++ b/article/migrations/0005_rename_tag_article_tags.py @@ -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', + ), + ] diff --git a/article/models.py b/article/models.py index 2181105..7cc78d4 100644 --- a/article/models.py +++ b/article/models.py @@ -1,10 +1,39 @@ from django.db import models from django.utils import timezone from django.contrib.auth.models import User +from markdown import Markdown + + +class Category(models.Model): + """文章分类""" + # 分类名称 + title = models.CharField(max_length=100) + created = models.DateTimeField(default=timezone.now) + + class Meta: + ordering = ['-created'] + + def __str__(self): + return self.title + + +class Tag(models.Model): + """文章标签""" + text = models.CharField(max_length=30) + + class Meta: + ordering = ['-id'] + + def __str__(self): + return self.text # 博客文章 model class Article(models.Model): + class Meta: + # 按创建时间降序排列 + ordering = ['-created'] + # 作者 author = models.ForeignKey( User, @@ -12,6 +41,24 @@ class Article(models.Model): on_delete=models.CASCADE, related_name='articles' ) + + # 分类 + category = models.ForeignKey( + Category, + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name='articles' + ) + + # 标签 + # 一篇文章可以有多个tag,一个tag可以属于多篇文章 + tags = models.ManyToManyField( + Tag, + blank=True, + related_name='articles', + ) + # 标题 title = models.CharField(max_length=100) # 正文 @@ -23,3 +70,18 @@ class Article(models.Model): def __str__(self): return self.title + + # 新增方法,将body转换为带html标签的正文 + 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 + + + diff --git a/article/serializers.py b/article/serializers.py index 79831fb..26d33cb 100644 --- a/article/serializers.py +++ b/article/serializers.py @@ -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 - fields = '__all__' \ No newline at end of file + 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) + + diff --git a/article/views.py b/article/views.py index 4e16769..f8d9faf 100644 --- a/article/views.py +++ b/article/views.py @@ -1,101 +1,50 @@ -from article.models import Article -from article.serializers import ArticleListSerializer, ArticleDetailSerializer -from rest_framework.decorators import api_view -from rest_framework.response import Response -from rest_framework.views import APIView -from django.http import Http404 -from rest_framework import mixins -from rest_framework import generics -from rest_framework import status +from rest_framework import viewsets +from article.serializers import ArticleSerializer, CategorySerializer, CategoryDetailSerializer +from article.serializers import TagSerializer,ArticleDetailSerializer +from article.models import Article, Category, Tag from article.permissions import IsAdminUserOrReadOnly +from rest_framework.filters import SearchFilter -@api_view(['GET', 'POST']) -def article_list(request): - # 获取所有文章列表 - if request.method == 'GET': - articles = Article.objects.all() - serializer = ArticleListSerializer(articles, many=True) - return Response(serializer.data) - # 创建新的文章 - elif request.method == 'POST': - serializer = ArticleListSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) +class ArticleViewSet(viewsets.ModelViewSet): + """文章视图集""" + queryset = Article.objects.all() + serializer_class = ArticleSerializer + permission_classes = [IsAdminUserOrReadOnly] + filter_backends = [SearchFilter, ] + # http://127.0.0.1:8000/api/article/?author__username=admin&title=第一篇文章 + # filter_fields = ['title', 'author__username',] # 精确查询 -# 和article_list()功能相同 -class ArticleList(generics.ListCreateAPIView): - # 添加用户权限 - permission_classes = [IsAdminUserOrReadOnly] - queryset = Article.objects.all() - serializer_class = ArticleListSerializer + # 下面两种字段都可以使用模糊查询 + search_fields = ('title', 'author__username') def perform_create(self, serializer): - # 在序列化数据真正保存之前调用 + # 在创建文章前,提供了视图集无法自行推断的用户外键字段。 serializer.save(author=self.request.user) + def get_serializer_class(self): + if self.action == 'list': + return ArticleSerializer + else: + return ArticleDetailSerializer -# 第一种版本 -# class ArticleDetail(APIView): -# """文章详情视图""" -# def get_object(self, pk): -# """获取单个文章对象""" -# try: -# # pk代表主键 -# return Article.objects.get(pk=pk) -# except: -# raise Http404 -# -# def get(self, request, pk): -# article = self.get_object(pk) -# serializer = ArticleDetailSerializer(article) -# # 返回Json数据 -# return Response(serializer.data) -# -# def put(self, request, pk): -# article = self.get_object(pk) -# serializer = ArticleDetailSerializer(article, data=request.data) -# # 验证提交的数据是否合法 -# # 不合法就返回400 -# if serializer.is_valid(): -# # 序列化器将持有的数据反序列化后 -# # 保存到数据库 -# serializer.save() -# return Response(serializer.data) -# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# -# def delete(self, request, pk): -# article = self.get_object(pk=pk) -# article.delete() -# # 删除成功后返回204 -# return Response(status=status.HTTP_204_NO_CONTENT) +class CategoryViewSet(viewsets.ModelViewSet): + """分类视图集""" + queryset = Category.objects.all() + serializer_class = CategorySerializer + permission_classes = [IsAdminUserOrReadOnly] -# 第二种版本 + def get_serializer_class(self): + if self.action == 'list': + return CategorySerializer + else: + return CategoryDetailSerializer -# class ArticleDetail(mixins.RetrieveModelMixin, -# mixins.UpdateModelMixin, -# mixins.DestroyModelMixin, -# generics.GenericAPIView): -# """文章详情视图""" -# queryset = Article.objects.all() -# # 序列化类 -# serializer_class = ArticleDetailSerializer -# -# def get(self, request, *args, **kwargs): -# return self.retrieve(request, *args, **kwargs) -# -# def put(self, request, *args, **kwargs): -# return self.update(request, *args, **kwargs) -# -# def delete(self, request, *args, **kwargs): -# return self.destroy(request, *args, **kwargs) -# ArticleDetail类还可以简化为下面的代码 -class ArticleDetail(generics.RetrieveUpdateDestroyAPIView): - permission_classes = [IsAdminUserOrReadOnly] - queryset = Article.objects.all() - serializer_class = ArticleDetailSerializer +class TagViewSet(viewsets.ModelViewSet): + """标签视图集""" + queryset = Tag.objects.all() + serializer_class = TagSerializer + permission_classes = [IsAdminUserOrReadOnly] \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index cbb09378a00382cc7bf11068bc4c33d4204dc9c2..88deac710d2650e3a203c5031426730e7cccf782 100644 GIT binary patch delta 6047 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 delta 1776 zcmah}eM}Q)7{B+f^jfg@f)vXHr64*g7OvOts(}+)!bj-`NC7pq&{D4Si?+1DW)w7Y zYUaetyo|-@A4`^O#t)jUTl9}KeGL?nK;bMa9f1L#VwO1+ZBf!&SdxIp7(v9 z_j#V*-7oL+J20Dj;8W3wl7b!tK`Qun`LRxIQ5W?fHSUre-UZ-d_CN*~CF?FFCSifR z-PS4RJw-AJZ>72SSUg{mE|*`*P2XF)AxDbCRses5t?)8T!eej%eg&y?Ik^_rLJ#ti zDC|qQ-JV#2=@0srXDNm$fj8g@`y+0TH^hY9!H6GYJ+%s5sYHU6i##KVLAO5^8Hw_c z<>IC7NZc3WcZE+6qrO;(iN~3UEC*8q_UfuaBVL$~RfFfC#6)(rVFBbA)B-v$4VtPS=?^q zfBu9deRGcr&Eu<}!~y~tVuAO5PY|Q11es7EqC$y8B&OM)!ZvJtAj&yz0I5W+*QvDH z2`R{iuyk0~OR01uNmkl>5?!rv?cj(9E9GyP3{}y)zt*xw?~$Zcn6L7dNRQeeGQdlUZd5 zGTH`LTZCeo$5KXDQ^P>ZkkUqXxttc=%MHqSyV7C_M*T{^d5Jj{sU&s(FsIUJbZT@1Z3A4fA61#+wu?qTtp{B;NaZ&Gq2(OABg{sc8g9K-!K@0|EXRQL^)xLNJkEh;wv3IKi^uO8S~m z4Fq|ckk{GY@k8823*e@1UCm5bfddHmes!^%^S%IYvXAnPaUYq%lguwIU>C^TZU?mm zwZdtn04+uYZ)5Y=E~p_G3B774?=#_?a3)tKzAu^=zbA5`#rQS;A|r@pie4k+Q^suy zuABkIil@z+ZN_Z(xm7Fod3@-0Sy>r)Z6o_9F53hfoMyVyEbpXuw3+1!Ua&oJ0BtN5BM8=#5?c9^#V)` zu&hBbOf1fdGX^aPFT?x!sf1T89po%Gz~w?dPXHfeRqX*^@>lqPsOJ@73z4b63r+#m zvVy1#CdyzU)fAa6if2uh06}&k&Kgk`qcrb2htGnn2p-MLT>lwttlaRQ9+D8>A_Afc zAts0_cpmO1s`&6V1Xr~l{SZuYch{p;U0RiiBv%TUmNJpr8d67*dIJx2r@BZ@BXj4@ m-T(N=+=atGoH{)B!P$f_o>-ZsQU9wDx4R5|mD^N@R{aI9yb34) diff --git a/drf_vue_blog/settings.py b/drf_vue_blog/settings.py index 2cf0d36..54baff8 100644 --- a/drf_vue_blog/settings.py +++ b/drf_vue_blog/settings.py @@ -28,6 +28,7 @@ INSTALLED_APPS = [ 'rest_framework', 'article', 'user_info', + 'django_filters', ] MIDDLEWARE = [ @@ -114,5 +115,11 @@ STATIC_URL = '/static/' # 使用django_rest_framework中的分页功能 REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', - 'PAGE_SIZE': 3, # 每页3条记录 -} \ No newline at end of file + # 每页3条记录 + 'PAGE_SIZE': 3, + # 使用django-filter后端过滤引擎 + 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], +} + + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/drf_vue_blog/urls.py b/drf_vue_blog/urls.py index 0069fb0..973784d 100644 --- a/drf_vue_blog/urls.py +++ b/drf_vue_blog/urls.py @@ -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')), ]