天天看點

Django REST framework開發-3

評論接口

添加評論

  • 建立應用,在項目目錄下
python manage.py startapp comments
           
  • 注冊應用 blogproj/setttings.py
INSTALLED_APPS = [
...
    'comments.apps.CommentsConfig',
]
           
  • 建立模型comments/models.py
from django.db import models
from django.utils import timezone

class Comment(models.Model):
    name = models.CharField(verbose_name='名字', max_length=50)
    email = models.EmailField(verbose_name='郵箱')
    url = models.URLField(verbose_name='網址', blank=True)
    text = models.TextField(verbose_name='内容')
    create_time = models.DateTimeField(verbose_name='建立時間', default=timezone.now)
    post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, verbose_name='文章')
    class Meta:
        verbose_name='評論'
        verbose_name_plural = verbose_name
        ordering = ['-create_time'] #降序

           
  • 資料庫遷移
python manage.py 
python manage.py migrate
           
  • 建立comments序列化器, comments/serializers.py
from rest_framework import serializers
from .models import Comment

class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = [
            'name'
            ,'email'
            ,'url'
            ,'text'
            ,'create_time'
            ,'post'
        ]
        read_only_fields = ['create_time',]
        extra_kwargs = {'post':{'write_only':True}}
           
  • 評論視圖集 comments/views.py
from rest_framework import viewsets,mixins
from .models import Comment
from .serializers import CommentSerializer

class CommentViewSet(viewsets.GenericViewSet,mixins.CreateModelMixin):
    #序列化器
    serializer_class = CommentSerializer
    queryset = Comment.objects.all()
           
  • 注冊Comment視圖集,blogproj/urls.py
from comments.views import CommentViewSet
...
router.register(r'comments',CommentViewSet,basename='comment')
           

通路:http://127.0.0.1:8000/api/comments/

評論清單

  • 修改blog/views.py
class PostViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
                  mixins.RetrieveModelMixin):
    ...
    @action(methods=["GET"], detail=True, url_path="comments", url_name="comment",
            pagination_class = LimitOffsetPagination,serializer_class=CommentSerializer)
    def list_comments(self,request,*args, **kwargs):
        post = self.get_object() #文章對象
        queryset = post.comment_set.all().order_by("-create_time") #文章的所有的評論
        page = self.paginate_queryset(queryset) #對評論清單進行分頁
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)
           
  • 通路:http://127.0.0.1:8000/api/posts/2/comments/

接口緩存

  • 緩存機制

    blogproj/settings.py

    • 本地
      CACHES = {
          'default':{
              'BACKEND':'django.core.cache.backends.locmem.LocMemCache',
          }
      }
                 
      redis
      • 安裝:pip install django-redis-cache
      • 配置
      CACHES = {
          "default": {
                  "BACKEND": "redis_cache.RedisCache",
                  "LOCATION": "redis://:[email protected]:6379/0",
                  "OPTIONS": {
                      "CONNECTION_POOL_CLASS": "redis.BlockingConnectionPool",
                      "CONNECTION_POOL_CLASS_KWARGS": {"max_connections": 50, "timeout": 20},
                      "MAX_CONNECTIONS": 1000,
                      "PICKLE_VERSION": -1,
                  },
              },
      }
                 
  • 緩存架構

    安裝:pip install drf-extensions

  • 文章清單緩存

    blog/views.py

    • 導入keyBit
      from rest_framework_extensions.key_constructor.constructors import DefaultKeyConstructor
      from rest_framework_extensions.key_constructor.bits import (
      ListSqlQueryKeyBit,PaginationKeyBit,RetrieveSqlQueryKeyBit
      )
      from rest_framework_extensions.cache.decorators import cache_response
                 
    • 實作KeyConstructor
    #文章清單緩存key生成器
    class PostListKeyConstructor(DefaultKeyConstructor):
        list_sql = ListSqlQueryKeyBit
        pagination = PaginationKeyBit
        updated_at = RetrieveSqlQueryKeyBit
               
    • 緩存list
    class PostViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
                      mixins.RetrieveModelMixin):
        ...
        @cache_response(timeout=5*60, key_func=PostListKeyConstructor())
        def list(self, request, *args, **kwargs):
            return super().list(self, request, *args, **kwargs)
               
  • 自動維護時間更新

blog/models.py

from django.db.models.signals import post_delete,post_save
from django.core.cache import cache
from datetime import datetime
...
def change_post_updated_at(sender=None, instance=None,*args,**kwargs):
    cache.set("post_updated_at", datetime.utcnow())

post_delete.connect(receiver=change_post_updated_at, sender=Post)
post_save.connect(receiver=change_post_updated_at, sender=Post)
           
  • 更新時間

blog/utils.py

from rest_framework_extensions.key_constructor.bits import KeyBitBase
from django.core.cache import cache
from datetime import datetime

class UpdateAtKeyBit(KeyBitBase):
    key = "updated_at"
    def get_data(self, **kwargs):
        value = cache.get(self.key, None)
        if not value:
            value = datetime.utcnow()
            cache.set(self.key, value=value)
        return str(value)

           
  • 更新keyConstructor

blog/views.py

from .utils import UpdateAtKeyBit
class PostUpdatedAtKeyBit(UpdateAtKeyBit):
    key = "post_updated_at"

#文章清單緩存key生成器
class PostListKeyConstructor(DefaultKeyConstructor):
    list_sql = ListSqlQueryKeyBit()
    pagination = PaginationKeyBit()
    updated_at = PostUpdatedAtKeyBit() #更新
           

API版本管理

  • 開啟版本管理

blogproj/settings.py

REST_FRAMEWORK = {
    ...
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.NamespaceVersioning',
    'DEFAULT_VERSION':'v1'
}
           
  • 修改blogproj/urls.py
urlpatterns = [
...
    #api根視圖
    path('api/v1/',include((router.urls,"api"), namespace='v1')),
    path('api/v2/',include((router.urls,"api"), namespace='v2')),
           

限流

  • 全局配置

blogproj/setttings.py

REST_FRAMEWORK = {
...
    'DEFAULT_THROTTLE_CLASSES':{
        'rest_framework.throttling.AnonRateThrottle'
    },
    'DEFAULT_THROTTLE_RATES':{
        'anon': '10/min',
    }
}
           
  • 局部配置
    • 限制評論通路次數

comments/views.py

from rest_framework.throttling import AnonRateThrottle
class CommentAnonRateThrottle(AnonRateThrottle):
    THROTTLE_RATES = {'anon': "5/min"}

class CommentViewSet(viewsets.GenericViewSet,mixins.CreateModelMixin):
...
    throttle_classes = [CommentAnonRateThrottle]