模型类
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
序列化类
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__"
路由
主路由
"""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)
视图类
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)