Django Rest Framework(二)

•基於Django

先建立一個django項目,在項目中建立一些表,用來測試rest framework的各類組件數據庫

models.pydjango

class UserInfo(models.Model):
    """用戶信息表"""
    user = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)


class UserToken(models.Model):
    """用戶token表"""
    user = models.OneToOneField(to="UserInfo", on_delete=models.CASCADE)
    token = models.CharField(max_length=64)


class Courses(models.Model):
    """
    課程表
    """
    name = models.CharField(verbose_name="課程名稱", max_length=32)
    course_img = models.CharField(verbose_name="課程圖片", max_length=64)
    level_choices = (
        (1, "初級"),
        (2, "中級"),
        (3, "高級"),
    )
    level = models.IntegerField(verbose_name="難度", choices=level_choices, default=1)

    def __str__(self):
        return self.name


class CourseDetail(models.Model):
     """課程詳細表"""
     course = models.OneToOneField(to="Courses", on_delete=models.CASCADE)
     slogan = models.CharField(verbose_name="口號", max_length=255)
     why = models.CharField(verbose_name="爲何要學", max_length=255)
     recommend_courses = models.ManyToManyField(verbose_name="推薦課程", to="Courses",
                                                related_name="rc")  # related_name設置反向查詢的字段,有多個關聯時指定某個字段進行反向查詢

    def __str__(self):
         return "課程詳細:" + self.course.title



class Chapter(models.Model):
    """
    課程章節表
    """
    num = models.IntegerField(verbose_name="章節")
    name = models.CharField(verbose_name="章節名稱", max_length=32)
    course = models.ForeignKey(verbose_name="所屬課程", to="Courses", related_name='coursechapters', on_delete=models.CASCADE)

    def __str__(self):
        return self.name

  

urls.pyapi

from django.contrib import admin
from django.urls import path, include, re_path

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^api/(?P<version>\w+)/', include("api.urls")),
]

api/urls.pyide

from django.urls import re_path
from api.views import courses, account, micro

urlpatterns = [
re_path(r'^courses/$', courses.CoursesView.as_view({"get": "list"})),
re_path(r'^courses/(?P<pk>\d+)/', courses.CoursesView.as_view({"get": "retrieve"})),

re_path(r'^micro/$', micro.MicroView.as_view({"get": "list"})),

re_path(r'^login/$', account.LoginView.as_view()),
]

 

•基本流程

請求到來以後,都要執行dispatch方法,dispatch方法根據請求方式不一樣觸發不一樣的方法,返回不一樣的內容

url.py函數

1 from django.conf.urls import url, include
2 from api.views import TestView
3  
4 urlpatterns = [
5     url(r'^test/', TestView.as_view()),
6 ]

views.pypost

 1 from rest_framework.views import APIView
 2 from rest_framework.response import Response
 3  
 4  
 5 class TestView(APIView):
 6     def dispatch(self, request, *args, **kwargs):
 7         """
 8         請求到來以後,都要執行dispatch方法,dispatch方法根據請求方式不一樣觸發 get/post/put等方法
 9          
10         注意:dispatch方法有好多好多的功能
11         """
12         return super().dispatch(request, *args, **kwargs)
13  
14     def get(self, request, *args, **kwargs):
15         return Response('GET請求,響應內容')
16  
17     def post(self, request, *args, **kwargs):
18         return Response('POST請求,響應內容')
19  
20     def put(self, request, *args, **kwargs):
21         return Response('PUT請求,響應內容')

•認證組件

給micro添加須要認證才能訪問的權限學習

micro.py測試

# _*_ coding=utf-8 _*_
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from api.auth.auth import MicroAuth


class MicroView(ViewSetMixin, APIView):
    # 給micro添加認證後才能訪問的組件
    authentication_classes = [MicroAuth]

    def list(self, request, *args, **kwargs):
        ret = {'code': 1000, 'data': '學習中心'}

        return Response(ret)

auth.pyurl

# _*_ coding=utf-8 _*_
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api.models import *


class MicroAuth(BaseAuthentication):
    """從token表讀取token進行認證"""
    def authenticate(self, request):
        token = request.query_params.get('token')
        obj = UserToken.objects.filter(token=token).first()
        if not obj:
            raise AuthenticationFailed({'code': 10001, 'error': '認證失敗'})
        return (obj.user.user, obj)

全局設置:spa

上述操做中均是對單獨視圖進行特殊配置,若是想要對全局進行配置,則須要再配置文件中寫入便可

