注意:
    DRF是一种编程风格,不是框架。drf真实好用,其好处可以认为是组件式的。他主要很好的解决下面几个问题。

权限

身份认证
权限
注意:都可以使用全局,或者局部;个人建议局部使用
身份认证
from rest_framework.permissions import IsAuthenticated,IsAdminUser;具体使用见jwt
自定义权限
局部使用
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
  """
  自定义权限只允许对象的所有者编辑它。
  """
  def has_object_permission(self, request, view, obj):
      # 读取权限允许任何请求,
      # 所以我们总是允许GET,HEAD或OPTIONS请求。
      if request.method in permissions.SAFE_METHODS:
          return True
      # 只有该snippet的所有者才允许写权限。
      return obj.owner == request.user

序列化

概要
序列化 序列化器会将模型类对象转化为字典,response 后变成 json 字符串。

反序列化 把客户发送过来的数据, request 后变成字典,序列化器可以将字典转成模型,同时校验客户发送数据的合法性。

序列化器(类) 开发者定义的一个实现序列化和反序列化的类。

模型序列化器(类) 一种对应 Django 模型的序列化器。【ModelSerializer】

序列化器基类 DRF 中所有的序列化器类都必须继承于序列化器基类(rest_framework.serializers.Serializer) 。

模型序列化器基类 序列化器基类的子类,同时 DRF 中所有的序列化器类都必须继承于 rest_framework.serializers.ModelSerializer。
ModelSerializer
from rest_framework import serializers 
class CategoryModelserializers(serializers.ModelSerializer):
#todo 这里可以添加对某些参数的校验,如下:BB
  class Meta:
      model = Category
      # 指定
      fields =['id',"name"]
      # 所有的
fields ="__all__"
# 排除某些字段
exclude = ('status',)
BB实例
name = serializers.CharField(max_length=50, label='分类名称', help_text='帮助文档',
                                validators=[validators.UniqueValidator(queryset=Project.objects.all(), message="分类名已存在")])
email = serializers.EmailField(max_length=50)
如果创建和更新较固定。序列化与反序列化
from rest_framework import serializers
from .models import book, LANGUAGE_CHOICES, STYLE_CHOICES


class bookSerializer(serializers.Serializer):
  id = serializers.IntegerField(read_only=True)
  title = serializers.CharField(required=False, allow_blank=True, max_length=100)
  style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

  def create(self, validated_data):
      """
      根据提供的验证过的数据创建并返回一个新的`Snippet`实例。
      """
      return book.objects.create(**validated_data)

  def update(self, instance, validated_data):
      """
      根据提供的验证过的数据更新和返回一个已经存在的`Snippet`实例。
      """
      instance.title = validated_data.get('title', instance.title)
      instance.style = validated_data.get('style', instance.style)
      instance.save()
      return instance
注意:
create还是update,django内部会自行调用。开发者只管在视图中save即可。
参考链接:https://www.cnblogs.com/zhaoyuanshi/p/16079279.html
数据校验
字段校验
验证一个字段
class TestSerializer(serializers.Serializer):
  def validate_<字段名>(self,value):
        pass
        return value

验证多个字段
class TestSerializer(serializers.Serializer):
  def validate(self,attrs):
      pass
      return attrs
自定义校验validators
一般在模型序列调用is_valid方法时才会校验。不限于get,post请求
serializer = StudentSerializer(instance=student, data=data)
# 获取验证结果
serializer.is_valid(raise_exception=True) # 抛出异常

自定义实现如下

def check_sex(value):
  """
  外部校验器 validator
  """
  if value not in [0, 1, 2]:
      raise serializers.ValidationError(
          detail='性别设置错误',
          code='check_sex'
      )
  return value

class StudentSerializer(serializers.Serializer):
  # read_only=True,在客户端提交数据[反序列化阶段]不要求的字段
  id = serializers.IntegerField(read_only=True)
  # 最大值 max_value 和最小值 min_value
  age = serializers.IntegerField(
      max_value=100,
      min_value=0,
      error_messages={
          "min_value": "Age must older than 0",
          "max_value": "Age must younger than 100"
      }
  )
  # 默认值为 True
  active = serializers.BooleanField(default=True)
  # validator的值为列表,列表的成员为函数名,而不是函数的调用
  sex = serializers.IntegerField(validators=[check_sex])

 

视图

DRF框架的视图的基类是 APIView
APIView的基本使用和View类似
用户发起请求,请求会转向到apiview时,apiview会继承drf的as_view,然后as_view继承django的view。
django的view会调用apiview的dispatch方法【这里其实就知道了请求的名字,也就知道路由】,然后将结果返回到django的as_view方法。
接着django的as_view返回给apiview的as_view,最后apiview将结果指向到用户定义的apiview

视图如下

 

