zoukankan      html  css  js  c++  java
  • 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)
  • 相关阅读:
    HDU 2544 最短路
    HDU 3367 Pseudoforest
    USACO 2001 OPEN
    HDU 3371 Connect the Cities
    HDU 1301 Jungle Roads
    HDU 1879 继续畅通工程
    HDU 1233 还是畅通工程
    HDU 1162 Eddy's picture
    HDU 5745 La Vie en rose
    HDU 5744 Keep On Movin
  • 原文地址:https://www.cnblogs.com/hzhcdhm/p/8763546.html
Copyright © 2011-2022 走看看