•訪問頻率控制

給course添加頻率限制,同一個IP,60秒內訪問不超過3次

•使用自定義類進行訪問頻率控制,繼承BaseThrottle類

myThrottle.py
import time
from rest_framework.throttling import BaseThrottle


class MyThrottle(BaseThrottle):
    """IP訪問頻率組件
    限制60秒內訪問3次"""

    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        current_time = time.time()

        ip = request.META.get('REMOTE_ADDR')
        print(ip)

        if ip not in visit_code:
            # 若是是第一次訪問就把此ip的訪問時間存入visit_code中,返回True,不限制
            visit_code[ip] = [current_time, ]
            return True

        # 若是不是第一次訪問,就獲取其ip的訪問時間[time1,time2..]
        history = visit_code.get(ip)
        self.history = history
        # print(history,visit_code)

        while history and history[-1] < current_time - 60:
            # 判斷第一次訪問時間和當前時間是否超過60s,超過則刪除
            history.pop()

        if len(history) < 3:
            # history裏面的元素小於3個則把當前時間添加進去,方法True
            history.insert(0, current_time)
            return True
        # else:  # 能夠不寫
        #     return False

    def wait(self):
        """須要等待多少時間才能訪問"""
        current_time = time.time()
        return 60 - (current_time - self.history[-1])

在CoursesView類添加訪問頻率組件

class CoursesView(ViewSetMixin, APIView):
    # 頻率訪問組件
    throttle_classes = [MyThrottle,]

•使用rest framework內置頻率控制組件

myThrottle.py
from rest_framework.throttling import  SimpleRateThrottle


class VisitThrottle(SimpleRateThrottle):
    """內置ip頻率組件,須要在settings裏面設置參數"""
    scope = "visit_rate"

    def get_cache_key(self, request, view):
        return self.get_ident(request)

settings.py

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": ["api.myThrottle.VisitThrottle", ],
    "DEFAULT_THROTTLE_RATES": {
        "visit_rate": "5/m",
        # 這個參數就是頻率類中定義的那個參數scope, 其中第一個數字5表示5次,
        # 後面的m表示一分鐘,還有s,一秒, h, 一小時, d, 一天
    }
}

 

•權限控制

給TestView添加權限認證

views.py

# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import BasePermission
from rest_framework.request import Request


class TestPermission(BasePermission):
    message = "權限驗證失敗"

    def has_permission(self, request, view):
        """
        判斷是否有權限訪問當前請求
        Return `True` if permission is granted, `False` otherwise.
        :param request: 
        :param view: 
        :return: True有權限;False無權限
        """
        if request.user == "管理員":
            return True

    # GenericAPIView中get_object時調用
    def has_object_permission(self, request, view, obj):
        """
        繼承GenericAPIView,並在其中使用get_object時獲取對象時,觸發單獨對象權限驗證
        Return `True` if permission is granted, `False` otherwise.
        :param request: 
        :param view: 
        :param obj: 
        :return: True有權限;False無權限
        """
        if request.user == "管理員":
            return True


class TestView(APIView):
    # 認證的動做是由request.user觸發
    # 添加權限
    permission_classes = [TestPermission, ]

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET請求,響應內容')

全局設置

settings.py

REST_FRAMEWORK = {
        "DEFAULT_PERMISSION_CLASSES": [
        "api.views.TestPermission",
    ],
}

•序列化

對用戶請求的數據進行序列化

a.自動生成字段

# _*_ coding=utf-8 _*_
from django.urls import re_path
from api.views import courses


urlpatterns = [
    re_path(r'^courses/$', courses.CoursesView.as_view({"get": "list"})),
]
urls.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from rest_framework import serializers
from api.models import *


class CoursesSerializers(serializers.ModelSerializer):
    """課程序列化"""
    level = serializers.CharField(source='get_level_display')   # 獲取choices的中文

        class Meta:
        model = Course  # 指定表
        fields = "__all__"  # 使用自動生成的字段


class CoursesView(ViewSetMixin, APIView):

    def list(self, request, *args, **kwargs):
        ret = {"code": 1000, "data": None}
        try:
            course_list = Course.objects.all()
            course_data = CoursesSerializers(instance=course_list, many=True)
            ret["data"] = course_data.data
        except Exception as e:
            ret["code"] = 1001
            ret["error"] = "獲取失敗"

        return Response(ret)
views.py

b.使用自定義字段

# _*_ coding=utf-8 _*_
from django.urls import re_path
from api.views import courses

