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)
# 上面寫的 BookSerializers 換作如下相似於 ModelForm 的寫法,更簡潔 # 這裏 BookSerializers 繼承的是 serializers.ModelSerializer class BookSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # 當序列化類 Meta 中定義了depth 時,這個序列化類中引用字段(外鍵)則自動變爲只讀, # 因此在進行更新或者建立的操做的時候不能使用此序列化類 # depth = 1
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)
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
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)
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] } ] '''
上一節的視圖部分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)
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
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)