1. 程式人生 > 實用技巧 >Django---drf, books系列表介面、分頁器、根據ip進行頻率限制

Django---drf, books系列表介面、分頁器、根據ip進行頻率限制

目錄

回顧

# 1 web開發模型,混合開發和前後端分離
# 2 web api:介面
# 3 postman的使用
# 4 restful規範:10條
# 5 djangorestframework,django的第三方外掛(app)
# 6 drf幾大元件
    請求(APIView原始碼,Requset物件)和響應(Response,自己封裝Response),
    序列化,
    檢視,
    路由,
    解析器(DEFAULT_PARSER_CLASSES,全域性配置,區域性配置),
    響應器(DEFAULT_RENDERER_CLASSES,全域性配,區域性配),
    認證:校驗是否登入(有內建,自定義,全域性配置,區域性配置)
    許可權:是否有許可權訪問某些介面(有內建,自定義,全域性配置,區域性配置)
    頻率:限制訪問頻次(有內建,自定義,全域性配置,區域性配置),根據使用者ip,根據使用者id限制
    過濾:篩選,查詢出符合條件的
    排序:結果進行排序
    異常:全域性異常(自定義,全域性配置)
    
    版本控制(不講)
    分頁器
    文件生成
    jwt認證
   	Xadmin的使用
    路飛專案
    git
    redis
    簡訊
    支付寶支付
    
    

今日內容

1 books系列表介面

# urls.py
from django.urls import path,re_path
from api import views
urlpatterns = [
    path('books/', views.BookAPIView.as_view()),
    re_path('books/(?P<pk>\d+)', views.BookAPIView.as_view()),
]
# views.py
from rest_framework.response import Response

from api import models
from  rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from api.ser import BookModelSerializer

class BookAPIView(APIView):
    def get(self,request,*args,**kwargs):
        #查詢單個和查詢所有,合到一起
        # 查所有
        book_list=models.Book.objects.all().filter(is_delete=False)
        book_list_ser=BookModelSerializer(book_list,many=True)
        return Response(data=book_list_ser.data)
        #查一個

    def post(self,request,*args,**kwargs):
        # 具備增單條,和增多條的功能
        if isinstance(request.data,dict):

            book_ser=BookModelSerializer(data=request.data)
            book_ser.is_valid(raise_exception=True)
            book_ser.save()
            return Response(data=book_ser.data)
        elif isinstance(request.data,list):
            #現在book_ser是ListSerializer物件
            from rest_framework.serializers import ListSerializer
            book_ser = BookModelSerializer(data=request.data,many=True)  #增多條
            print('--------',type(book_ser))
            book_ser.is_valid(raise_exception=True)
            book_ser.save()
            # 新增---》ListSerializer--》create方法
            # def create(self, validated_data):
            #   self.child是BookModelSerializer物件
            #   print(type(self.child))
            #     return [
            #         self.child.create(attrs) for attrs in validated_data
            #     ]
            return Response(data=book_ser.data)

    def put(self,request,*args,**kwargs):
        # 改一個,改多個
        #改一個個
        if kwargs.get('pk',None):
            book=models.Book.objects.filter(pk=kwargs.get('pk')).first()
            book_ser = BookModelSerializer(instance=book,data=request.data,partial=True)  # 增多條
            book_ser.is_valid(raise_exception=True)
            book_ser.save()
            return Response(data=book_ser.data)
        else:
            #改多個,
            # 前端傳遞資料格式[{id:1,name:xx,price:xx},{id:1,name:xx,price:xx}]
            # 處理傳入的資料  物件列表[book1,book2]  修改的資料列表[{name:xx,price:xx},{name:xx,price:xx}]
            book_list=[]
            modify_data=[]
            for item in request.data:
                #{id:1,name:xx,price:xx}

                pk=item.pop('id')
                book=models.Book.objects.get(pk=pk)
                book_list.append(book)
                modify_data.append(item)
            # 第一種方案,for迴圈一個一個修改
            #把這個實現
            # for i,si_data in enumerate(modify_data):
            #     book_ser = BookModelSerializer(instance=book_list[i], data=si_data)
            #     book_ser.is_valid(raise_exception=True)
            #     book_ser.save()
            # return Response(data='成功')
            # 第二種方案,重寫ListSerializer的update方法
            book_ser = BookModelSerializer(instance=book_list,data=modify_data,many=True)
            book_ser.is_valid(raise_exception=True)
            book_ser.save()  #ListSerializer的update方法,自己寫的update方法
            return Response(book_ser.data)
            # request.data
            #
            # book_ser=BookModelSerializer(data=request.data)

    def delete(self,request,*args,**kwargs):
        #單個刪除和批量刪除
        pk=kwargs.get('pk')
        pks=[]
        if pk:
            # 單條刪除
            pks.append(pk)
        #不管單條刪除還是多條刪除,都用多條刪除
        #多條刪除
        # {'pks':[1,2,3]}
        else:
            pks=request.data.get('pks')
        #把is_delete設定成true
        # ret返回受影響的行數
        ret=models.Book.objects.filter(pk__in=pks,is_delete=False).update(is_delete=True)
        if ret:
            return Response(data={'msg':'刪除成功'})
        else:
            return Response(data={'msg': '沒有要刪除的資料'})

