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)