drf框架

Posted zhuangshenhao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了drf框架相关的知识,希望对你有一定的参考价值。

一:drf

  drf的全称: django rest framework

  用处: 用来前后端分离的项目: 结合前段的vue框架, 数据库mysql

二: 安装

  在命令行中输入: pip3 install djangorestframework

三: 配置

INSTALLED_APPS = [
    app01.apps.App01Config,  # App的注册,一般命名:api
    # 注册: 在setting中配置,rest_framework实质上是一个app,需要注册才可以使用
    # 但是我不注册, 照样能使用
    rest_framework,
]

四: 接口

  1)  什么是接口?

  大白话解释: 两个东西通过一个东西进行连接、通信等, 一个东西就叫做接口

  wed程序中的接口:连接前后台页面尽心数据交互的媒介

  2) restful规范:

    为什么要有restful规范: 因为后台的语言有多种, 这样接口就会有多种, 所以要统一规范

    1. api表示url接口,

    2. 使用https协议,数据更加安全

    3. 一个接口有多个版本, 需要在url连接中表示, v1表示版本

      如: api. xiaohuar.com/v1/...

    4. 接口操作的数据源称之为资源, 资源在url连接中一般采用复数

      如: api. xiaohuar.com/books/...

    5. 请求的方式有多种, 用一个url处理,为了保证不混乱, 通过请求方式标识操作资源的方式

      get(pk)         获取所有(获取一个)

      post             增加一个或多个

      delete          删除一个或多个(数据不会真正的删除, 只会用一个字段标识)

      put/patch     整体更新/局部更新

    6. 资源往往涉及数据的各种操作:    过滤, 排序, 限制

      如:  api.xiaohuar.com/book/?search=西&ordering=-price&limit=3

      查找书本中有西的, 价格从小到大, 找出三本

    7. 返回状态码:  前台和后台约定好的

{
"status": 0  # 操作资源成功
"msg": "登录成功"   # 文字提示信息
"results": 需要返回前段的数据
}  
{"status": 1}  # 操作资源失败
{"status": 2}  # 操作资源成功, 但是没有与之匹配的数据

    8. 不能直接还回的资源, 如视频, 图片,等,

      需要还回url连接

五: 基于restful的原生路由

  总路由: 

from django.urls import url, include
from contrib importadmin

urlpatterns = [
     url(r^admin/, admin.site.urls),
     url(r^api/, include(api.urls))        
]

  子路由:

from django.urls import url

