django框架基础-rest framework-长期维护-20191216

###############   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,获取版本,

 

###############  结束线   ###############

 

 

###############  结束线   ###############

 

 

###############  结束线   ###############

你可能感兴趣的:(django框架基础-rest framework-长期维护-20191216)