10-django——RESTful API 之序列化

Django RESTful API之序列化

先後端分離:就是前臺的開發和後臺的開發分離,這個技術方案的實現須要藉助API,簡單來講就是開發人員提供編程的接口被其餘人調用,調用以後會返回數據供其使用python

安裝pip install djangorestframework程序員

什麼是序列化?:把模型對象轉換爲JSON格式而後響應出去,便於客戶端進行數據解析shell

建立序列化類

在應用目錄下建立名爲serializers.py的文件django

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))
<class 'rest_framework.utils.serializer_helpers.ReturnDict'>
  • 將數據渲染成JSON格式
>>> from rest_framework.renderers import JSONRenderer
>>> content = JSONRenderer().render(serializer.data)
>>> print(content)
b'{"id":1,"name":"\xe8\x96\x9b\xe5\xbb\xb6\xe7\xbe\x8e","sex":false,"age":20,"contend":"\xe6\x88
\x91\xe5\x8f\xab\xe8\x96\x9b\xe5\xbb\xb6\xe7\xbe\x8e","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))
<class 'dict'>
  • 檢測數據並保存
>>> stu2.save()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
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', <Grade: python04>)])
>>> print(type(serializer.validated_data))
<class 'collections.OrderedDict'>
>>> print(serializer.validated_data["name"])
薛延美
>>> serializer.save()
<Student: 薛延美>

視圖實現使用序列化

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類 會根據客戶端的請求頭信息返回正確的內容類型json

狀態碼

發送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/(?P<pk>\d+)/$', 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/(?P<pk>\d+)/$', views.StudentDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)

優勢

  • 把各類HTTP請求分離開
  • 能夠輕鬆構成可重複使用的行爲
  • 能夠大大簡化代碼
  • 增長了可讀性

使用Mixins類

基本使用api

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 之認證和權限

若是沒有權限認證功能,任何資源都會被任何用戶隨意修改,因此實現以下功能session

  • 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/(?P<pk>\d+)/$', views.StudentDetail.as_view()),

    url(r'^users/$', views.UsersList.as_view()),
    url(r'^users/(?P<pk>\d+)/$', 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/(?P<pk>\d+)/$', student_detail, name="student_detail"),
    url(r'^users/$', users_list, name="users_list"),
    url(r'^users/(?P<pk>\d+)/$', 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))
]
相關文章
相關標籤/搜索