urlpatterns = [
   url(r‘^book/$‘, views.Book.as_view()),   # 查看有的数据
  url(r‘^book/(?P<pk>.*)/$‘, view.Book.as_view()), # 查看某一个数据 ]

  models.py

技术图片
from django.db.models import F
# 快熟导入模块alt + enter键

class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True  # 其它模型类继承该类, 该类不创建表

class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    img = models.ImageField(upload_to=icon, default=icon/default.jpg)
    publish = models.ForeignKey(
        to=Publish,
        db_constraint=False,
        related_name=books,
        on_delete=models.DO_NOTHING
    )
    authors = models.ManyToManyField(
        to=Author,
        db_constraint=False,
        related_name=books
    )

    @property
    def publish_name(self):
        return self.publish.name


    @property
    def author_list(self):
        return self.authors.values(name, age, mobile = F(detail__mobile)).all()

    class Meta:
        db_table = book
        verbose_name_plural = 书籍

    def __str__(self):
        return self.name


class Publish(BaseModel):
    name = models.CharField(max_length=64)
    address = models.CharField(max_length=64)

    class Meta:
        db_table = publish
        verbose_name_plural = 出版社

    def __str__(self):
        return self.name


class Author(BaseModel):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    class Meta:
        db_table = author
        verbose_name_plural = 作者

    def __str__(self):
        return self.name


class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    # 外键字段建在作者详情中: 因为不经常被查询,建在作者那是被经常查,走数据库
    author = models.OneToOneField(
        # 和那张表建立连接, 也可以不写
        to=Author,
        # 与连接的表断开连接
        db_constraint=False,
        # 通过detail进行连表查询
        related_name= detail,   # 正向按字段, 反向查询按detail
        # 级联
        on_delete= models.CASCADE,

        # on_delete= models.DO_NOTHING,

        # null=True,
        # on_delete=models.SET_NULL,

        # default= 0,
        # on_delete=models.SET_DEFAULT
    )

    class Meta:
        db_table = author_detail
        verbose_name_plural = 作者详情

    def __str__(self):
        return %s的详情 % self.author.name
View Code

   数据库迁移命令:

python manage.py makemigrations  # 数据库迁移记录
python manage.py migrate   # 创建数据库

# 创建管理有用户, 对创建的表进行操作
createsuperman

  admin.py

技术图片
from django.contrib import admin
from . import models

# Register your models here.
# admin.site.register(models.Books)
# admin.site.register(models.User)

# 只有在admin.py中注册之后, 才能在浏览器的admin的身份尽心操作
admin.site.register(models.Book)
admin.site.register(models.Author)
admin.site.register(models.Publish)
admin.site.register(models.AuthorDetail)
View Code

  views.py

技术图片
from rest_framework.views import APIView
from . import models
from rest_framework.response import Response
from . import serializers


class Book(APIView):
    # 单查 全查
    def get(self, request, *args, **kwargs):
        pk = kwargs.get(pk)
        # 单查
        if pk:
            try:
                # 查找没有被删除的书籍 is_delete = False
                book_obj = models.Book.objects.get(pk=pk, is_delete=False)
                book_ser = serializers.BookModelSerializer(book_obj)
            except:
                return Response({
                    status: 1,
                    msg: 数据不存在
                })
        # 群查
        else:
            # 查找没有被删除的书籍 is_delete = False
            book_obj = models.Book.objects.filter(is_delete=False).all()
            book_ser = serializers.BookModelSerializer(book_obj, many=True)
        return Response({
            status: 0,
            msg: ok,
            results: book_ser.data
        })
    # 单增 群增
    def post(self, request, *args, **kwargs):
        # 前台出过来的数据在request.data里面
        request_data = request.data
        # 单增
        if isinstance(request_data, dict):
            many = False
        # 群增
        elif isinstance(request_data, list):
            many = True
        # 发送的数据格式不对
        else:
            return Response({
                status: 1,
                msg: 传入的数据有误, 只能传{},or [{},{}]
            })
        # 数据交给序列化校验                      # 点data查看many
        book_ser = serializers.BookModelSerializer(data=request_data, many=many)
        # 当校验失败, raise_exception=True马上终止当前视图方法, 报异常返回给前台
        book_ser.is_valid(raise_exception=True)
        # 校验成功保存数据
        book_obj = book_ser.save()
        return Response({
            status: 0,
            msg: OK,
            results: serializers.BookModelSerializer(book_obj, many=many).data
        })

    # 单删 群删
    def delete(self, request, *args, **kwargs):
        # print(kwargs.get(‘pk‘),66666666666666)
        # print(request.data.get(‘pks‘),7777777777)
        pk = kwargs.get(pk)  # 一个pk走的是在url路径中拼接
        if pk:
            pks = [pk]
        else:
            pks = request.data.get(pks)  # 多个pk的数据在request.data中
        # 只能删除一次, 不是真正意义上的删除,只是把is_delete=True, 所以用的是update方法
        if models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True):
            return Response({
                status: 0,
                msg: ok
            })
        return Response({
            status: 1,
            msg0: 已经被删除过
        })
View Code

六: postman接口工具

  安装: 官网直接下载, 点击安装即可

  1) get请求: 携带参数采用Params

    数据在request.query_params中

  2) post请求:  提交数据的三种方式: form_data, urlencode, json

    数据在request.data中

  注意: 所有的请求都可以携带请求头

