rest-framework 框架的基本组件

快速实例

Quickstart

序列化

创建一个序列化类

简单使用

开发 Web API 的第一件事是为 Web API 提供一种将代码片段实例序列化和反序列化为诸如 json 之类的表示形式的方式。我们可以通过声明与Django forms 非常相似的序列化器(serializers)来实现。

models 部分:

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 部分:

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

或者是用之前的方法,将代码改成以下形式:

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:

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)

 上一节的视图部分

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)

发现代码的重复还是很严重

使用通用的基于类的视图

通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以更进一步来简化代码。

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)

 

posted on 2018-04-09 22:55  卖火柴的嫩火柴  阅读(145)  评论(0编辑  收藏  举报

导航