serializers 序列化
20 个模块?
BrowsableAPIRenderer
https://www.django-rest-framework.org/topics/browsable-api/#the-browsable-api
https://www.imooc.com/video/22363
$ python manage.py createsuperuser
$ python manage.py makemigrations $ python manage.py migrate
os.pardir ❓
os.pathdir ❓
views 返回 HTML,全栈开发, PHP / JAVA
$ python3 manage.py runserver 0.0.0.0:8080
Restful API 规范
RESTFUL 最佳实践维度:
python manage.py migrate
python manage.py makemigrations
here, https://github.com/liaogx/drf-tutorial
http://github.com/liaogx/drf-tutorial
DRF提供了几种permission level:
如何在API level提供非全局的权限控制?
类view:
然而上面还是不够的,不能提供基于业务的精细化的权限控制。比如老师只能修改自己的课程信息,不能修改不是自己的课程的信息。
本节后半段提供的是如何基于业务需要,自定义权限。从6:03开始的内容开始看
Token Authentication
需要在setting的installed 里添加'rest_framework.authtoken',
这节就看看就好了。有些细节我觉得要看源码才能懂。而且正常也是用jwt,不用这个了。
通过这种方式,认证成功的话,request.user 还是django的user,
request.auth就变成了rest_framework.authtoken.models.Token
如果认证失败,resp的header会多一个www-authenticate的field,成功是没这个fIeld的
如何对不同的view提供不同的认证方式?而不是所有api的view都用同一个认证方式呢?
function view:
用@authentication_classes()
class view/viewset:
用 class property
authentication_classses = (a,,b,c)
auth: 登录的用户能访问哪些资源,API
authen: 对登录中的用户身份进行认证
认证机制依赖于django.contrib.auth
Django会按上面的列表顺序逐个尝试,并使用第一个成功通过的认证
Basic: 密码认证。不建议用于Producrion。前端要用户输入密码后,会在header里加一个authorizaion field, 内容是一个bas64 加密的hash value 但是base 64可以被解密。所以不安全。
使用Basic认证的话,如果通过了。会把下面的self.request.user设置成 contrib.auth.models.User
self.request.auth 设置为none
如何认证失败。会在response的header里多一个 WWW-authenticate的field 这个就是个规律。方便你debug
Session认证:
如果是post,put, 需要提供CSRF token。
认证通过的话,如果通过了。会把下面的self.request.user设置成 contrib.auth.models.User
self.request.auth 设置为none。
认证失败的话会响应403 forbidden,但是response的header里不会多额外信息
通用类视图简化了自己的代码。但是我觉得会让人觉得不清晰。因为hide details.
这节谈了到底该如何选择view的写法,func, class, or generics, viewsets
还有下面这些内容没有讲:
DRF的运行机制,限流,缓存
这节回头再来看看。就是讲解了怎么用那个api doc
本文demo了Postman里怎么用各种author。介绍了api key的形式和输入密码的形式。
还demo了collection的含义,查看History, 如何保存某个request,如何添加request 到某个collections
collections右键里可以设置这个collections的全局的authorization信息。这样就不用一个个copy token
drf的序列化器和django自带的有什么不同呢?
drf自带的缺点是:
缺乏前端json数据的验证处理
验证器的参数
同时序列化多个对象
序列化过程中添加上下文,比如添加queryset中没有的数据到最后想返回的json里
没有对无效数据的异常处理
本节内容
Django原生中是function base view 和class view2种。之前ins项目有用到。本节也有代码复习演示
这节课pycharm直接创建的方式我可能是社区版,没有这个功能。
anyway, demo里的首次migration我也做了一次。不然无法创建admin user for login to admin portal.
put,更新。指的是更新完整的一条DB记录。比如有个表有5个columns,你put请求要包含完整的5个columns的值。
patch部分更新,比如只是update username
这里调用gcbv/list/会报错:
views.py文件内容:
from django.http.response import HttpResponse from rest_framework.decorators import api_view from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Course from .serializers import CourseSerializer from rest_framework import generics """一、函数式编程""" @api_view(["GET","POST"]) def course_list(request): """ 获取所有课程信息或新增一个课程 """ if request.method == "GET": s = CourseSerializer(instance=Course.objects.all(),many=True) return Response(data=s.data, status=status.HTTP_200_OK) elif request.method == "POST": s = CourseSerializer(data=request.data, partial=True) if s.is_valid(): s.save(teacher=request.user) return Response(data=s.data, status=status.HTTP_201_CREATED) return Response(s.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(["GET","PUT","DELETE"]) def course_detail(request,pk): """ 获取、删除更新一个课程 """ try: course = Course.objects.get(pk=pk) except Course.DoesNotExist: return Response(data={"msg": "没有此课程信息"},status=status.HTTP_404_NOT_FOUND) else: if request.method == "GET": s = CourseSerializer(instance=course) return Response(data=s.data, status=status.HTTP_200_OK) elif request.method == "PUT": # 这里的instance的只是查出来的course,而data则是request传来的值 s = CourseSerializer(instance=course, data=request.data) if s.is_valid(): s.save() return Response(s.data,status=status.HTTP_200_OK) elif request.method == "DELETE": course.delete() return Response(status=status.HTTP_204_NO_CONTENT) """类试图 Class Based Views""" class CourseList(APIView): def get(self, request): queryset = Course.objects.all() s = CourseSerializer(instance=queryset, many=True) # instance = xxx 是查询集 return Response(s.data, status=status.HTTP_200_OK) def post(self,request): s = CourseSerializer(data=request.data) # data是传递过来的数据,return前要先调用is_valid()方法验证数据 if s.is_valid(): s.save(teacher=self.request.user) return Response(data=s.data, status=status.HTTP_201_CREATED) return Response(s.errors, status.HTTP_400_BAD_REQUEST) class CourseDetail(APIView): @staticmethod def get_object(pk): try: return Course.objects.get(pk=pk) except Course.DoesNotExist: # 返回none return def get(self,request,pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND) s = CourseSerializer(instance=obj) return Response(s.data, status=status.HTTP_200_OK) def put(self,request,pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND) s = CourseSerializer(instance=obj, data=request.data) if s.is_valid(): s.save() return Response(data=s.data, status=status.HTTP_201_CREATED) return Response(data=s.data,status=status.HTTP_400_BAD_REQUEST) def delete(self,request,pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND) obj.delete() return Response(status=status.HTTP_204_NO_CONTENT) """三、通用类试图 Generic Class Based View""" class GCourseList(generics.ListCreateAPIView): queryset = Course.objects.all() serializer_class = CourseSerializer def perform_create(self, serializer): serializer.save(teacher=self.request.user) class GCourseDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Course.objects.all() serializer_class = CourseSerializer
course的views.py
from django.http.response import HttpResponse from rest_framework.decorators import api_view from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Course from .serializers import CourseSerializer """一、函数式编程""" @api_view(["GET","POST"]) def course_list(request): """ 获取所有课程信息或新增一个课程 """ if request.method == "GET": s = CourseSerializer(instance=Course.objects.all(),many=True) return Response(data=s.data, status=status.HTTP_200_OK) elif request.method == "POST": s = CourseSerializer(data=request.data, partial=True) if s.is_valid(): s.save(teacher=request.user) return Response(data=s.data, status=status.HTTP_201_CREATED) return Response(s.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(["GET","PUT","DELETE"]) def course_detail(request,pk): """ 获取、删除更新一个课程 """ try: course = Course.objects.get(pk=pk) except Course.DoesNotExist: return Response(data={"msg": "没有此课程信息"},status=status.HTTP_404_NOT_FOUND) else: if request.method == "GET": s = CourseSerializer(instance=course) return Response(data=s.data, status=status.HTTP_200_OK) elif request.method == "PUT": # 这里的instance的只是查出来的course,而data则是request传来的值 s = CourseSerializer(instance=course, data=request.data) if s.is_valid(): s.save() return Response(s.data,status=status.HTTP_200_OK) elif request.method == "DELETE": course.delete() return Response(status=status.HTTP_204_NO_CONTENT) """类试图 Class Based Views""" class CourseList(APIView): def get(self, request): queryset = Course.objects.all() s = CourseSerializer(instance=queryset, many=True) # instance = xxx 是查询集 return Response(s.data, status=status.HTTP_200_OK) def post(self,request): s = CourseSerializer(data=request.data) # data是传递过来的数据,return前要先调用is_valid()方法验证数据 if s.is_valid(): s.save(teacher=self.request.user) return Response(data=s.data, status=status.HTTP_201_CREATED) return Response(s.errors, status.HTTP_400_BAD_REQUEST) class CourseDetail(APIView): @staticmethod def get_object(pk): try: return Course.objects.get(pk=pk) except Course.DoesNotExist: # 返回none return def get(self,request,pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND) s = CourseSerializer(instance=obj) return Response(s.data, status=status.HTTP_200_OK) def put(self,request,pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND) s = CourseSerializer(instance=obj, data=request.data) if s.is_valid(): s.save() return Response(data=s.data, status=status.HTTP_201_CREATED) return Response(data=s.data,status=status.HTTP_400_BAD_REQUEST) def delete(self,request,pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND) obj.delete() return Response(status=status.HTTP_204_NO_CONTENT)
添加 course下的路由:
from django.urls import path, include
from course import views
urlpatterns = [ # Function Based View path("fbv/list/", views.course_list, name="fbv-list"), path("fbv/detail/<int:pk>/", views.course_detail, name="fbv-detail"), # Class Based View path("cbv/list/", views.CourseList.as_view(), name="cbv-list"), path("cbv/detail/<int:pk>/", views.CourseDetail.as_view(), name="cbv-detail") ]
工程下的urls.py
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('api-auth/', include('rest_framework.urls')), # DRF的登录退出 path('admin/', admin.site.urls), path('course/', include('course.urls')) ]
实例代码:course工程下views.py
from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from .models import Course from .serializers import CourseSerializer """一、函数式编程 Function Baseed View""" @api_view(["GET","POST"]) def course_list(request): """ 获取所有课程信息或新增一个课程 :param request: :return: """ if request.method == "GET": s = CourseSerializer(instance=Course.objects.all(), many=True) return Response(data=s.data, status=status.HTTP_200_OK) elif request.method == "POST": # partial=True 允许传部分字段过来不是全部,但是model要求必须传的,这个参数设置了也没用 # 只适用于非必填字段 s = CourseSerializer(data=request.data, partial=True) if s.is_valid(): s.save(teacher=request.user) return Response(data=s.data,status=status.HTTP_201_CREATED) return Response(s.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(["GET","PUT","DELETE"]) def course_detail(request, pk): """ 获取删除或更新一个课程 """ try: course = Course.objects.get(pk=pk) except Course.DoesNotExist: return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND) else: if request.method == "GET": s = CourseSerializer(instance=course) return Response(data=s.data, status=status.HTTP_200_OK) elif request.method == "PUT": s = CourseSerializer(instance=course,data=request.data) if s.is_valid(): s.save() return Response(s.data, status=status.HTTP_200_CREATED) elif request.method == "DELETE": course.delete() return Response(status=status.HTTP_204_NO_CONTENT)