Django REST Framework介绍

时间:Nov. 8, 2019 分类:

目录:

基本知识整理

RESTful设计风格

通过不同的请求方法来定义逻辑,url描述资源,不包含动词

GET     127.0.0.1/books/    # 查询所有书籍,返回书籍对象列表
GET     127.0.0.1/books/1/  # 查询特定id书籍信息,返回单个书籍对象
POST    127.0.0.1/books/    # 添加书籍,返回新生成的书籍对象
PUT     127.0.0.1/books/1/  # 更新特定id书籍信息,返回修改完的书籍资源对象
DELETE  127.0.0.1/books/1/  # 删除特定id书籍信息,返回空

返回的消息理论上不只有请求,还要有

  • 返回状态码
  • 携带错误信息
  • 携带超链接

基于类的视图

不同于基于函数的视图,基于类的视图,需要通过视图类调用as_view()这个类方法,从而实现请求的分发

url.py

from django.urls import path

from classbasedview import views

urlpatterns = [
    path('login/', views.LoginView.as_view()),
]

view.py

from django.shortcuts import render, HttpResponse
from django.views import View

# Create your views here.


class LoginView(View):
    def get(self, request):
        return render(request, 'classbasedview/login.html')

    def post(self, request):
        return HttpResponse("post")

类方法classmethod和django的classonlymethd

classmethod是一个装饰器,允许类直接调用该方法而不用传入实例化对象

# -*- coding: utf-8 -*-
from django.utils.decorators import classonlymethod

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def show_info(self):
        print("show info method been executed")

    @classmethod
    def show_info2(cls):
        print("show info2 method been executed")

    @classonlymethod
    def show_info3(cls):
        print("show info3 method been executed")


p1 = Person("pizza", 18)
# 普通方法可以通过实例对象和类调用
# 通过实例调用时默认传入self作为第一个参数,不需要手动传入参数
# 通过类调用时,必须手动指定实例对象传递给该方法
p1.show_info()
Person.show_info(p1)

# 被classmethod装饰器装饰后的方法可以通过实例对象和类直接调用
# 默认传入类名作为第一个参数
p1.show_info2()
Person.show_info2()

p1.show_info3()      # 报错,Django框架实现了classonlymethd,不再允许实例对象调用被该装饰器装饰的方法
Person.show_info3()

hasattr、getattr、setattr

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self._hobby = 'girls'

    def show_info(self):
        print("show info method been executed")


p1 = Person("pizza", 18)
# 查看该对象是否有show_info方法
print(hasattr(p1, "show_info"))
# 查看该对象是否有age属性
print(hasattr(p1, "age"))
print(hasattr(p1, "hahaha"))

greeting = "Hello World"
# 设置属性
setattr(p1, "hahaha", greeting)
print(hasattr(p1, "hahaha"))

func = getattr(p1, "show_info")
print(func)  # <bound method Person.show_info of <__main__.Person object at 0x102219d68>>
# 注意:直接调用,不需要传入self,getattr时已经绑定self到func了
func()

print(hasattr(Person, "show_info"))  # True
print(hasattr(Person, "name"))       # False
print(hasattr(Person, "country"))    # False
# 给Person类设置属性
setattr(Person, "country", "china")
print(hasattr(Person, "country"))    # True
print(hasattr(p1, "country"))        # True

DRF介绍

通过类视图或者其他方式我们都可以完成满足RESTful API的规范

class CoursesView(View):
    def get(self, request):
        courses = list()

        for item in Courses.objects.all():
            course = {
                "title": item.title,
                "price": item.price,
                "publish_date": item.publish_date,
                "publish_id": item.publish_id
            }

            courses.append(course)

        return HttpResponse(json.dumps(courses, ensure_ascii=False))

django有对应的包djangorestframework

$ pip install djangorestframework

djangorestframework包含以下功能

  • APIView
  • 解析器组件
  • 序列化组件
  • 视图组件
  • 认证组件
  • 权限组件
  • 频率控制组件
  • 分页组件
  • 响应器组件
  • url控制器

对于一个函数在不修改函数的前提下增加功能一般是需要装饰器来实现

def outer(func):
    def inner(*args, **kwargs):
        import time
        start_time = time.time()
        ret = func(*args, **kwargs)
        end_time = time.time()
        print("This function elapsed %s" % str(end_time - start_time))
        return ret
    return inner


@outer
def add(x, y):
    return x + y

使用djangorestframework的类视图不继承View,而是继承其APIView,也是提供了重写dispatch()方法来实现定制

  • as_view调用为继承的view类中的as_view
  • as_view的返回值是view方法
  • view方法返回的dispatch方法
  • dispatch方法判断请求的方法是否在列表中,如果在就获取对应的反射函数执行,如果没有就返回405异常

Django解析json格式的Post请求

django的request的源码

if self.content_type == 'multipart/form-data':
    if hasattr(self, '_body'):
        # Use already read data
        data = BytesIO(self._body)
    else:
        data = self
    try:
        self._post, self._files = self.parse_file_upload(self.META, data)
    except MultiPartParserError:
        # An error occurred while parsing POST data. Since when
        # formatting the error the request handler might access
        # self.POST, set self._post and self._file to prevent
        # attempts to parse POST data again.
        # Mark that an error occurred. This allows self.__repr__ to
        # be explicit about it instead of simply representing an
        # empty POST
        self._mark_post_parse_error()
        raise
elif self.content_type == 'application/x-www-form-urlencoded':
    self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
else:
    self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()

Django原生的解析器并不处理application/json编码协议的请求,需要通过request.body获取数据,并通过json.loads获取数据

class LoginView(View):
    def get(self, request):
        return render(request, 'classbasedview/login.html')

    def post(self, request):
        print(request.POST)  # <QueryDict: {}>
        print(request.body)  # b'{"username":"wanghongyu","password":123456}'
        data = request.body.decode('utf-8')
        dict_data = json.loads(data)

        username = dict_data['username']
        password = dict_data['password']

        return HttpResponse(json.dumps(dict_data))

可以用于测试的前端代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
  <script src="/static/jquery-1.10.2.min.js"></script>
</head>
<body>
  <form action="" method="post" enctype="application/x-www-form-urlencoded">
    {% csrf_token %}
    用户名: <input type="text" name="username"/>
    密码:  <input type="password" name="password"/>
    提交:  <input type="submit" value="提交"/>
  </form>

  <hr>
  <button class="btn">点击发送Ajax请求</button>

  <script>
    $(".btn").click(function () {
      $.ajax({
        url: '',
        type: 'post',
        contentType: 'application/json',
        data: JSON.stringify({
          username: "alex",
          password: 123
        }
        ),
        success: function (data) {
          console.log(data);
        }
      })
    })

  </script>

</body>
</html>