ser.py


from rest_framework import serializers
from api import models


#寫一個類,繼ListSerializer,重寫update
class BookListSerializer(serializers.ListSerializer):
    # def create(self, validated_data):
    #     print(validated_data)
    #     return super().create(validated_data)
    def update(self, instance, validated_data):
        print(instance)
        print(validated_data)
        # 儲存資料
        # self.child:是BookModelSerializer物件
        # ll=[]
        # for i,si_data in enumerate(validated_data):
        #     ret=self.child.update(instance[i],si_data)
        #     ll.append(ret)
        # return ll
        return [
            # self.child.update(物件,字典) for attrs in validated_data
            self.child.update(instance[i],attrs) for i,attrs in enumerate(validated_data)
        ]



#如果序列化的是資料庫的表,儘量用ModelSerializer
class BookModelSerializer(serializers.ModelSerializer):
    # 一種方案(只序列化可以,反序列化有問題)
    # publish=serializers.CharField(source='publish.name')
    # 第二種方案,models中寫方法

    class Meta:
        list_serializer_class=BookListSerializer
        model=models.Book
        # fields='__all__'
        # 用的少
        # depth=0
        fields = ('name','price','authors','publish','publish_name','author_list')

        extra_kwargs={
            'publish':{'write_only':True},
            'publish_name':{'read_only':True},
            'authors':{'write_only':True},
            'author_list':{'read_only':True}
        }
# models.py
from django.db import models




from django.contrib.auth.models import AbstractUser
class BaseModel(models.Model):
    is_delete=models.BooleanField(default=False)
    # auto_now_add=True 只要記錄建立,不需要手動插入時間,自動把當前時間插入
    create_time=models.DateTimeField(auto_now_add=True)
    # auto_now=True,只要更新,就會把當前時間插入
    last_update_time=models.DateTimeField(auto_now=True)
    # import datetime
    # create_time=models.DateTimeField(default=datetime.datetime.now)
    class Meta:
        # 單個欄位,有索引,有唯一
        # 多個欄位,有聯合索引,聯合唯一
        abstract=True  # 抽象表,不再資料庫建立出表




class Book(BaseModel):
    id=models.AutoField(primary_key=True)
    # verbose_name admin中顯示中文
    name=models.CharField(max_length=32,verbose_name='書名',help_text='這裡填書名')
    price=models.DecimalField(max_digits=5,decimal_places=2)
    # 一對多的關係一旦確立,關聯欄位寫在多的一方
    #to_field 預設不寫,關聯到Publish主鍵
    #db_constraint=False  邏輯上的關聯,實質上沒有外來鍵練習,增刪不會受外來鍵影響,但是orm查詢不影響
    publish=models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING,db_constraint=False)

    # 多對多,跟作者,關聯欄位寫在 查詢次數多的一方

    # 什麼時候用自動,什麼時候用手動?第三張表只有關聯欄位,用自動    第三張表有擴充套件欄位,需要手動寫
    # 不能寫on_delete
    authors=models.ManyToManyField(to='Author',db_constraint=False)
    class Meta:
        verbose_name_plural='書表'  # admin中表名的顯示

    def __str__(self):
        return self.name

    @property
    def publish_name(self):
        return self.publish.name
    # def author_list(self):
    def author_list(self):
        author_list=self.authors.all()
        # ll=[]
        # for author in author_list:
        #     ll.append({'name':author.name,'sex':author.get_sex_display()})
        # return ll
        return [ {'name':author.name,'sex':author.get_sex_display()}for author in author_list]

