drf(初始drf,restfull规范 ,源码)

发布时间 2023-09-20 13:24:22作者: coder雪山

一 web开发模式

# 前后端混合开发(前后端不分离):通过模版语法,在服务器上处理好html的内容(组合字符串),返回给浏览器一堆字符串(字符串封装到respons对象里),浏览器在渲染

# 前后端分离:只专注于写后端接口,返回json、xml格式
        # xml比json笨重



# 补充:什么是动态页面(需要查数据库的),什么是静态页面(内容写死的页面)
# 访问量大的home页面,进行静态页面优化

二 API接口

# 通过网络,规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介
Application Programming Interface

三 postman的使用

# postman是当前最好用的,模拟发送http请求的工具
#  解析json的网站
www.json.cn

# 请求头中User-Agent:客户端的类型
# 请求头中加其他参数
# url的末尾加/,浏览器中不加,是因为两次get,重定向自动给加上去的

四 restfull规范

'''
1. 什么是RESTFUll

    RESTfull 是目前最流行的 API 设计规范,用于 Web 数据接口的设计。

    REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”

    REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态

    所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
'''

十条规范

1 数据的安全保障
  • url链接一般都采用https协议进行传输

    注:采用https协议,可以提高数据交互过程中的安全性

抓包工具:fiddler,chales

2 接口特征表现
3 多数据版本共存

就比如手机app,有 些升级了,有些还没升级,没升级的,就是用的老版本

新升级的,用的新版本

4 数据即是资源,均使用名词(可复数)
5 资源操作由请求方式决定

6 过滤信息(filtering,或称查询参数)

  • 如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。下面是一些常见的参数。

https://api.baidu.com/v1/books?limit=10:指定返回记录的数量
https://api.baidu.com/v1/books?offset=10:指定返回记录的开始位置。
https://api.baidu.com/v1/books?page=2&per_page=100:指定第几页,以及每页的记录数。
https://api.baidu.com/v1/books?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
https://api.baidu.com/v1/books?animal_type_id=1:指定筛选条件
7.响应状态码

7.1 正常响应

  • 响应状态码2xx

    • 200:常规请求

    • 201:创建成功

7.2 重定向响应

  • 响应状态码3xx

    • 301:永久重定向

    • 302:暂时重定向

7.3 客户端异常

  • 响应状态码4xx

    • 403:请求无权限

    • 404:请求路径不存在

    • 405:请求方法不存在

7.4 服务器异常

  • 响应状态码5xx

    • 500:服务器异常

8 错误处理,应返回错误信息,error当成key

发生错误时不要响应200状态码,有一种不恰当的做法是,即使发生错误,也返回200状态码,把错误信息放在数据体里面,就像下面这样。

{
  "status": "failure",
  "data": {
    "error": "Expected at least two items in list."
  }
}
9 返回结果,针对不同操作,服务器向用户返回的结果应该符合一下规范
GET /collection:返回资源对象的列表(数组),一般是[{"id":1,"name":"a",},{"id":2,name:"b"},]这种类型
GET /collection/resource:返回单个资源对象, 一般是查看的单条数据 {"id":1,"name":'a'}
POST /collection:返回新生成的资源对象 , 一般是返回新添加的数据信息, 格式一般是{}
PUT /collection/resource:返回完整的资源对象  一般时返回更新后的数据,{}
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档  一般返回一个空字符串
10 Hypermedia API,提供链接

RESTful API最好做到Hypermedia,即返回结果中提供链接,API 的使用者未必知道,URL 是怎么设计的。

一个解决方法就是,在回应中,给出相关链接,便于下一步操作。

这样的话,用户只要记住一个 URL,就可以发现其他的 URL。

{
  ...
  "feeds_url": "https://api.github.com/feeds",
  "followers_url": "https://api.github.com/user/followers",
  "following_url": "https://api.github.com/user/following{/target}",
  "gists_url": "https://api.github.com/gists{/gist_id}",
  "hub_url": "https://api.github.com/hub",
  ...
}

五 drf的安装和简单使用

# 安装:pip install djangorestframework=3.10.3
# 使用:
1 在settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rest_framework'
]
2 在models.py中写表模型
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    author = models.CharField(max_length=32)
3 新建一个序列化类
app01下建一个ser.py
from rest_framework.serializers import ModelSerializer
from app01.models import Book


class BookModelSerializer(ModelSerializer):
     class Meta:
          model = Book
          fields = "__all__"
4 在视图函数中写视图类,views.py
from rest_framework.viewsets import ModelViewSet
from app01.models import Book
from app01.ser import BookModelSerializer


# Create your views here.
class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer
    
5 写路由关系,urls.py
from app01 import views
from rest_framework.routers import DefaultRouter

router = DefaultRouter()  # 可以处理视图的路由器
router.register('book', views.BookViewSet)  # 向路由中注册视图集

# 将路由中的所有路由信息追到django的路由列表中

urlpatterns = [
    path('admin/', admin.site.urls),
]