七: drf的请求生命周期

  1)  路由匹配走APIView的as_view函数

  2)  在as_view函数中调用父类的as_view(原生django的as_view), 增加了禁用csrf认证

  3)  在父类的as_view中dispatch方法请求走的是APIView的dispatch

  5)  完成任务方法交给视图类处理, 得到请求的响应结果, 返回给前台

八: 视图家族

  1.  views.py 视图>>APIView: 主要是设置

    1).  drf提供的渲染类, 解析模块规定数据的格式(form_data, urlencoded, json), 异常模块的配置

REST_FRAMEWORK = {
    # drf提供的渲染类
    DEFAULT_RENDERER_CLASSES: [
        rest_framework.renderers.JSONRenderer,
        rest_framework.renderers.BrowsableAPIRenderer,
    ],
    # 解析模块  数据格式
    # as_view>dispatch>self.initialize_request>self.get_parsers()>parser_classes
    DEFAULT_PARSER_CLASSES: [
        rest_framework.parsers.JSONParser,
        rest_framework.parsers.FormParser,
        rest_framework.parsers.MultiPartParser
    ],
    # 全局配置异常模块
    EXCEPTION_HANDLER: app01.exception.exception_handler,
}

    2) urls.py

urlpatterns = [
    # 不能缺少book/  "/",  在postman中的路径必须有"/"
    url(r^v1/books/$, views.Book.as_view()),
    url(r^v1/books/(?P<pk>.*)/$, views.Book.as_view()),
]

    3) views.py


from rest_framework.views import APIView
from . import models
from rest_framework.response import Response
from . import serializers

