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)
本文来自博客园,作者:羊驼之歌,转载请注明原文链接:https://www.cnblogs.com/shijieli/p/14882779.html