资讯专栏INFORMATION COLUMN

10-django——RESTful API 之序列化

Bowman_han / 2292人阅读

摘要:之序列化前后端分离就是前台的开发和后台的开发分离,这个技术方案的实现需要借助,简单来说就是开发人员提供编程的接口被其他人调用,调用之后会返回数据供其使用安装什么是序列化把模型对象转换为格式然后响应出去,便于客户端进行数据解析创建序列化类在应

Django RESTful API之序列化

前后端分离:就是前台的开发和后台的开发分离,这个技术方案的实现需要借助API,简单来说就是开发人员提供编程的接口被其他人调用,调用之后会返回数据供其使用

安装pip install djangorestframework

什么是序列化?:把模型对象转换为JSON格式然后响应出去,便于客户端进行数据解析

创建序列化类

在应用目录下创建名为serializers.py的文件

from rest_framework import serializers
from myApp.models import Student, Grade
#给学生类创建序列化类
class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = ("id", "name", "sex", "age", "content", "isDelete", "grade")
#该班级创建序列化类
class GradeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Grade
        fields = ("id", "name", "boyNum", "girlNum", "isDelete")
使用系列化

进入shell环境:python manage.py shell

引入序列化类,创建序列化对象查看可序列化的字段:

>>> from myApp.serializers import StudentSerializer
>>> serializer = StudentSerializer()
>>> print(serializer)
StudentSerializer():
    id = IntegerField(label="ID", read_only=True)
    name = CharField(max_length=20)
    sex = BooleanField(required=False)
    age = IntegerField(max_value=2147483647, min_value=-2147483648)
    contend = CharField(max_length=40)
    isDelete = BooleanField(label="IsDelete", required=False)
    grade = PrimaryKeyRelatedField(queryset=Grade.objects.all())

找到一个学生:

>>> from myApp.models import Student
>>> stu = Student.objects.get(pk=1)
>>> print(stu)
薛延美

依据学生创建序列化对象,再对对象进行序列化操作:

>>> serializer = StudentSerializer(stu)
>>> print(serializer.data)
{"id": 1, "name": "薛延美", "sex": False, "age": 20, "contend": "我叫薛延美", "isDelete": False, "grade": 4}
>>> print(type(serializer.data))

将数据渲染成JSON格式

>>> from rest_framework.renderers import JSONRenderer
>>> content = JSONRenderer().render(serializer.data)
>>> print(content)
b"{"id":1,"name":"xe8x96x9bxe5xbbxb6xe7xbex8e","sex":false,"age":20,"contend":"xe6x88
x91xe5x8fxabxe8x96x9bxe5xbbxb6xe7xbex8e","isDelete":false,"grade":4}"

反序列化:当客户需要修改、增加、删除数据时,就要这个过程反过来,就叫反序列化

>>> from rest_framework.parsers import JSONParser
>>> from django.utils.six import BytesIO
>>> stream = BytesIO(content)
>>> print(stream)
<_io.BytesIO object at 0x000001EECF597E08>
>>> stu2 = JSONParser().parse(stream)
>>> print(stu2)
{"id": 1, "name": "薛延美", "sex": False, "age": 20, "contend": "我叫薛延美", "isDelete": False, "grade": 4}
>>> print(type(stu2))

检测数据并保存