apiview与view的区别
Django默认的View请求对象是 HttpRequest,REST framework 的请求对象是 Request。
Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
HttpRequest.GET.get ————> Request.query_params
HttpRequest.POST.get 、HttpRequest.body.get————> Request.data;json.loads(request.body)
Django默认的View响应对象是 HttpResponse(以及子类),REST framework 的响应对象是Response。
支持定义的属性:
  authentication_classes列表或元祖,身份认证类
  permissoin_classes列表或元祖,权限检查类
  throttle_classes列表或元祖,流量控制类。
   
4. 任何APIException异常都会被捕获到,并且处理成合适的响应信息;APIException异常捕获  
GenericAPIView
GenericAPIView是继承自APIView,GenericAPIView肯定在APIView的基础上封装了一些属性和方法:增加了对于列表视图和详情视图可能用到的通用方法和属性的支持

  属性:
      queryset 设置结果集
      serializer_class 设置序列化器
      lookup_field 查询指定的对象
  方法:
      get_queryset(self) 返回视图使用的查询集
      get_serializer(self,_args, *_kwargs) 返回序列化器对象
      get_object(self) 返回详情视图所需的模型类数据对象
GenericAPIView和Mixin配合使用
mixin类提供用于提供基本视图行为的操作。
请注意,mixin类提供了操作方法,而不是直接定义处理程序方法,例如.get()和.post()。这允许更灵活的行为组合。

ListModelMixin
提供一种.list(request, *args, **kwargs)实现列出查询集的方法。

CreateModelMixin
提供.create(request, *args, **kwargs)实现创建和保存新模型实例的方法。

RetrieveModelMixin
提供一种.retrieve(request, *args, **kwargs)方法

UpdateModelMixin
提供.update(request, *args, **kwargs)实现更新和保存现有模型实例的方法。

DestroyModelMixin
提供一种.destroy(request, *args, **kwargs)实现删除现有模型实例的方法。
viewSet
APIView,GenericAPIView,ListAPIView 都是继承自View
继承自View的类视图,只能定义相同的一个函数名,例如:只能定义一个get,post方法
那么:
  列表视图中 设置了 queryset,serializer_class get,post
  详情视图中也设置了 queryset,serializer_class get,put,delete
  能否将这两个视图合并?我们是可以将 列表和详情视图 组合到一起的,称之为 视图集 ViewSet
它不提供任何方法处理程序( 如get(),post() ),而是提供了诸如list() create()之类的操作
三级视图
CreateAPIView
提供post方法处理程序。
ListAPIView
用于只读端点以表示模型实例的集合。
提供get方法处理程序。

RetrieveAPIView
用于表示单个模型实例的只读端点。
提供get方法处理程序。

DestroyAPIView
用于单个模型实例的仅删除端点。
提供delete方法处理程序。

UpdateAPIView
用于单个模型实例的仅更新端点。
提供put和patch方法处理程序。

ListCreateAPIView
用于读写端点以表示模型实例的集合。
提供get和post方法处理程序。

RetrieveUpdateAPIView
用于读取或更新端点以表示单个模型实例。
提供get,put并且patch方法处理。
RetrieveDestroyAPIView
用于读取或删除端点以表示单个模型实例。
提供get和delete方法处理程序。
RetrieveUpdateDestroyAPIView
用于读写 - 删除端点以表示单个模型实例。
提供get,put,patch和delete方法处理。

分页器

分页模式
rest framework中提供了三种分页模式:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
全局配置在setting文件
  REST_FRAMEWORK = {
      'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
      'PAGE_SIZE': 100
  }

局部配置
  class PublisherViewSet(ModelViewSet):
      queryset = models.Publisher.objects.all()   # 只有差所有才需要分页
      serializer_class = PublisherModelSerializer
      pagination_class = PageNumberPagination # 注意不是列表(只能有一个分页模式)

DRF内置分页器
  PageNumberPagination
  按页码数分页,第n页,每页显示m条数据
  例如:http://127.0.0.1:8000/api/article/?page=2&size=1
  class MyPageNumberPagination(PageNumberPagination):
      page_size=3 #每页条数
      page_query_param='aaa' #查询第几页的key, 默认是page
      page_size_query_param='size' # 每一页显示的条数
      max_page_size=5   # 每页最大显示条数


  LimitOffsetPagination
  分页,在n位置,向后查看m条数据
  例如:http://127.0.0.1:8000/api/article/?offset=2&limit=2
  class MyLimitOffsetPagination(LimitOffsetPagination):
      default_limit = 3   # 每页条数
      limit_query_param = 'limit' # 往后拿几条
      offset_query_param = 'offset' # 标杆
      max_limit = 5   # 每页最大几条


  CursorPagination
  加密分页,把上一页和下一页的id值记住,只提供上一页和下一页url,效率最高,速度最快
  列如: http://127.0.0.1:8000/api/book2/?cursor=cD0xMw%3D%3D
  class MyCursorPagination(CursorPagination):
      cursor_query_param = 'cursor' # 每一页查询的key
      page_size = 2   # 每页显示的条数
      ordering = '-id' # 排序字段
调用方式
# 查询语句
  ret=models.Book.objects.all()
      # 实例化对象
  page=MyPageNumberPagination()
      # 在数据库中获取分页的数据
  page_list=page.paginate_queryset(ret,request,view=self)

