############### django框架-rest framework ###############
# django rest framework 框架 # 为什么学习这个rest framework? # 1,写代码的时候会用到,前后端分离的项目,可以帮助我们快速的写api, # 2,面试的时候会问到, # 这个框架提供了一些什么功能? # 1,权限组件, # 2,认证组件 # 3,访问频率限制组件, # 4,序列化,这个序列化是干什么的?把字符串序列化为json返回, # 5,路由 # 6,视图,你写的rest framework继承过哪些类?这个学过了就知道了 # 7,分页 # 8,解析器,解析器干什么的?parse,vue发过来的请求,我要解析,根据头不同解析成不同的结果, # 9,渲染器,渲染页面的, # 10,版本, # 这些就是序列化是用的最多的,分页也用的多,其他的比如权限这样的写一次就很少改动了, # django rest framework安装: # 在pycharm中的terminal中执行命令: # pip install djangorestframework
# 这是一个app,注意app的注册在settings.py中
############### restful协议 ###############
# restful协议 # REST与技术无关,代表的是一种软件架构风格 # 所有的数据,不管是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性 # 1,HTTPs协议: # API与用户的通信协议,总是使用HTTPs协议。https收费,但是更加的安全 # 2,域名:唯一的目的就是为了能一眼看到就是接口,可以不加,但是这是规范, # 第一种:子域名方式 # https://api.example.com,这一种会存在跨域,浏览器有同源策略, # 第二种:url方式, # https://example.org/api/,这种便宜, # 3,版本: # URL,如:https://api.example.com/v1/ # https://example.org/api/v1/ # 4,路径:面向资源编程,写url的时候推荐使用名词, # https://example.org/api/v1/名词/ # https://example.org/api/v1/order/ # 5,method, # GET:获取资源(一项或多项) # POST:新建一个资源 # PUT:更新资源(全部更新) # PATCH:更新资源(局部更新) # DELETE:删除资源 # 6,过滤,在url加条件,通过?号 # https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数 # 7,状态码+code:常用的状态码 # 200 OK - [GET]:服务器成功返回用户请求的数据 # 200系列表示成功 # 300系列表示重定向 # 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作 # 400系列是客户端错误 # 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 # 500系列是服务端错误, # api需要返回状态码,但是我们通常使用code来做,因为只有状态码是不够用的,比如支付宝的状态码,可以参考看看, # 所以需要状态码和code结合, # 8,错误信息: # api一定要返回错误信息, # 9,返回请求的结果, # 针对不同的操作,返回不同的结果, # GET /order/ 返回所有的订单 # GET /order/1/ 返回单个订单 # POST /order/ 创建订单,如果前端需要,就返回创建的订单信息,不需要就不返回 # PUT /order/1/ 修改订单,全部 # PATCH /order/1/ 修改订单,局部 # DELETE /order/1/ 删除订单 # 10,Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么 # 上面就是所有的规范,但是我们可以不遵循,需要根据业务情况来选择是否遵循,
通过一个小项目来研究
############### models.py ###############
from django.db import models class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class User(models.Model): name=models.CharField(max_length=32) pwd=models.CharField(max_length=32) type_choices=((1,"普通"),(2,"VIP"),(3,"SVIP")) user_type=models.IntegerField(choices=type_choices,default=1) class Token(models.Model): user=models.OneToOneField("User") token=models.CharField(max_length=128) def __str__(self): return self.token
############### serializers.py ###############
from rest_framework import serializers from api.models import * class PublishModelSerializers(serializers.ModelSerializer): class Meta(): model=Publish fields='__all__' class AuthorModelSerializers(serializers.ModelSerializer): class Meta(): model=Author fields='__all__' class UserModelSerializers(serializers.ModelSerializer): class Meta(): model=User fields='__all__' class BookModelSerializers(serializers.ModelSerializer): class Meta(): model=Book fields='__all__'
############### views.py ###############
from django.shortcuts import render from api.serializer import PublishModelSerializers,AuthorModelSerializers,UserModelSerializers,BookModelSerializers from rest_framework.views import APIView from rest_framework.response import Response from api import models from api.utils import TokenAuth,SVIPPermission,VisitRateThrottle,MyPageNumberPagination class Publisher(APIView): authentication_classes = [TokenAuth] permission_classes = [SVIPPermission] def get(self,request): publisher_all = models.Publish.objects.all() print(publisher_all) ps = PublishModelSerializers(publisher_all,many=True) return Response(ps.data) def post(self,request): ps = PublishModelSerializers(data=request.data) if ps.is_valid(): ps.save() return Response(ps.data) else: return Response(ps.errors) class PublisherDetail(APIView): def get(self,request,pk): publish = models.Publish.objects.filter(pk=pk).first() ps = PublishModelSerializers(publish) return Response(ps.data) def put(self,request,pk): publish = models.Publish.objects.filter(pk=pk).first() ps = PublishModelSerializers(publish,data=request.data) if ps.is_valid(): ps.save() return Response(ps.data) else: return Response(ps.errors) def delete(self,request,pk): models.Publish.objects.filter(pk=pk).delete() return Response() def get_random_str(user): import hashlib,time create_time =str(time.time()) md5=hashlib.md5(bytes(user,encoding='utf8')) md5.update(bytes(create_time,encoding='utf8')) return md5.hexdigest() class Login(APIView): authentication_classes = [] def post(self,request): name=request.data.get('name') pwd = request.data.get('pwd') print(request.data) user= models.User.objects.filter(name=name,pwd=pwd).first() print(user) print(user.name) ret ={'status_code':100,'msg':None} if user: random_str = get_random_str(user.name) print(random_str) token=models.Token.objects.update_or_create(user=user,defaults={'token':random_str}) ret['token']=str(token) else: ret['status_code']=1001 ret['mag']='用户名或密码错误' return Response(ret) from rest_framework import mixins from rest_framework import generics from rest_framework import viewsets # 第一种视图,使用mixins class Author(mixins.ListModelMixin,mixins.CreateModelMixin,generics.GenericAPIView): authentication_classes = [] queryset = models.Author.objects.all() serializer_class = AuthorModelSerializers def get(self,request,*args,**kwargs): return self.list(request,*args,**kwargs) def post(self,request,*args,**kwargs): return self.create(request,*args,**kwargs) class AuthorDetail(mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,generics.GenericAPIView): authentication_classes = [] queryset = models.Author.objects.all() serializer_class = AuthorModelSerializers def get(self,request,*args,**kwargs): return self.retrieve(request,*args,**kwargs) def put(self,request,*args,**kwargs): return self.update(request,*args,**kwargs) def delete(self,request,*args,**kwargs): return self.destroy(request,*args,**kwargs) # 第二种视图:使用通用的基于类的视图 class User(generics.ListCreateAPIView,generics.GenericAPIView): authentication_classes = [] queryset = models.User.objects.all() serializer_class = UserModelSerializers class UserDetail(generics.RetrieveUpdateDestroyAPIView,generics.GenericAPIView): queryset = models.User.objects.all() serializer_class = UserModelSerializers # 第三种视图:把两个视图类合并成一个, 那就需要改动url了,因为url现在还是两个, class Book(viewsets.ModelViewSet): # throttle_classes = [VisitRateThrottle] queryset = models.Book.objects.all() serializer_class = BookModelSerializers pagination_class = MyPageNumberPagination
############### urls.py ###############
from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/(?P\w+)/ ', include("api.urls")), ]
from django.conf.urls import url,include from django.contrib import admin from api import views # 但是现在有一个问题,以后是不是只要多一个表,就需要两个url了? # 这样有十张表就要写十次了,所以你封装了视图类了,你也需要封装一下路由, # 怎么封装? from rest_framework import routers routers=routers.DefaultRouter() routers.register("book",views.Book) # 再有一张表,就只需要注册就可以了, # 这样再加这样一句:routers.register("authors",views.AuthorModelView) urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^publish/$',views.Publisher.as_view()), url(r'^publish/(?P\d+)/$ ', views.PublisherDetail.as_view()), url(r'^login/$', views.Login.as_view()), url(r'^author/$', views.Author.as_view()), url(r'^author/(?P\d+)/$ ', views.AuthorDetail.as_view()), url(r'^user/$', views.User.as_view()), url(r'^user/(?P\d+)/$ ', views.UserDetail.as_view()), url(r'', include(routers.urls)), # url(r'^book/$', views.Book.as_view({"get":"list",'post':"create"})), # url(r'^book/(?P\d+)/$', views.Book.as_view({"get":"retrieve","put":"update","delete":"destroy"})), ]
############### settings.py ###############
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["api.utils.TokenAuth"], "DEFAULT_PERMISSION_CLASSES": ["api.utils.SVIPPermission"], 'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本 'VERSION_PARAM': 'version', # 参数 'DEFAULT_VERSION': 'v1', # 默认版本 }
############### 解析器 ###############
# 解析器----数据解析器, # # 前端发送了json数据,在request的body里面, # 我们需要把json数据解析成字典,这样每次都要解析,所以可以创建一个中间件,只要是json就解析,然后在视图里面就可以直接使用了, # 所以什么是解析器?就是把数据转换成我们需要的格式, # 原生的django不支持对json的解析, # 但是我们的rest framework支持,没有进行任何的处理,就直接给你在request.data里面返回了字典了, # from rest_framework.parsers import JSONParser # 这是rest framework自带的json解析器, # 你在视图类中,加入一句话:parser_classes = [JSONParser] # 加上这一句,这个视图类就只能解析json数据了,默认能解析三种,json,form,multi,
############### 频率组件 ###############
# 下面开始频率组件: # # 定义一个类,访问ip一分钟不能超过20次, # 加上一句话, # 怎么知道你是浏览器访问还是收集访问,就是请求头里面有一个user_agent, # print(request.META.get("REMOTE_ADDR")),这就是访问ip # 每个公司的业务需求都是不一样的,都需要在组件的基础上进行修改, # 所以还需要理解源码的逻辑原理, # 频率组件就这些东西,
############### 分页组件 ###############
# 分页组件 # # django也有分页,rest framework也有分页,但是没有页面这个概念了, # 这个分页是返回给前端数据太多的时候,需要分页返回, # http://127.0.0.1:8000/books/?page=1, # 我们需要把继承这个分页类,然后重写一些方法, # class MyPageNumberPagination(PageNumberPagination): # page_size =2 # page_query_param = "page" # page_size_query_param = "size" # ##################################### # http://127.0.0.1:8000/books/?page=1&size=1 # 默认显示两个,但是可以自己指定显示几个, # ######################################### # http://127.0.0.1:8000/books/?limit=3 # http://127.0.0.1:8000/books/?limit=3&offset=1,这是偏移, # ############################################# # pagination_class = MyPageNumberPagination # 这视图里面加上这一句,就会去找我们定义的视图类了, # 所以分页器的使用,就是定义一个分页的类,然后加一句,就可以了,
############### 响应器 ###############
# 响应器: response, # 之前使用的都是httpResponse,这是原生的,里面只能使用字符串, # from rest_framework.response import Response # 这是rest framework中的Response, # 他的返回使用浏览器是有样式的,是有页面的,数据是有格式的,很漂亮,rest framework怕你访问不方便,所以有了一个页面,是Response自带的, # 浏览器访问和postman访问一样吗? # 是不一样的,如果是浏览器,认为是一个用户,是一个页面, # postman访问,只有数据,没有页面, # 建议一开始不要使用这个界面,比较乱,就使用postman就可以了, # postman的安装和使用 # Postman一款非常流行的API调试工具。
############### 版本 ###############
# # 版本的问题: # rest_framework.versioning.URLPathVersioning # 一般就是这种,http://127.0.0.1:8000/api/v2/course/ # 只要配置好了,以后就很少动了, # 原理要知道 # 使用: # 1,添加配置, # 2,设置路由, # 3,获取版本,
############### 结束线 ###############
############### 结束线 ###############
############### 结束线 ###############