>>> stu2.save()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: "dict" object has no attribute "save"
>>> serializer = StudentSerializer(data=stu2)
>>> print(serializer.is_valid())
True
>>> print(serializer.validated_data)
OrderedDict([("name", "薛延美"), ("sex", False), ("age", 20), ("contend", "我叫薛延美"), ("isDel
ete", False), ("grade", )])
>>> print(type(serializer.validated_data))

>>> print(serializer.validated_data["name"])
薛延美
>>> serializer.save()
视图实现使用序列化
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse

from myApp.models import Student, Grade

from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from django.utils.six import BytesIO

from myApp.serializers import StudentSerializer, GradeSerializer

def studentsList(request):
    if request.method == "GET":
        stus = Student.objects.all()
        #序列化
        serializer = StudentSerializer(stus, many=True)
        return JsonResponse(serializer.data, safe=False)
    elif request.method == "POST":
        # content = JSONRenderer().render(request.POST)
        # stream = BytesIO(content)
        # stuDict = JSONParser().parse(stream)
        # serializer = StudentSerializer(data=stuDict)
        serializer = StudentSerializer(data=request.POST)
        if serializer.is_valid():
            #存数据
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse({"error":serializer.errors}, status=400)
def studentDetail(request, pk):
    try:
        stu = Student.objects.get(pk=pk)
    except Student.DoesNotExist as e:
        return JsonResponse({"error":str(e)}, status=404)

    if request.method == "GET":
        serializer = StudentSerializer(stu)
        return JsonResponse(serializer.data)
    elif request.method == "PUT":
        #content = JSONRenderer().render(request.data)
        #stream = BytesIO(content)
        #stuDict = JSONParser().parse(stream)
        # print(stuDict)
        #修改
        serializer = StudentSerializer(stu, data=request.data)
        if serializer.is_valid():
            #存数据
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse({"error":serializer.errors}, status=400)
    elif request.method == "DELETE":
        stu.delete()
        return HttpResponse(status=204,content_type="application/json")
Django RESTful API 之请求与响应 激活应用
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "myApp",
    "rest_framework",
]
Request对象
request.POST: 只能处理表单数据,并且只能处理POST请求

扩展: request.data 能处理各种请求的数据,可以处理PUT和PATCH请求的数据

Response对象
HttpResponse、JsonResponse类: 用于返回json数据,在return的时候需要指明json格式

扩展: Reponse类 会根据客户端的请求头信息返回正确的内容类型

状态码
发送http请求会返回各种各样的状态码,但是状态码都是数字,不能够明确的让程序员了解是什么问题

扩展 HTTP_400_BAD_REQUEST 极大提高了可读性

视图

@api_view: 是装饰器,用在基于函数的视图上

APIView: 是类,用在基于类的视图上

作用: 提供一些功能,让程序员省去了很多工作,确保在视图中收到request对象或在对象中添加上下文 装饰器可以在接收到输入错误的request.data时抛出ParseError异常,在适当的时候返回405状态码

代码
# from django.shortcuts import render
# from django.http import HttpResponse, JsonResponse
from myApp.models import Student, Grade

# from rest_framework.renderers import JSONRenderer
# from rest_framework.parsers import JSONParser
# from django.utils.six import BytesIO

from myApp.serializers import StudentSerializer

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(["GET", "POST"])
def studentsList(request):
    if request.method == "GET":
        stus = Student.objects.all()
        #序列化
        serializer = StudentSerializer(stus, many=True)
        # 不需要指定json格式,返回客户端可以返回json或者HTML,返回HTML内容的话,会在浏览器中经过渲染成页面
        return Response(serializer.data, status=status.HTTP_200_OK)
    elif request.method == "POST":
        # content = JSONRenderer().render(request.POST)
        # stream = BytesIO(content)
        # stuDict = JSONParser().parse(stream)
        serializer = StudentSerializer(data=request.data)
        if serializer.is_valid():
            #存数据
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response({"error":serializer.errors}, status=status.HTTP_400_BAD_REQUEST)

@api_view(["GET", "PUT", "DELETE"])
def studentDetail(request, pk):
    try:
        stu = Student.objects.get(pk=pk)
    except Student.DoesNotExist as e:
        return Response({"error":str(e)}, status=status.HTTP_404_NOT_FOUND)

    if request.method == "GET":
        serializer = StudentSerializer(stu)
        return Response(serializer.data)
    elif request.method == "PUT":
        # content = JSONRenderer().render(request.POST)
        # stream = BytesIO(content)
        # stuDict = JSONParser().parse(stream)
        # print(stuDict)
        #修改
        serializer = StudentSerializer(stu, data=request.data)
        if serializer.is_valid():
            #存数据
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response({"error":serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
    elif request.method == "DELETE":
        stu.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
向URL添加可选的后缀 视图
def studentsList(request, format=None):
def studentDetail(request, pk, format=None):
路由
from django.conf.urls import url
from myApp import views
#格式后缀
from rest_framework.urlpatterns import format_suffix_patterns

urlpatterns = [
    # GET /students/
    # POST /students/
    url(r"^students/$", views.studentsList),
    # GET /students/id
    # PUT /students/id
    # PATCH /students/id
    # DELETE /students/id
    url(r"^students/(?Pd+)/$", views.studentDetail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
测试
http://127.0.0.1:8000/students.api
http://127.0.0.1:8000/students.json
Django RESTful API 之基于类的视图 把视图变成类
from myApp.models import Student
from myApp.serializers import StudentSerializer
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from django.http import Http404

class StudentsList(APIView):
    def get(self, request, format=None):
        stus = Student.objects.all()
        serializer = StudentSerializer(stus, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
    def post(self, request, format=None):
        serializer = StudentSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)

class StudentDetail(APIView):
    def getObject(self, pk):
        try:
            return Student.objects.get(pk=pk)
        except Student.DoesNotExist as e:
            raise Http404
    def get(self, request, pk, format=None):
        stu = self.getObject(pk)
        serializer = StudentSerializer(stu)
        return Response(serializer.data)
    def put(self, request, pk, format=None):
        stu = self.getObject(pk)
        serializer = StudentSerializer(stu, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response({"error": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
    def delete(self, request, pk, format=None):
        stu = self.getObject(pk)
        stu.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
修改路由匹配类视图
from django.conf.urls import url
from myApp import views
#格式后缀
from rest_framework.urlpatterns import format_suffix_patterns

urlpatterns = [
    # GET /students/
    # POST /students/
    url(r"^students/$", views.StudentsList.as_view()),
    # GET /students/id
    # PUT /students/id
    # PATCH /students/id
    # DELETE /students/id
    url(r"^students/(?Pd+)/$", views.StudentDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
优点

把各种HTTP请求分离开

可以轻松构成可重复使用的行为

可以大大简化代码

增加了可读性

使用Mixins类

基本使用

from myApp.models import Student
from myApp.serializers import StudentSerializer
from rest_framework import mixins, generics

#父类中有且只有一个能继承自APIView类
class StudentsList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    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 StudentDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    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)

通用视图使用

from myApp.models import Student
from myApp.serializers import StudentSerializer
from rest_framework import generics
class StudentsList(generics.ListCreateAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
class StudentDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
Django RESTful API 之认证和权限

如果没有权限认证功能,任何资源都会被任何用户随意修改,所以实现如下功能

Student与其创建者相互关联

只有经过身份验证(登陆)的用户才可以创建Student对象

只有创建该Student对象的用户才可以对齐进行更新或者删除

未经验证的用户只有访问(只读)的功能

给学生添加所属用户字段:owner = models.ForeignKey("auth.User", related_name="students")

重新生成表

创建几个用户 python manage.py createsuperuser

在serializers.py文件中给User添加序列化类

from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("id", "username", "students")
增加用户的接口

路由

from django.conf.urls import url
from myApp import views
#格式后缀
from rest_framework.urlpatterns import format_suffix_patterns

urlpatterns = [
    url(r"^students/$", views.StudentsList.as_view()),
    url(r"^students/(?Pd+)/$", views.StudentDetail.as_view()),

    url(r"^users/$", views.UsersList.as_view()),
    url(r"^users/(?Pd+)/$", views.UserDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)

视图

from django.contrib.auth.models import  User
class UsersList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
class UserDetail(generics.RetrieveDestroyAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
把Student和User关联

概述: 还不能把Student和User关联,因为在使用的时候User的数据时通过Request传入的,而不是以序列化数据传递的,此时刚才添加了一个owner作为外键,此时使用外键

class StudentsList(generics.ListCreateAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    
    #让用户在通过post请求创建一个新的student时,在保证创建学生时会把request中的user赋值给该学生的owner字段
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
在显示学生时还需要显示学生属于哪个用户
class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        owner = serializers.ReadOnlyField(source="owner.username")
        model = Student
        fields = ("id", "name", "sex", "age", "contend", "isDelete", "grade", "owner")
添加权限
from rest_framework import permissions
class StudentsList(generics.ListCreateAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    #让用户在通过post请求创建一个新的student时,在保证创建学生时会把request中的user赋值给该学生的owner字段
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
class StudentDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    # 只有所有者用户才能删除、修改,其他用户只能访问
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
为可浏览的API添加登陆功能

工程目录下与工程目同名目录下的urls.py文件

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r"^admin/", admin.site.urls),
    url(r"^api-auth/", include("rest_framework.urls", namespace="rest_framework")),
    url(r"^", include("myApp.urls")),
]
添加对象权限

要实现让所有的Students可以被所有人访问,但是每个学生只能被其创建者所操作。

需要自定义权限,让每个学生只能被其创建者所操作,在应用目录下创建permissions.py的文件

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            # 用户请求为GET 可以只读
            return True
        return obj.owner == request.user

添加自定义权限

from myApp.permissions import IsOwnerOrReadOnly
class StudentDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    # 只有所有者用户才能删除、修改,其他用户只能访问
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly)
API授权

由于现在我们还没有使用Authentication类,所以项目目前还是使用默认的SessionAuthentication和BaseAuthentication

在使用浏览器访问API的时候,浏览器会帮助我们保存会话信息,所以当权限满足是就可以对一个学生对象进行删除或者更新,还可以创建学生

当如果通过命令来操作API,我们就必须在每次发送请求是附带验证信息 : http://user1:sunck1999@127.0.0.1:8000/students/1/

程序中使用 from django.contrib.auth import login

Django RESTful API 之ViewSet和Routers

目的: 介绍另一种基于类的视图的写法,它的抽象程度更高,代码更少

使用ViewSets重构视图
from myApp.models import Student
from myApp.serializers import StudentSerializer, UserSerializer
from rest_framework import permissions
from myApp.permissions import IsOwnerOrReadOnly
from django.contrib.auth.models import  User
from rest_framework import viewsets

class StudentViewSet(viewsets.ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly)
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
重构路由
from django.conf.urls import url, include
from myApp.views import StudentViewSet, UserViewSet
from rest_framework.urlpatterns import format_suffix_patterns

students_list = StudentViewSet.as_view({
    "get":"list",
    "post":"create"
})
student_detail = StudentViewSet.as_view({
    "get":"retrieve",
    "put":"update",
    "patch":"partial_update",
    "delete":"destroy"
})
users_list = UserViewSet.as_view({
    "get":"list"
})
user_detail = UserViewSet.as_view({
    "get":"retrieve"
})
urlpatterns = format_suffix_patterns([
    url(r"^students/$", students_list, name="students_list"),
    url(r"^students/(?Pd+)/$", student_detail, name="student_detail"),
    url(r"^users/$", users_list, name="users_list"),
    url(r"^users/(?Pd+)/$", user_detail, name="user_detail"),
])
使用Routers
from django.conf.urls import url, include
from myApp import views
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"students", views.StudentViewSet)
router.register(r"users", views.UserViewSet)

urlpatterns = [
    url(r"^", include(router.urls))
]

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42039.html

相关文章

  • 9-django——restful设计风格

    摘要:设计风格协议与用户的通信协议,总是使用协议域名应该尽量将部署在专用域名之下,如果确定很简单,不会有进一步的扩展,可以考虑放在主域名之下。数据库中的表示记录同种数据的集合,所以中的名词也应该使用复数。 showImg(https://segmentfault.com/img/bVbdXlE?w=1560&h=913); RESTful Api设计风格 协议:API与用户的通信协议,总是使...

    scq000 评论0 收藏0
  • Flask 扩展系列 Flask-RESTful

    摘要:励以最少的安装方式进行最佳实践。上面的例子接收了一个对象并准备将其序列化。装饰器会通过进行转换。从对象中提取的唯一字段是。是一个特殊的字段,它接受端点名称并为响应中的端点生成一个。可以查看项查看完整列表。 大纲 简介 安装 快速入门 一个最小的 api 例子 资源丰富的路由 端点 参数解析 数据格式化 完整 TODO 应用例子 简介 Flask-RESTful是一个Flas...

    阿罗 评论0 收藏0
  • Kubernetes1.5源码分析(三) apiServergo-restful的使用

    摘要:它包括一组和一个对象,使用进行请求派发。流程基本就是这样,接着我们直接进入接口看实现拼装然后填充并返回一个对象创建一个这个是关键,会对各种进行注册增加一个的将该加入到前两个调用函数比较简单,这里不进行介绍了。 源码版本 Kubernetes v1.5.0 go-restful 简介 go-restful是用于构建REST-style web服务的golang包。它是出现时因为一个jav...

    Doyle 评论0 收藏0
  • Kubernetes1.5源码分析(二) apiServer资源注册

    摘要:我们先将上面的接口解析放放,先看下是如何初始化的路径定义了,再看路径定义空的创建,用于不同版本对象转换增加一些转换函数上面就创建了一个空的。其实就是向添加了转换函数,比如将转换为,将转换为。 源码版本 Kubernetes v1.5.0 简介 k8s里面有各种资源,如Pod、Service、RC、namespaces等资源,用户操作的其实也就是这一大堆资源。但这些资源并不是杂乱无章的,...

    imccl 评论0 收藏0

发表评论

0条评论

Bowman_han

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<