频率

# 获取登录主机的id
id = request.META.get('REMOTE_ADDR')
# 访问者,dict类型
visit_record
#访问时间
self.history

路由

1.一般路由
viewSet
  2.DefaultRouter
  3.SimpleRouter
4.自定义视图路由action
一般路由
我们在使用视图集的过程中,要对路由进行映射,当扩展方法过多时,路由会写的很长,如下:
如果在括号中不写东西的话,那么自带的方法是比较少的get,post,put,delete
urlpatterns = [
path('bookinfo/',BookOperation.as_view({'get':'list','post':'create'})),
re_path(r'bookinfo/(?P<pk>\d+)/',BookOperation.as_view({'get':'retrieve','put':'update','delete':'destroy'}))
]
DefaultRouter
from django.urls import path,re_path
from rest_framework.routers import DefaultRouter,SimpleRouter
from .views import BookOperation

urlpatterns = [
# path('bookinfo/',BookOperation.as_view({'get':'list','post':'create'})),
# re_path(r'bookinfo/(?P<pk>\d+)/',BookOperation.as_view({'get':'retrieve','put':'update','delete':'destroy'}))
]

# 创建路由对象
router = DefaultRouter()
SimpleRouter
# views.py
class BookModelViewSet(ModelViewSet):
  queryset = models.Book.objects.all()
  serializer_class = ser.BookModelSerializer

  def list(self, request, *args, **kwargs):
      book_list = self.get_queryset()[:3]
      ser_obj = self.get_serializer(book_list, many=True)
      return Response(ser_obj.data)

   
# urls.py
# 第一步
from rest_framework import routers
from app01 import views
# 第二步
router = routers.SimpleRouter()
# 第三步
router.register(prefix='books', viewset=views.BookModelViewSet)

urlpatterns = [
  url(r'^admin/', admin.site.urls),
]
# 第四步
urlpatterns += router.urls


# 新增的两条url
print(router.urls)
[
  <RegexURLPattern book-list ^books/$>,
  <RegexURLPattern book-detail ^books/(?P<pk>[^/.]+)/$>
]
自定义视图路由action
# 用途
为了给继承自ModelViewSet的视图类中定义的函数也添加路由
# 使用
from rest_framework.decorators import action
  class BookModelViewSet(ModelViewSet):

      queryset = models.Book.objects.all()
      serializer_class = ser.BookModelSerializer

      @action(methods=['get'], detail=False)
      def get_top3(self, request):
          print(request.user.username)
          book_list = self.get_queryset()[:3]
          ser_obj = self.get_serializer(book_list, many=True)
          return Response(ser_obj.data)
# 参数说明:
methods参数赋值一个列表,放可以访问该方法的请求方式
  detail参数是布尔值:
  detail=False表示方法是不使用pk;^books/get_top3/$
      反之使用pk参数;^books/(?P<pk>[^/.]+)/get_top3/$
       
# 总结:action装饰器,放在被装饰的函数上方,method:请求方式,detail:是否带pk

DefaultRouter 与SimpleRouter区别

在这里可以看到基于drf的viewSet与传统的django view有本质的不同DRF编写的ViewSet不是直接写在urlpatterns中,而是通过router注册的形式。
先声明一个Router类的实例,然后再这个router中调用register方式注册api
相比较DefaultRouter,SimpleRouter少了很多URLPAttern,一般多出的是detail,对应的方法。

 

解析器

get
request.query_params.get("data")
put
update_data = request.data.get("is_show")
post
data = json.loads(request.body.decode())

原生的Django的Request只支持如下两种类型
application/x-www-form-urlencoded(只能上传文本格式的文件)----->request.POST
multipart/form-data(以二进制的形式上传)---->reuqest.FILES
很好的解析了django为何使用json的json.loads进行转化,而不是直接使用自己的方法。
当然drf解决了这个问题,但是实际的开发过程中并没有太多的开发者直接使用。
具体使用如下:
全局配置
REST_FRAMEWORK = {
  'DEFAULT_PARSER_CLASSES': (
      'rest_framework.parsers.JSONParser',
  )
}
局部使用
class BookViewSet(ModelViewSet):
  queryset = models.Book.objects.all()
  serializer_class = BookModelSerializer
  parser_classes = [JSONParser, ]

响应器

全局使用
settings里配置:
REST_FRAMEWORK = {
  'DEFAULT_RENDERER_CLASSES':['rest_framework.renderers.JSONRenderer']
}
局部使用
from rest_framework.renderers import HTMLFormRenderer,BrowsableAPIRenderer
class BookDetailView(APIView):
  renderer_classes = [HTMLFormRenderer,BrowsableAPIRenderer ]
  def get(self,request,pk):
      book_obj=models.Book.objects.filter(pk=pk).first()
      bs=BookSerializers(book_obj,many=False)
      return Response(bs.data)
 
posted on 2022-08-21 11:05  topass123  阅读(16)  评论(0编辑  收藏  举报