rest-framework 框架的基本組件

快速實例

Quickstartphp

序列化

建立一個序列化類

簡單使用

開發 Web API 的第一件事是爲 Web API 提供一種將代碼片斷實例序列化和反序列化爲諸如 json 之類的表示形式的方式。咱們能夠經過聲明與Django forms 很是類似的序列化器(serializers)來實現。python

models 部分:django

from django.db import models

# Create your models here.


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 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

views 部分:json

from django.shortcuts import render, HttpResponse
from django.core import serializers
from django.views import View
from .models import *
import json
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

# Create your views here.


# Serializer是從rest_framework中的類
class BookSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()

    # publish 是一對多的外鍵,若是不加source="publish.pk",則使用的是model.py中表的__str__
    publish = serializers.CharField(source="publish.pk")
    
    # authors 是ManyToManyField類型,能夠按照publish的方式來寫,可是結果看起來不清晰
    # authors = serializers.CharField(source="authors.all")  # "authors": "<QuerySet [<Author: xh>, <Author: xh>]>"
    
    # 將authors 按照下面的方式寫
    authors = serializers.SerializerMethodField()

    # def get_authors(self, obj):
    #     temp = []
    #     for author in obj.authors.all():
    #         temp.append(author.name)
    #     return temp

    '''
       顯示的結果是  
       [
          {
            "title": "php",
            "price": 13,
            "pub_date": "2018-03-02",
            "publish": "3",
            "authors": ["xh","xh"]
          },
          {
            "title": "python",
            "price": 24,
            "pub_date": "2018-04-09",
            "publish": "2",
            "authors": [ "xh","xm"]
          }
       ] 
       '''

    # 也能夠進行自定製顯示樣式
    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append({"pk": author.pk, "name": author.name})
        return temp
    '''
    顯示的author是  
    [
      {
        "title": "php",
        "price": 13,
        "pub_date": "2018-03-02",
        "publish": "3",
        "authors": [{"pk": 2, "name": "xh"},{"pk": 2,"name": "xh"}]
      },
      {
        "title": "python",
        "price": 24,
        "pub_date": "2018-04-09",
        "publish": "2",
        "authors": [{"pk": 2,"name": "xh"},{"pk": 1,"name": "xm"}]
      }
    ]
    '''


class BookViewSet(APIView):
    def get(self, request, *args, **kwargs):
        book_list = Book.objects.all()
        # 序列化方式一:
        # book_list = list(Book.objects.all().values("title", "price"))
        # return HttpResponse(json.dumps(book_list))

        # 序列化方式二:
        # temp = []
        # for 循環book_list,獲得的每個book,都是一個book對象
        # for book in book_list:
        #     temp.append({
        #         "title": book.title,
        #         "price": book.price,
        #         "pub_data": book.pub_date
        #     })
        # return HttpResponse(json.dumps(temp))

        # 序列化方式三:
        # temp = serializers.serialize("json", book_list)
        # return HttpResponse(temp)

        # 序列化方式四:這個時候就不能繼承View,要繼承的是APIView
        # 將book_list 轉換成json數據 [{}, {}, {}]
        bs = BookSerializers(book_list, many=True)
        return Response(bs.data)

ModelSerializer

# 上面寫的 BookSerializers 換作如下相似於 ModelForm 的寫法,更簡潔

# 這裏 BookSerializers 繼承的是 serializers.ModelSerializer
class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"

        # 當序列化類 Meta 中定義了depth 時,這個序列化類中引用字段(外鍵)則自動變爲只讀,
        # 因此在進行更新或者建立的操做的時候不能使用此序列化類
        # depth = 1

提交POST請求

    def post(self, request, *args, **kwargs):
        
        # 獲得用戶添加的數據,其中request在APIView中的def dispatch中經過request = self.initialize_request(request, *args, **kwargs)進行了從新定義,
        # 如今使用的request = self.request._request
        bs = BookSerializers(data=request.data, many=False)
        if bs.is_valid():  # 對數據bs進行驗證
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

重寫 save 中的 create 方法

class BookSerializers(serializers.ModelSerializer):

      class Meta:
          model=Book
          fields="__all__"
          # exclude = ['authors',]
          # depth=1

      def create(self, validated_data):
        
          authors = validated_data.pop('authors')
          obj = Book.objects.create(**validated_data)
          obj.authors.add(*authors)
          return obj

或者是用以前的方法,將代碼改爲如下形式:api

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"

        # 當序列化類 Meta 中定義了depth 時,這個序列化類中引用字段(外鍵)則自動變爲只讀,
        # 因此在進行更新或者建立的操做的時候不能使用此序列化類
        # depth = 1
        
    # 自定義authors字段的顯示格式
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append({"pk": author.pk, "name": author.name})
        return temp

單條數據的 get 和 put 請求

urls.py 文件中添加一條url:app

url(r'^books/(?P<pk>\d+)/$', views.BookDetailViewSet.as_view(), name="book_detail"),