class Publish(BaseModel):
    name = models.CharField(max_length=32)
    addr=models.CharField(max_length=32)
    def __str__(self):
        return self.name


class Author(BaseModel):
    name=models.CharField(max_length=32)
    sex=models.IntegerField(choices=((1,'男'),(2,'女')))
    # 一對一關係,寫在查詢頻率高的一方
    #OneToOneField本質就是ForeignKey+unique,自己手寫也可以
    authordetail=models.OneToOneField(to='AuthorDetail',db_constraint=False,on_delete=models.CASCADE)

class AuthorDetail(BaseModel):
    mobile=models.CharField(max_length=11)

# 二、表斷關聯
# 1、表之間沒有外來鍵關聯,但是有外來鍵邏輯關聯(有充當外來鍵的欄位)
# 2、斷關聯後不會影響資料庫查詢效率,但是會極大提高資料庫增刪改效率(不影響增刪改查操作)
# 3、斷關聯一定要通過邏輯保證表之間資料的安全,不要出現髒資料,程式碼控制
# 4、斷關聯
# 5、級聯關係
#       作者沒了,詳情也沒:on_delete=models.CASCADE
#       出版社沒了,書還是那個出版社出版:on_delete=models.DO_NOTHING
#       部門沒了,員工沒有部門(空不能):null=True, on_delete=models.SET_NULL
#       部門沒了,員工進入預設部門(預設值):default=0, on_delete=models.SET_DEFAULT

2 分頁器

#views.py

# 查所有,才需要分頁
from rest_framework.generics import ListAPIView
# 內建三種分頁方式
from  rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
'''
PageNumberPagination
    page_size:每頁顯示的條數
'''
class MyPageNumberPagination(PageNumberPagination):
    #http://127.0.0.1:8000/api/books2/?aaa=1&size=6
    page_size=3  #每頁條數
    page_query_param='aaa' #查詢第幾頁的key
    page_size_query_param='size' # 每一頁顯示的條數
    max_page_size=5    # 每頁最大顯示條數


# class MyLimitOffsetPagination(LimitOffsetPagination):
#     default_limit = 3   # 每頁條數
#     limit_query_param = 'limit' # 往後拿幾條
#     offset_query_param = 'offset' # 標杆
#     max_limit = 5   # 每頁最大幾條

class MyCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'  # 每一頁查詢的key
    page_size = 2   #每頁顯示的條數
    ordering = '-id'  #排序欄位
# class BookView(ListAPIView):
#     # queryset = models.Book.objects.all().filter(is_delete=False)
#     queryset = models.Book.objects.all()
#     serializer_class = BookModelSerializer
#     #配置分頁
#     pagination_class = MyCursorPagination

# 如果使用APIView分頁
from utils.throttling import MyThrottle
class BookView(APIView):
    # throttle_classes = [MyThrottle,]
    def get(self,request,*args,**kwargs):
        book_list=models.Book.objects.all()
        # 例項化得到一個分頁器物件
        page_cursor=MyPageNumberPagination()

        book_list=page_cursor.paginate_queryset(book_list,request,view=self)
        next_url =page_cursor.get_next_link()
        pr_url=page_cursor.get_previous_link()
        # print(next_url)
        # print(pr_url)
        book_ser=BookModelSerializer(book_list,many=True)
        return Response(data=book_ser.data)

    
  

#settings.py
REST_FRAMEWORK={
    'PAGE_SIZE': 2,
}

2 根據ip進行頻率限制

# 寫一個類,繼承SimpleRateThrottle,只需要重寫get_cache_key 
from rest_framework.throttling import ScopedRateThrottle,SimpleRateThrottle

#繼承SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
    scope='luffy'
    def get_cache_key(self, request, view):
        print(request.META.get('REMOTE_ADDR'))
        return request.META.get('REMOTE_ADDR')   # 返回
    
# 區域性使用,全域性使用 
REST_FRAMEWORK={
    'DEFAULT_THROTTLE_CLASSES': (
        'utils.throttling.MyThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
        'luffy': '3/m'  # key要跟類中的scop對應
    },
}

# python3 manage.py runserver 0.0.0.0:8000   你們區域網就可以相互訪問


# 內網穿透

作業

1 一個表可不可以有多個自增欄位

2 用GenericAPIView重寫book的單增,群增,。。。。