一: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.nameView 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, ‘删除成功‘) """ 群增, 群删, 群局部改, 群整体改???????????"""