view.py 文件中:框架

class BookDetailViewSet(APIView):
    def get(self, request,pk, *args, **kwargs):
        book_list = Book.objects.filter(pk=pk)
        # 實例化一個帶有數據的 BookSerializers 對象
        bs = BookSerializers(book_list)
        return Response(bs.data)

    def post(self, request, pk, *args, **kwargs):
        book_list = Book.objects.filter(pk=pk)
        bs = BookSerializers(book_list, data=request.data)
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

超連接 API:Hyperlinked

class BookSerializers(serializers.ModelSerializer):
    publish = serializers.HyperlinkedIdentityField(
        view_name="book_detail",  # 是urls.py中的name的值
        lookup_field="publish_id",  # 在頁面展現時的格式
        lookup_url_kwarg="pk"
    )

    class Meta:
        model = Book
        fields = "__all__"
'''
[
  {
    "id": 1,
    "publish": "http://127.0.0.1:8001/books/3/",
    "title": "php",
    "price": 13,
    "pub_date": "2018-03-02",
    "authors": [2]
  },
  {
    "id": 2,
    "publish": "http://127.0.0.1:8001/books/2/",
    "title": "python",
    "price": 24,
    "pub_date": "2018-04-09",
    "authors": [2,1]
  }
]
'''

視圖三部曲

使用混合(mixins)

 上一節的視圖部分dom

from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from django.shortcuts import HttpResponse
from django.core import serializers


class BookSerializers(serializers.ModelSerializer):
    publish = serializers.HyperlinkedIdentityField(
        view_name="book_detail",  # 是urls.py中的name的值
        lookup_field="publish_id",  # 在頁面展現時的格式
        lookup_url_kwarg="pk"
    )

    class Meta:
        model = Book
        fields = "__all__"