# 两个列表相加,就是将列表rooter.urls的值追加到urlpatterns,for循环,再.append
urlpatterns += router.urls

六 CBV源码

# ModelViewSet继承View(django原生View)
# APIView继承View

# 先读View
# views.py
from django.views import View
class Books(View):
    def get(self, request):
        return HttpResponse('ok')

# urls.py
path('books/', views.Books.as_view()),
'''
在这个地方应该写个函数的内存地址,views.Books.as_view()执行完,是个函数内存地址,Books继承了Views类,里面的as_view是类方法,所有由Books.as_view()
'''
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    '''
    因return view,view是as_view的闭包函数,Books.as_view()的调用,就是调用view,类的绑定方法,把类当对象传入。
    '''  
    @classonlymethod
    # 路由中,一般没有传参, **initkwargs没有值
    def as_view(cls, **initkwargs):
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)    # 实例化,book对象
            self.setup(request, *args, **kwargs)
            return self.dispatch(request, *args, **kwargs)
            # dispatch方法,先对象里找,没有去类中找,类中没有就去给类的父类找
        return view
    
    def setup(self, request, *args, **kwargs):
        if hasattr(self, 'get') and not hasattr(self, 'head'):
        self.head = self.get
        # 传入的request,赋值给对象
        self.request = request
        
    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            # 反射取值,得到get的内存地址
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
            # 内存地址加括号并传参,就是传参调用函数
        return handler(request, *args, **kwargs)

七 APIView源码

1 一切皆对象

函数也是对象

def foo(a,b):
    return a+b

foo.name = 'lq'    # 一切皆对象

print(foo(2,3))

print(foo.name)  # lq’

2 局部禁用csrf

# 在视图函数上加装饰器@csrf_exempt
# csrf_exempt(view)  这么写和在视图函数上加装饰器是一模一样

3 源码

# urls.py
path('booksapiview/', views.BooksAPIView.as_view())

# views.py
from rest_framework.views import APIView
class BooksAPIView(APIView):
    def get(self, request):    
        return HttpResponse('ok')
    
# APIView的as_view方法(类的绑定方法)
@classmethod
def as_view(cls, **initkwargs):
    # 调用父类,就是django的View类的as_view方法
    view = super().as_view(**initkwargs)
    # 把函数当对象,进行属性赋值
    view.cls = cls
    view.initkwargs = initkwargs
    # Note: session based authentication is explicitly CSRF validated,
    # 以后所有的请求,都没有csrf认证了,只要继承了APIView,就没有csrf的认证
    # all other authentication is CSRF exempt.
    # 就是给函数加装饰器@csrf_exempt
    return csrf_exempt(view)

# view = super().as_view(**initkwargs),--->django的View类中的as_view方法--->return self.dispatch(request, *args, **kwargs)--->回到APIView类的dispatch方法

# 类的方法调用,先从对象找--->产生对象的类中找--->父类--->父类的父类

def dispatch(self, request, *args, **kwargs):
    self.args = args
    self.kwargs = kwargs
    # 重新包装成一个request对象,以后再用的request对象,就是新的request对象了,initialize_request,是Request类的一个实例化的返回
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
        # 三大认证模块(request是新的request)
        self.initial(request, *args, **kwargs)

        # Get the appropriate handler method
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
            # 响应模块
            response = handler(request, *args, **kwargs)

    except Exception as exc:
        # 异常模块
        response = self.handle_exception(exc)
    # 渲染模块,根据客户端的不同(浏览器,postman),渲染不同的数据
    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response

Request类的源码

# from rest-framework.request import Request
# 只要继承了APIView,视图类中的request对象,都是新,也就是上面新的self.initialize_request(request, *args, **kwargs)
# 原生的request在新的request._request

class Request:
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        # 二次封装request,将原生request作为drf-request对象的_request属性
        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

# 以后使用request对象,就像使用之前的request是一模一样(因为重写了__getattr__方法,request.methed用法是一样的,不用request._request.methed,request.属性的时,自动触发__getattr__方法
def __getattr__(self, attr):
    try:
        # 放射,从原生request对象中取属性方法
        return getattr(self._request, attr)
    except AttributeError:
        # 改进,不仅可以点取属性,还可以用[]的形式取属性,类.__dict__
        return self.__getattribute__(attr)
    
# request.data 不是属性,是@property修饰的一个方法
# 还有返回的一个字典,post请求不管使用什么编码(urldecode、formdata、json),传过来的数据,都在request.data,
@property
def data(self):
    if not _hasattr(self, '_full_data'):
        self._load_data_and_files()
        return self._full_data
    
# GET方发虽然重写了,还是request.GET,
@property
def query_params(self):
    return self._request.GET
    
    # 视图类中
    print(request.query_params) 
    '''
        也是GET过来的数据,作者给改了个名,认为query_params是查询集,
        更符合drf规范,本来get过来的数据就是查询集
        '''        
    print(request.GET)