class
Book(APIView): # 单查 全查 def get(self, request, *args, **kwargs): pk = kwargs.get(pk) # 单查 if pk: try: # 查找没有被删除的书籍 is_delete = False book_obj = models.Book.objects.get(pk=pk, is_delete=False) book_ser = serializers.BookModelSerializer(book_obj) except: return Response({ status: 1, msg: 数据不存在 }) # 群查 else: # 查找没有被删除的书籍 is_delete = False book_obj = models.Book.objects.filter(is_delete=False).all() book_ser = serializers.BookModelSerializer(book_obj, many=True) return Response({ status: 0, msg: ok, results: book_ser.data }) # 单增 群增 def post(self, request, *args, **kwargs): # 前台出过来的数据在request.data里面 request_data = request.data # 单增 if isinstance(request_data, dict): many = False # 群增 elif isinstance(request_data, list): many = True # 发送的数据格式不对 else: return Response({ status: 1, msg: 传入的数据有误, 只能传{},or [{},{}] }) # 数据交给序列化校验 # 点data查看many book_ser = serializers.BookModelSerializer(data=request_data, many=many) # 当校验失败, raise_exception=True马上终止当前视图方法, 报异常返回给前台 book_ser.is_valid(raise_exception=True) # 校验成功保存数据 book_obj = book_ser.save() return Response({ status: 0, msg: OK, results: serializers.BookModelSerializer(book_obj, many=many).data }) # 单删 群删 def delete(self, request, *args, **kwargs): # print(kwargs.get(‘pk‘),66666666666666) # print(request.data.get(‘pks‘),7777777777) pk = kwargs.get(pk) # 一个pk走的是在url路径中拼接 if pk: pks = [pk] else: pks = request.data.get(pks) # 多个pk的数据在request.data中 # 只能删除一次, 不是真正意义上的删除,只是把is_delete=True, 所以用的是update方法 if models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True): return Response({ status: 0, msg: ok }) return Response({ status: 1, msg0: 已经被删除过 }) # 单整体修改, 对books(pk)传入的数据与models模型类中的字段对应, 是字典, 字段为选填 def put(self, request, *args, **kwargs): # 根据pk获取要修改的对象 pk = kwargs.get(pk) old_book_obj = models.Book.objects.filter(pk=pk).first() # 获取要修改的数据 request_data = request.data # 把数据传入序列化,进行校验, partial=默认False表示, 所有字段必填, partial=True: 表示字段为选填 book_ser = serializers.BookModelSerializer(instance=old_book_obj, data=request_data, partial=True) # 判断校验的数据是否有误, raise_exception=True表示校验的数据有误, 直接返回 book_ser.is_valid(raise_exception=True) # 校验完成的数据进行更新update, 操作数据库 book_obj = book_ser.save() # 返回前段请求响应的数据 return Response({ status: 0, msg: 更新完成, results: serializers.BookModelSerializer(book_obj).data }) # 坑: "detail": "Unsupported media type "text/plain" in request." # 原因: 传入的数据没有设置成json格式 # 局部修改,对books(pk)传入的数据是与models的模型类对应的字典啊 # 群改和单改何为一个方式, 其中字段为可选字段, partial=True # 规定前段出入的数据格式: [{},{},{}] 或者 {} def patch(self, request, *args, **kwargs): # 获取数据 request_data = request.data # 获取要修改的对象, 先获取id pk = kwargs.get(pk) # 首先判断数据的格式: 字典还是list # 进步思想: 把单改变为群改, 就是把pk变为多个, 把字典变为多个就是列表 # 单改 if pk and isinstance(request_data, dict): pks = [pk,] request_data = [request_data,] # 群改 elif not pk and isinstance(request_data, list): # [{"pk": 1, "name": "json"},{"pk": 3, "price": 99.8},{"pk": 5, "author": 2}] # 取的一个个id, id在字典里面 pks = [] for dic in request_data: # 应该把pk从字典中取出, # pop()取pk的时候,如果pk不存在, 报错, 所以设置默认值None pk = dic.pop(pk, None) # 保证字典中的是数据, 必须有一个字段(除pk外) if dic: # 判断pk是fou存在 if pk: pks.append(pk) else: # 保证了每个字典都有pk # 感觉有问题, 如果列表中第一个字典pk值为空, 其余字典的pk有值, 结束程序的运行 return Response({ status: 1, msg: 传入的数据有误 }) else: return Response({ status: 1, msg: 传入的数据有误 }) # 上面只保证了,pk必须有值,没有保证其他字段必须有一个值 # 前段发送的数据有误 else: return Response({ status: 1, msg: 传入的数据有误 }) # pks中的数据进行筛选, 把没有pk的request_data中的数据去除 # 取出每个pk进行判断, 判断该pk的数据是否存在, # 定义一个对象列表,存放pk数据存在的 book_objs = [] # 定义一个列表存放, pk对应的数据存在的, 其余数据 new_request_data = [] for index, pk in enumerate(pks): # 从数据库中查, 判断是否有该pk对应的数据 try: # 该pk对应的数据是否存在, 是否被删除 old_obj = models.Book.objects.get(pk=pk, is_delete=False) book_objs.append(old_obj) # 对应索引的数据就需要保存下来 new_request_data.append(request_data[index]) except: # 重点: 反面教材,如pk对应的数据有误,将对应索引的中request_data中的数据移除 # index = pks.index(pk) # request_data.pop(index) continue # 遇到不符合要求的数据,继续进行for循环, 直到循环结束为止 # context = {‘request‘: request} book_ser = serializers.BookModelSerializer(instance=book_objs, data=new_request_data, many=True, partial=True) book_ser.is_valid(raise_exception=True) book_obj = book_ser.save() # 不能实现群改, 需要重写update方法, 在serializers.py文件中书写 return Response({ status: 0, msg: 修改成功, results: serializers.BookModelSerializer(book_obj, many=True).data })

  2. generics.py工具视图>> GenericAPIView

    1)  完全继承APIView

    2) 继承之后的操作

      get_queryset(): 从类属性queryset中获取model的queryset数据

      get_object(): 从类属性queryset中获取model的queryset数据, 在通过有名分组pk确定唯一对象

      get_serializer(): 从类属性serializer_class中获取的serializer的序列化类

    3) urls.py