class PublishSerializers(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = "__all__"


class BookViewSet(APIView):
    def get(self, request, *args, **kwargs):
        book_list = Book.objects.all()
     # many = True 是能夠同時序列化一個Queryset對象 bs
= BookSerializers(book_list, many=True, context={'request': request}) return Response(bs.data) def post(self, request, *args, **kwargs):
     # request.data傳的實際上是一個Unicode字符串 bs
= BookSerializers(data=request.data, many=False, context={'request': request}) if bs.is_valid(): # 對數據bs進行驗證 bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class BookDetailViewSet(APIView): def get(self, request,pk, *args, **kwargs): book_list = Book.objects.filter(pk=pk) # 實例化一個帶有數據的 BookSerializers 對象 bs = BookSerializers(book_list) return Response(bs.data) def post(self, request, pk, *args, **kwargs): book_list = Book.objects.filter(pk=pk) bs = BookSerializers(book_list, data=request.data, context={'request': request}) if bs.is_valid(): bs.save() # save 內部作了一個updata 操做 return Response(bs.data) else: return HttpResponse(bs.errors) class PublishViewSet(APIView): def get(self, request, *args, **kwargs): publish_list = Publish.objects.all() bs = PublishSerializers(publish_list, many=True, context={'request': request}) return Response(bs.data) def post(self, request, *args, **kwargs): bs = PublishSerializers(data=request.data, many=False, context={'request': request}) if bs.is_valid(): # 對數據bs進行驗證 bs.save() # save 的內部作了一個create操做 return Response(bs.data) else: return HttpResponse(bs.errors) class PublishDetailViewSet(APIView): def get(self, request,pk, *args, **kwargs): publish_list = Publish.objects.filter(pk=pk) bs = PublishSerializers(publish_list) return Response(bs.data) def post(self, request, pk, *args, **kwargs): publish_list = Publish.objects.filter(pk=pk) bs = PublishSerializers(publish_list, data=request.data, context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)

mixin類編寫視圖

from rest_framework import mixins
from rest_framework import generics
from api.service.serializers import BookSerializers, PublishSerializers


class BookViewSet(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView
                  ):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

    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 BookDetailView(mixins.RetrieveModelMixin,
                     mixins.UpdateModelMixin,
                     mixins.DestroyModelMixin,
                     generics.GenericAPIView
                     ):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

    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.delete(request, *args, **kwargs)


class PublishViewSet(mixins.ListModelMixin,
                     mixins.CreateModelMixin,
                     generics.GenericAPIView
                    ):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializers

    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 PublishDetailView(mixins.RetrieveModelMixin,
                        mixins.UpdateModelMixin,
                        mixins.DestroyModelMixin,
                        generics.GenericAPIView
                         ):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializers

    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.delete(request, *args, **kwargs)

發現代碼的重複仍是很嚴重post

使用通用的基於類的視圖

經過使用mixin類,咱們使用更少的代碼重寫了這些視圖,但咱們還能夠更進一步來簡化代碼。ui

REST 框架提供了一組已經混合好的通用視圖,可使用它來簡化 views.py 模塊。

class BookSerializers(serializers.ModelSerializer):
    publish = serializers.HyperlinkedIdentityField(
        view_name="book_detail",  # 是urls.py中的name的值
        lookup_field="publish_id",  # 在頁面展現時的格式
        lookup_url_kwarg="pk"
    )

    class Meta:
        model = Book
        fields = "__all__"


class PublishSerializers(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = "__all__"


class BookView(generics.ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers


class BookDetailView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers


class PublishView(generics.ListCreateAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializers


class PublishDetailView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializers

viewsrts.Model.ModelViewSet

urls.py 部分:

from django.conf.urls import url
from django.contrib import admin
from api import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^books/$', views.BookView.as_view({"get": "list", "post": "create"})),
    url(r'^publishes/$', views.PublishView.as_view({"get": "list", "post": "create"})),
    url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view({'get': 'retrieve','put': 'update','patch': 'partial_update','delete': 'destroy'}), name="book_detail"),
    url(r'^publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view({'get': 'retrieve','put': 'update','patch': 'partial_update','delete': 'destroy'}), name="book_detail"),
]

views.py 文件中

 # 這一部分被移到了 api.service.serializers文件中了
from rest_framework import serializers
from ..models import *

# Book表的序列化組件 class BookSerializers(serializers.ModelSerializer): publish = serializers.HyperlinkedIdentityField( view_name="book_detail", # 是urls.py中的name的值 lookup_field="publish_id", # 在頁面展現時的格式 lookup_url_kwarg="pk" ) class Meta: model = Book fields = "__all__"
# Publish表的序列化組件 class PublishSerializers(serializers.ModelSerializer): class Meta: model = Publish fields = "__all__" # 這一部分是在 views.py 文件中的 rom rest_framework import viewsets class BookView(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers class BookDetailView(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers class PublishView(viewsets.ModelViewSet): queryset = Publish.objects.all() serializer_class = PublishSerializers class PublishDetailView(viewsets.ModelViewSet): queryset = Publish.objects.all() serializer_class = PublishSerializers

認證與權限組件

認證組件

局部視圖認證

這時候發現原來的表不夠用了,將原來的model.py文件中添加下面的兩張表:

from django.db import models


class User(models.Model):
    username = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    tokentyppe  = models.IntegerField(choices=((1, "大衆會員"),(2, "白銀會員"), (3, "黃金會員"), (3, "鑽石會員")), default=1)

    def __str__(self):
        return self.username


class UserToken(models.Model):
    user = models.OneToOneField("User")
    token = models.CharField(max_length=128)

在 api.service.auth.py文件中:

from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from ..models import *


class Authentication(BaseAuthentication):

    def authenticate(self, request):
        token = request._request.GET.get("token")
        token_obj = UserToken.objects.filter(token=token).first()  # 後面 .first()獲得的是一個obj對象
        # 要進行認證,則須要經過判斷是否有 token 爲標準
        if not token_obj:
            return exceptions.AuthenticationFailed("驗證失敗")

        return (token_obj.user, token_obj)

在views.py 文件中:

from rest_framework import viewsets
from api.service.auth import *
from django.http import JsonResponse


def get_random_str(user):
    import hashlib, time
    # 將用戶登陸時的當前時間轉換成str類型,生成一個隨機字符串
    ctime = str(time.time())
    # 將用戶名轉換成utf8編碼的bytes類型,並進行md5加密
    md5 = hashlib.md5(bytes(user, encoding="utf8"))
    md5.update(bytes(ctime, encoding="utf8"))
    return md5.hexdigest()


class LoginView(APIView):
    # 這裏是在局部進行認證
    # 這裏的authentication_classes是來自於APIView中的源碼,名字不可隨意更改,
    # 在其後的列表中加入本身寫的Authentication類,若是沒有本身寫Authentication類,就會默認走父類本身的DEFAULT_AUTHENTICATION_CLASSES
    # authentication_classes = [MyAuthentication, ]

    def post(self, request, *args, **kwargs):
        # 定義返回值,當用戶登陸成功時code=100,當用戶登陸錯誤的時候返回錯誤提示msg
        res = {"code":100, "msg": None}
        # request.data 獲得的是原生數據
        user = request.data.get("username")  # request在源碼中又複寫了POST方法,因此request.POST == request._request.POST
        pwd = request.data.get("pwd")
        user_obj = User.objects.filter(username=user, pwd=pwd).first()  # 後面加上.first()獲得的是一個obj對象
        if not user:
            res["code"] = 110
            res["msg"] = "用戶名或密碼錯誤"
        else:
            token = get_random_str(user_obj.username)
            # 在第一次登陸的時候會自動建立一條token記錄,若是不是第一次登陸,則會更新原來的token記錄
            user_token_obj = UserToken.objects.update_or_create(user=user_obj, defaults={"token": token})
            res["token"] = token
            res["msg"] = "登陸成功"
        print(res["msg"])
        return JsonResponse(res)
相關文章
相關標籤/搜索