• 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)
  • 相关阅读:
    sshd服务防止暴力破解
    使用秘钥ssh登录远程服务器
    SSH配置文件详解
    WinForm、wpf、silverlight三者关系
    silverlight 和winform的结合使用
    IIS在W7下使用
    c#多线程
    Silverlight的Socket通信
    wcf和webservice区别
    aspx向silverlight传值
  • 原文地址:https://www.cnblogs.com/hzhcdhm/p/8763546.html
Copyright © 2020-2023  润新知