urlpatterns = [
    re_path(r'^courses/$', courses.CoursesView.as_view({"get": "list"})),
    re_path(r'^courses/(?P<pk>\d+)/', courses.CoursesView.as_view({"get": "retrieve"})),
]
urls.py
from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from api.serializers.courses_serializers import *
from api.models import *


class CourseDetailSerializers(serializers.ModelSerializer):
    """課程詳細序列化"""
    # OneToOne/fk/choice等字段查詢某一條記錄時適用: 自定義要序列化的字段,關聯的表:表名.字段,
    name = serializers.CharField(source='course.name')
    level = serializers.CharField(source='course.get_level_display')  # 獲取choice類型對應的中文

    # ManyToMany,fk查詢多條記錄適用:
    recommend_courses = serializers.SerializerMethodField()
    chapters = serializers.SerializerMethodField()

    class Meta:
        model = CourseDetail
        # 添加自定義字段
        fields = ['name', 'level',  'recommend_courses', 'chapters']

    def get_recommend_courses(self, obj):
        # 獲取全部的課程,obj指的是CourseDetail,recommends字段數據爲此函數的返回值
        course_list = obj.recommend_course.all()
        return [{"id": i.id, "title": i.name} for i in course_list]

    def get_chapters(self, obj):
        # 反向查詢時若是定義了related_name,要使用related_name的值進行查詢
        chapters = obj.course.coursechapters.all()
        return [{"id": i.id, "name": i.name} for i in chapters]


class CoursesView(ViewSetMixin, APIView):

    def list(self, request, *args, **kwargs):
        ret = {"code": 1000, "data": None}
        try:
            course_list = Course.objects.all()
            course_data = CoursesSerializers(instance=course_list, many=True)
            ret["data"] = course_data.data
        except Exception as e:
            ret["code"] = 1001
            ret["error"] = "獲取失敗"

        return Response(ret)

    def retrieve(self, request, *args, **kwargs):
        ret = {"code": 1000, "data": None}
        try:
            # 課程id
            pk = kwargs.get("pk")
            # 課程詳細對象
            obj = CourseDetail.objects.filter(course_id=pk).first()
            course_detail = CourseDetailSerializers(instance=obj, many=False)
            ret["data"] = course_detail.data
        except Exception as e:
            ret["code"] = 1001
            ret["error"] = "獲取失敗"
        return Response(ret)
views.py

 

•分頁器

設置頁碼進行分頁

# _*_ coding=utf-8 _*_
from django.urls import re_path
from api.views import courses

urlpatterns = [
    re_path(r'^courses/$', courses.CoursesView.as_view({"get": "list"})),
    re_path(r'^courses/(?P<pk>\d+)/', courses.CoursesView.as_view({"get": "retrieve"})),
]
urls.py
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from api import models

from rest_framework.pagination import PageNumberPagination


class MyPagination(PageNumberPagination):
    # 默認每頁顯示的數據條數
    page_size = 1
    # 獲取URL參數中設置的每頁顯示數據條數
    page_size_query_param = 'page_size'

    # 獲取URL參數中傳入的頁碼key
    page_query_param = 'page'

    # 最大支持的每頁顯示的數據條數
    max_page_size = 1


class CourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Course
        fields = "__all__"


class CoursesViewSet(APIView):
    def list(self, request, *args, **kwargs):
        course_list = models.Course.objects.all().order_by('-id')

        # 實例化分頁對象,獲取數據庫中的分頁數據
        paginator = MyPagination()
        page_course_list = paginator.paginate_queryset(course_list, self.request, view=self)

        # 序列化對象
        serializer = CourseSerializer(page_user_list, many=True)

        # 生成分頁和數據
        response = paginator.get_paginated_response(serializer.data)
        return response
views.py

使用自動生成url

urls.py

from rest_framework import routers
from api.views import *

router = routers.DefaultRouter()
router.register('courses', CoursesViewSet)

便捷使用分頁器

views.py

from rest_framework import viewsets
from rest_framework import serializers
from api.models import *


class CoursesSerializers(serializers.ModelSerializer):
    """課程序列化"""
    level = serializers.CharField(source='get_level_display')   
        class Meta:
        model = Courses  
        fields = "__all__"  


class CoursesViewSet(viewsets.ModelViewSet):
     # 指定已經寫好的分頁器
    pagination_class = MyPagination
   
    queryset = Courses.objects.all().order_by('id')  # 使用分頁器要排序
    serializer_class = CoursesSerializersModel
相關文章
相關標籤/搜索