DRF 基本功能梳理 demo
模型類
from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=50, verbose_name="標題", help_text="標題") # help_text : 文檔的顯示信息 collect_num = models.IntegerField(default=0, verbose_name="收藏數", help_text="收藏數") author = models.ForeignKey('Author', on_delete=models.CASCADE, related_name='books', help_text="作者") def __str__(self): return self.title class Author(models.Model): name = models.CharField(max_length=30, verbose_name="姓名", help_text="姓名") age = models.IntegerField(default=18, verbose_name="年齡", help_text="年齡") publish = models.ManyToManyField('Publish', help_text="出版社") def __str__(self): return self.name class Publish(models.Model): name = models.CharField(max_length=30, verbose_name="出版社名", help_text="出版社名") create_dt = models.DateField(verbose_name="成立時間", default="1995-06-01", help_text="成立時間") def __str__(self): return self.name
模型類拓展字段
在模型類中中可以以 property 修飾方法為類變量, 然后即可在序列化器中作為邏輯數據庫字段使用
通常可以進行系列數據的拓展屬性展示, 但是此字段不可帶有任何參數裝飾器本身就是去除到 () 執行, 因此參數是無從傳遞
倘若需要從request 傳遞的動態參數, 則只能在 序列化器中操作
class XXX(models.Model): .....
.....
@property def xxx(self): if self.x1: return { "x2": self.x2, "x3": self.x3, } return { "x2": None, "x3": None } class Meta: verbose_name = "某某模型" verbose_name_plural = verbose_name
序列化類
from rest_framework import serializers from .models import Book, Author, Publish # 自定義校驗方法 def check_name(value): if "羊駝" not in value: raise serializers.ValidationError("必須要有羊駝") return value class AuthorSerializer(serializers.ModelSerializer): # 常規方式對字段進行校驗, 將 model 的字段在這里重復寫一遍 name = serializers.CharField(max_length=30, label="姓名", validators=[check_name]) # validators 自定義校驗方法 """Meta 相關屬性""" class Meta: model = Author # fields = ["id", "name"] # 自己指定要序列化的字段 """ fields 和 exclude 不能同時存在 Cannot set both 'fields' and 'exclude' options on serializer AuthorSerializer. 這兩個字段的源碼位置 ModelSerializer.get_field_names # exclude = ["id"] # 排除某些字段 """ # 生成指定字段 fields = "__all__" # 所有的字段都加進來 # 設置只讀字段 read_only_fields = [] # 源碼位置 ModelSerializer.get_extra_kwargs # 給字段添加額外約束 extra_kwargs = { # # 源碼位置 ModelSerializer.get_extra_kwargs "name": { "max_length": 30, "min_length": 1, } } """多對一的復寫""" # 多對一的地方需要使用反向字段 如果未設置則為 關聯對象表名+ _set 如果設置了 related_name, 則直接使用 related_name # 多的一方無法自定義字段國擴展 # books = serializers.PrimaryKeyRelatedField(read_only=True, many=True) # 使用 關聯對象的 __str__ 方法作為填充 books = serializers.StringRelatedField(read_only=True, many=True) class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" """一對多字段的復寫, 若不復寫取出的是 id (int) """"" # 0. 默認的外鍵字段序列化方式, 取出的是 關聯對象的 ID/ PK # author = serializers.PrimaryKeyRelatedField(read_only=True) # 1. source 對序列化方式進行復寫 author_id = serializers.CharField(source="author.id", read_only=True) # str(obj.publish.id) 取 pk 也是一樣 # 2. 可實現自定義拓展字段, 一對多 在 "一" 的一方可以使用, 在 "多" 的一方不可使用 author_name = serializers.CharField(source="author.name", read_only=True) # 3. 返回 關聯對象的 __str__ 方法 - 必須基于已有的字段 # author = serializers.StringRelatedField(read_only=True) # 4. 使用序列化器作為關聯 - 必須基于已有的字段 author = AuthorSerializer(read_only=True) # 取出所有的子字段 """ 單字段校驗 - 實現對字段的額外要求的校驗 固定格式: validate(可自動提示) + _ + 校驗字段名 自定義邏輯在主校驗邏輯之后, 相當于額外校驗, 而非完全替換校驗 """"" @staticmethod def validate_title(value): """自定義邏輯""" if "羊駝" not in value: # return False # return 不管是什么都是通過 # 必須拋出異常才可以表示校驗不通過 """ HTTP 400 Bad Request Allow: GET, POST, HEAD, OPTIONS Content-Type: application/json Vary: Accept { "title": [ "不贊美羊駝不通過" ] } """ raise serializers.ValidationError("不贊美羊駝不通過") return value # 通過返回 value 即可 """ 多字段校驗 - 實現對字段的額外要求的校驗 validate 自定義邏輯在主校驗邏輯之后, 相當于額外校驗, 而非完全替換校驗 """"" def validate(self, attrs): """ bs = BookSerializer(data=request.data) attrs: 就是外界穿過來的數據 request.data 多字段的校驗里面也可以對單字段分別校驗 """"" # 自定義邏輯 title = attrs["title"] collect_num = attrs["collect_num"] if str(collect_num) not in title: """ HTTP 400 Bad Request Allow: GET, POST, HEAD, OPTIONS Content-Type: application/json Vary: Accept { "non_field_errors": [ "書名里面要有收藏數里面的數字" ] } """ raise serializers.ValidationError("書名里面要有收藏數里面的數字") return attrs # 校驗通過返回原參數 """ 數據入庫 - 創建 create 數據入庫 - 更新 update """ def create(self, validated_data): """ :param validated_data: 校驗后的數據 :return: """ book = Book.objects.create(**validated_data) return book def update(self, instance, validated_data): """ :param instance: 待更新的對象 :param validated_data: 校驗后的數據 :return: """ instance.title = validated_data["title"] instance.collect_num = validated_data["collect_num"] instance.save() return instance class PublishSerializer(serializers.ModelSerializer): class Meta: model = Publish fields = "__all__"
序列化中拓展字段
部分場景可能存在需要動態參數, 即從request 中的數據作為參數對某些字段控制
如以下場景. 在訂單序列化器中加入 is_flavor 字段用于標識當前用戶對此訂單是否已收藏
在序列化中, self 的 context 上下文會存儲 request , 從而實現此操作
# 工單列表序列化器 class OrderListModelSerializer(serializers.ModelSerializer): is_flavor = serializers.SerializerMethodField() def get_is_flavor(self, obj): user = self.context['request'].user return obj.is_flavor(user.u_name or user.username) class Meta: model = models.Order fields = ( ....."is_flavor", ..... )
路由
主路由
"""untitled1 URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path, include from rest_framework.documentation import include_docs_urls urlpatterns = [ path('admin/', admin.site.urls), path('yangtuo/', include('yangtuo.urls')), path('docs/', include_docs_urls(title='yangtuo API')), ]
子路由
from rest_framework import routers from .views import BooksView, AuthorsView, PublishesView from django.urls import path routers = routers.DefaultRouter() routers.register('books', viewset=BooksView) routers.register('author', viewset=AuthorsView) routers.register('publish', viewset=PublishesView) # 用 action 裝飾器替換 """ # 自定義路由 path("publish/get_yangtuo_publish/", PublishesView.as_view({"get": "get_yangtuo_publish"})), # 自定義部分更新路由 path("publish/change_publish/<int:pk>/", PublishesView.as_view({"put": "change_publish"})) """ urlpatterns = [ ] urlpatterns += routers.urls print(urlpatterns)
過濾器
詳細的文檔這里 這里這里
常用過濾器
CharFilter 字符串過濾器
DateTimeFilter 時間過濾器
BooleanFilter 布爾過濾器
NumberFilter 數字過濾器
DateRangeFilter 時間范圍過濾器
內部屬性
field_name 查詢字段
lookup_expr 匹配模式(與orm 的運算符一致)
label 顯示字段 (用于 drf 渲染器中的字段)
method 指定函數
指定函數的時候必須傳遞 三個固定參數
queryset 在視圖中的 get_queryset 函數的返回結果
name
value request 指定過濾參數傳遞的值
meta 字段
model 指定模型, 類, 不是字符串
fields 過濾參數, 不加入這里, 上面定義了也是可以使用的. 此處是用于指定數據庫字段所用
exclude 排除字段, 不能用作過濾的參數
示例
class OrderFilter(django_filters.rest_framework.FilterSet): i_followers = django_filters.CharFilter(field_name='followers', lookup_expr='icontains', label="關注人(包含)") start_date = django_filters.rest_framework.DateTimeFilter(field_name='create_time', lookup_expr='gte', label="開始時間") end_date = django_filters.rest_framework.DateTimeFilter(field_name='create_time', lookup_expr='lte', label="結束時間") handler = django_filters.CharFilter(method='handler_filter', label="待處理人") is_flavor = django_filters.CharFilter(method='is_flavor_filter', label="收藏工單") @staticmethod def is_flavor_filter(queryset, name, value): return queryset.filter(flavors__operator=value) @staticmethod def handler_filter(queryset, name, value): return queryset.filter(Q(cur_nodes_o__operator__icontains=value) | (Q(creator=value, status="entering"))) \ .distinct() class Meta: model = models.Order fields = ('creator', 'tenant', 'cur_slot', 'status', 'recheck', 'mould', 'order_no')
生效
class xxxxx(ReadOnlyViewSet): queryset = xxxxx.objects.all() serializer_class = ...... filter_class = xxxxxFilter search_fields = (....) filter_fields = (....) ordering_fields = (....) ordering = (....)
視圖類
from django_filters.rest_framework import DjangoFilterBackend from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.filters import OrderingFilter from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination from rest_framework.permissions import IsAuthenticated from rest_framework.throttling import UserRateThrottle from rest_framework.views import exception_handler from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response from rest_framework.decorators import action from .models import Book, Author, Publish from .serializers import BookSerializer, AuthorSerializer, PublishSerializer # 序列化相關的注釋解析 class BooksView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer def list(self, request, *args, **kwargs): books = Book.objects.all() # 多個對象的時候 要 many=True bs = BookSerializer(instance=books, many=True) # instance 要被序列化的對象 return Response(bs.data) def create(self, request, *args, **kwargs): """序列化器對數據類型的校驗 1. 字段類型 int/char/data/bool/mail/uuid.... 2. 字段屬性 max_length/required/read_only 3. 單字段 (方法) 4. 多字段 (方法) 5. 自定義 (方法) """"" print(request.data) """ { "csrfmiddlewaretoken": "o1XJ6weot4dx4fXwgIC2goYW3aeHUXer4IsIVoL7oyw31gfAi7TTUsTjnxBn9X0F", "title": "海洋之歌", "collect_num": "999" } """ bs = BookSerializer(data=request.data) # 把數據序列化 用 data 參數傳遞 if bs.is_valid(raise_exception=True): # True / False # raise_exception 控制是否提示校驗不通過的信息報錯 """ 報錯回傳示例 HTTP 400 Bad Request Allow: GET, POST, HEAD, OPTIONS Content-Type: application/json Vary: Accept { "title": [ "該字段不能為空。" ], "collect_num": [ "請填寫合法的整數值。" ] } """ bs.save() # 調用序列化器里面執行 create 方法 return Response(bs.data) def update(self, request, *args, **kwargs): book = Book.objects.get(id=kwargs["pk"]) # instance 要更新的對象, data 更新的數據 bs = BookSerializer(instance=book, data=request.data) if bs.is_valid(raise_exception=True): # 校驗 bs.save() # 入庫 return Response(bs.data) # 視圖類的相關注解 # 0.視圖類繼承關系 """ from rest_framework.viewsets import ModelViewSet ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet) from rest_framework.viewsets import ReadOnlyModelViewSet ReadOnlyModelViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, GenericViewSet) from rest_framework.generics import CreateAPIView..... CreateAPIView(mixins.CreateModelMixin, GenericAPIView) ListAPIView(mixins.ListModelMixin, GenericAPIView) RetrieveAPIView(mixins.RetrieveModelMixin, GenericAPIView) ..... from rest_framework import mixins mixins CreateModelMixin - > create() CreateModelMixin - > list() RetrieveModelMixin - > retrieve() UpdateModelMixin - > update() DestroyModelMixin - > destroy() from rest_framework.viewsets import GenericViewSet GenericViewSet(ViewSetMixin, generics.GenericAPIView): ViewSetMixin.as_view() self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) GenericAPIView.(views.APIView) get_queryset() get_object() get_serializer() filter_queryset() paginate_queryset() APIView(View): as_view() check_permissions() ... """ # 1. request 的參數獲取 """ request.GET - > request.query_params request.POST / body - > request.data """ # 2. 視圖返回封裝 """ HttpResponse/JsonResponse..... -> Response(data=None, status=None, template_name=None, headers=None, exception=False, content_type=None) """ # 3. 返回狀態碼封裝 """ from rest_framework import status status.HTTP_100_CONTINUE.... """ # 4. 方法封裝 """ get - > list / retrieve post - > create put - > update delete - > destroy """ # 5. 常用屬性行為對象封裝 """ GenericAPIView: queryset = None serializer_class = None lookup_field = 'pk' # 默認查詢字段 lookup_url_kwarg = None # 使用示例 # queryset = Author.objects.all() # serializer_class = AuthorSerializer filter_backends = api_settings.DEFAULT_FILTER_BACKENDS # 過濾器 pagination_class = api_settings.DEFAULT_PAGINATION_CLASS # 分頁器 get_queryset() get_serializer() get_object() # 基于 lookup_field / lookup_url_kwarg """ # 6. CURD 封裝 """ ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet) """ class AuthorsView(ModelViewSet): queryset = Author.objects.all() # 可以被使用 self.get_queryset() 的地方調用到 serializer_class = AuthorSerializer # 可以被使用 self.get_serializer() 的地方調用到 lookup_url_kwarg = "id" # 優先級比 lookup_field 高, 源碼: lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field # 默認就是 pk lookup_field = "pk" # lookup_url_kwarg / lookup_field 設定的值必須是數據庫有的字段 def list(self, request, *args, **kwargs): # 動態查詢 """ 不要使用 authors = self.queryset 動態查詢是沒法自動拿到 .all() 的, 因此調用的時候還需要自己再加上 .all() 要不這樣 authors = self.queryset.all() 或者這樣 authors = self.get_queryset() """"" authors = self.get_queryset() # self.queryset authors_serializer = self.get_serializer() # self.serializer_class def retrieve(self, request, *args, **kwargs): # author = self.queryset.get(kwargs["id"]) author = self.get_object() # 等價上面 # 自定義分頁器 class MyPageNumberPagination(PageNumberPagination): page_size = 1000 # 復寫全局的頁面大小 page_size_query_param = 'page_size' # 前段指定每頁大小的定義字段 max_page_size = 10000 # 每頁最大可以指定的數量, 為了防止前段穿過來 page_size=100000000000000這樣的情況 # 自定義異常處理 def my_exception_handler(exc, context): response = exception_handler(exc, context) if response is not None: # APIException 類的異常 response.data['status_code'] = response.status_code # 其他異常類型自定義處理 else: from django.db import DatabaseError if isinstance(exc, DatabaseError): return Response("數據庫我特么裂開") else: return Response("其他異常") return Response("假設這里是個很好看的 404頁面") # 自定義方法/權限/認證/頻率/分頁/過濾/排序/異常處理/文檔 相關注釋 class PublishesView(ModelViewSet): """ # 文檔里面的注釋信息 list: 獲取所有出版社 """ queryset = Publish.objects.all() serializer_class = PublishSerializer """局部認證是完全替換全局, 而非取并集, 即兩種同時配置, 局部生效, 全局失效""" # 局部認證 authentication_classes = [SessionAuthentication, BasicAuthentication] # 局部權限 permission_classes = [IsAuthenticated] # 局部限流 throttle_classes = [UserRateThrottle] # throttle_scope = "yangtuo" # 限流器使用 ScopedRateThrottle 時的映射字段 # 局部分頁器 pagination_class = MyPageNumberPagination # 可使用自定義分頁器, 復寫 PageNumberPagination 的屬性實現自定義字段生效 # 過濾器 搜索 / 排序 filter_backends = [DjangoFilterBackend, OrderingFilter] # 注意導入的時候 from rest_framework.filters import OrderingFilter filterset_fields = ['name', 'create_dt', 'id'] ordering_fields = ['id', 'create_dt'] # ordering 前段參數 倒敘加個 - # 自定義接口方法, 以及生成路由 @action(methods=['GET'], detail=False) # detail 表示是否有參數 def get_yangtuo_publish(self, request): publishes = Publish.objects.filter(name__contains="羊駝") publish_serializer = self.serializer_class(instance=publishes, many=True) return Response(publish_serializer.data) # 額外添加參數, 更新局部信息 @action(methods=['PUT'], detail=True) def change_publish(self, request, pk): publish = self.get_object() data = request.data # partial 參數攜帶表示只對部分數據進行校驗保存 publish_serializer = self.serializer_class(instance=publish, data=data, partial=True) if publish_serializer.is_valid(raise_exception=True): publish_serializer.save() return Response(publish_serializer.data)
本文來自博客園,作者:羊駝之歌,轉載請注明原文鏈接:http://www.rzrgm.cn/shijieli/p/14882779.html

浙公網安備 33010602011771號