urlpatterns = [

    url(r^v2/books/$, views.BooKGenericAPIView.as_view()),
    url(r^v2/books/(?P<pk>.*)/$, views.BooKGenericAPIView.as_view()),
]

    4) views.py

 

from rest_framework.generics import GenericAPIView
from . import serializers
from . import models
from utils.response import APIResponse

# 见名知导入的文件
class BooKGenericAPIView(GenericAPIView):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer
    # 自定义有名分组的名字
    lookup_field = pk  # 默认
    # 群取
    def get(self, request, *args, **kwargs):
        book_query = self.get_queryset()
        book_ser = self.get_serializer(book_query, many=True)
        book_data = book_ser.data
        return APIResponse(results=book_data)

    # 单增
    # def get(self, request, *args, **kwargs):
    #     book_obj = self.get_object()
    #     book_ser = self.get_serializer(book_obj)
    #     book_data = book_ser.data
    #     return APIResponse(results=book_data)

 

  3. mixins.py

    1) mixins视图工具集

"""
mixins视图工具集: 用来辅助GenericAPIView
    1) mixins有五个工具类文件, 一共提供了五个工具类, 六个工具方法: 
          单增, 单删, 群查, 单查, 单整体改, 单局部改
    2) 继承工具类可以简化请求函数的实现体, 但是必须继承GenericAPIView,
        需要GenericAPIView类提供的几个类属性和方法
    3) 工具类的工具方法返回值都是Response类型对象,如果要格式化数据,返回给前台,
        可以通过request.data 拿到工具方法返回的Response类型的响应数据
"""

    2) urls.py

urlpatterns = [

    url(r^v3/books/$, views.BookMixinsGenericAPIView.as_view()),
    url(r^v3/books/(?P<pk>.*)/$, views.BookMixinsGenericAPIView.as_view()),
]

    3) views.py

from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin
from . import serializers
from . import models
from utils.response import APIResponse


class BookMixinsGenericAPIView(GenericAPIView, ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer
    lookup_field = pk
    # 坑: TypeError: Object of type ‘Response‘ is not JSON serializable
    # 解决: response.data
    def get(self, request, *args, **kwargs):
        # 单查, 群查
        if "pk" in kwargs:
            response = self.retrieve(request, *args, **kwargs)
        else:
            response = self.list(request, *args, **kwargs)
        return APIResponse(results=response.data)

    def post(self, request, *args, **kwargs):
        response = self.create(request, *args, **kwargs)
        return APIResponse(results=response.data)

    def put(self, request, *args, **kwargs):
        response = self.update(request, *args, **kwargs)
        return APIResponse(results=response.data)

    def patch(self, request, *args, **kwargs):
        response = self.partial_update(request, *args, **kwargs)
        return APIResponse(results=response.data)

  4. viewsets.py视图集

    1)  

    2) urls.py

urlpatterns = [

    url(r^v6/books/$, views.BookModelViewSet.as_view({get: list, post: create})),  # post 单增
    url(r^v6/books/(?P<pk>.*)/$, views.BookModelViewSet.as_view({
        get: retrieve, put: update, patch: partial_update, delete: destroy
    })),
]

    3) views.py

from rest_framework.viewsets import ModelViewSet
class BookModelViewSet(ModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object() # type  models.Book对象
        # 传入的参数不对
        if not instance:
            return APIResponse(1, 删除失败)  # 实际操作在此之前就完成了
        instance.is_delete = True
        instance.save()
        return APIResponse(0, 删除成功)

    """ 群增, 群删, 群局部改, 群整体改???????????"""

 

 

以上是关于drf框架的主要内容,如果未能解决你的问题,请参考以下文章

DRF框架serializer之视图优化

DRF框架之mixins

DRF框架serializer之ModelSerializer

067.Python框架Django之DRF视图类

DRF框架生成接口文档

DRF框架之Serializer序列化器的序列化操作