django web经典模块开发实战——第二章 用Django REST framework实现豆瓣API应用

1 豆瓣API功能介绍

豆瓣图书的API功能原理是通过输入图书的ISBN号、书名、作者、出版社等部分信息,就可以获取到该图书在豆瓣上的所有信息。除了检索信息,还要包含aplikey,用来记录开发者访问API的次数,以此向开发者收费。

2 Django REST framework序列化

2.1 简介

序列化是指将对象的状态信息转换为可以存储或传输形式的过程。Django中的序列化就是指将对象状态的信息转换为JSON数据,以达到将数据信息传送给前端的目的。

2.2 用serializers.Serializer方式序列化

  • 安装Django REST framework及其依赖包markdown和django-filter

    pip install djangorestframework markdown django-filter
    
  • 在settings中注册,代码如下

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'users',
        'rest_framework'
    ]
    
  • 在users的models.py,重构用户表UserProfile

    class UserProfile(AbstractUser):
        """用户"""
        APIkey = models.CharField(max_length=30, verbose_name='APIkey', default='abcdefghijklmn')
        money = models.IntegerField(default=10, verbose_name='余额')
        class Meta:
            verbose_name = '用户'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.username
    
  • 在settings中配置用户表的继承代码

    AUTH_USER_MODEL = 'users.UserProfile'
    
  • 在users的models.py中新建book

    class Book(models.Model):
        """书籍信息"""
        title = models.CharField(max_length=30, verbose_name='书名', default='')
        isbn = models.CharField(max_length=30, verbose_name='isbn', default='')
        author = models.CharField(max_length=20, verbose_name='作者', default='')
        publish = models.CharField(max_length=30, verbose_name='出版社', default='')
        rate = models.FloatField(default=0, verbose_name='豆瓣评分')
        add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
    
        class Meta:
            verbose_name = '书籍信息'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.title
    
  • 执行数据更新命令

    python manage.py makemigrations
    python manage.py migrate
    
  • 建立一个超级用户

    python manage.py createsuperuser
    Username:admin
    邮箱:1@1.com
    Password:
    Password(again):
    
  • 通过Databases操作版面,直接在book表内增加一条记录,title为一个书名,isbn为777777,author为一个作者,pubulish为一个出版社,rate为6.6,add_time随意

  • 在users目录下新建serializes.py,写入一下代码

    from rest_framework import serializers
    from .models import UserProfile, Book
    
    class BookSerializer(serializers.Serializer):
        title = serializers.CharField(required=True, max_length=100)
        isbn = serializers.CharField(required=True, max_length=100)
        author = serializers.CharField(required=True, max_length=100)
        publish = serializers.CharField(required=True, max_length=100)
        rate = serializers.FloatField(default=0)
    
  • 在users/views中写入视图代码

    from django.shortcuts import render
    from .serializers import BookSerializer
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .models import UserProfile, Book
    
    class BookAPIView1(APIView):
        """使用Serializer"""
        def get(self, request, format=None):
            APIKey = self.request.query_params.get('apikey', 0)
            developer = UserProfile.objects.filter(APIkey=APIKey).first()
            if developer:
                balance = developer.money
                if balance > 0:
                    isbn = self.request.query_params.get('isbn', 0)
                    books = Book.objects.filter(isbn=int(isbn))
                    books_serializer = BookSerializer(books, many=True)
                    developer.money -= 1
                    developer.save()
                    return Response(books_serializer.data)
                else:
                    return Response('要充钱啦!')
            else:
                return Response('查无此人啊')
    
  • 总路由以及自路由

    # 总
    url(r'^', include('users.urls', namespace='users')),
    # 子
    url(r'^apibook1/', views.BookAPIView1.as_view(), name='book1'),
    
  • 测试

    http://127.0.0.1:8000/apibook1/?apikey=abcdefghijklmn&isbn=777777

  • 获得了想要的json数据

    [
        {
            "title": "地狱公寓",
            "isbn": "777777",
            "author": "黑色火种",
            "publish": "网络",
            "rate": 6.6
        }
    ]
    

    去数据库看一下,发现money变为9

  • 如果故意输错apikey

    "查无此人啊"
    
  • 访问10次后

    "要充钱啦!"
    

至此,一个简单的模仿就实现了。实际项目中,查询表的字段不会这么少,查询一本书也会返回大量数据,如果使用Serializer进行序列化,工作量太大。

2.3 用sterilizers.ModelSerializer方式序列化

  • 定义序列化类

    class BookModelSerializer(serializers.ModelSerializer):
        """使用ModelSerializer"""
        class Meta:
            model = Book
            fields = '__all__'
    
  • 视图

    class BookAPIView2(APIView):
        """使用Serializer"""
        def get(self, request, format=None):
            APIKey = self.request.query_params.get('apikey', 0)
            developer = UserProfile.objects.filter(APIkey=APIKey).first()
            if developer:
                balance = developer.money
                if balance > 0:
                    isbn = self.request.query_params.get('isbn', 0)
                    books = Book.objects.filter(isbn=int(isbn))
                    books_serializer = BookModelSerializer(books, many=True)
                    developer.money -= 1
                    developer.save()
                    return Response(books_serializer.data)
                else:
                    return Response('要充钱啦!')
            else:
                return Response('查无此人啊')
    
  • 路由

url(r'^apibook2/', views.BookAPIView2.as_view(), name='book2'),
  • GET方式去访问

    http://127.0.0.1:8000/apibook1/?apikey=abcdefghijklmn&isbn=777777
    得到的json数据

    [
        {
            "id": 1,
            "title": "地狱公寓",
            "isbn": "777777",
            "author": "黑色火种",
            "publish": "网络",
            "rate": 6.6,
            "add_time": null
        }
    ]
    

    注:这里的add_time为null是因为使用了db.sqlite3数据库,以时间戳格式保存的,序列化失败,项目中使用mysql等主流数据库就不会出现这种情况

  • 返回指定字段

    class BookModelSerializer(serializers.ModelSerializer):
        """使用ModelSerializer"""
        class Meta:
            model = Book
            # fields = '__all__'
            fields = ('title', 'isbn', 'author')
    

    得到的json数据

    [
        {
            "title": "地狱公寓",
            "isbn": "777777",
            "author": "黑色火种"
        }
    ]
    

3 Django REST framework 视图三层封装

3.1 用mixins.ListModelMixin+GenericAPIView的方式实现视图封装

  • 视图

    class BookMixinView1(mixins.ListModelMixin, generics.GenericAPIView):
        queryset = Book.objects.all()
        serializer_class = BookModelSerializer
        def get(self, request, *args, **kwargs):
            APIKey = self.request.query_params.get('apikey', 0)
            developer = UserProfile.objects.filter(APIkey=APIKey).first()
            if developer:
                balance = developer.money
                if balance > 0:
                    isbn = self.request.query_params.get('isbn', 0)
                    developer.money -= 1
                    developer.save()
                    self.queryset = Book.objects.filter(isbn=int(isbn))
                    return self.list(request, *args, **kwargs)
                else:
                    return Response('要充钱啦')
            else:
                return Response('查无此人啊')
    
  • 路由

    url(r'^apibook3/', views.BookMixinView1.as_view(), name='book3'),
    
  • 返回数据

    [
        {
            "title": "地狱公寓",
            "isbn": "777777",
            "author": "黑色火种"
        }
    ]
    

3.2 用generics.ListAPIView 的方式实现视图封装

  • 视图

    class BookMixinView2(generics.ListAPIView):
        queryset = Book.objects.all()
        serializer_class = BookModelSerializer
    
        def get(self, request, *args, **kwargs):
            APIKey = self.request.query_params.get('apikey', 0)
            developer = UserProfile.objects.filter(APIkey=APIKey).first()
            if developer:
                balance = developer.money
                if balance > 0:
                    isbn = self.request.query_params.get('isbn', 0)
                    developer.money += 1
                    developer.save()
                    self.queryset = Book.objects.filter(isbn=int(isbn))
                    return self.list(request, *args, **kwargs)
                else:
                    return Response('该充钱了')
            else:
                return Response('查无此人')
    
  • 路由

    url(r'^apibook4/', views.BookMixinView2.as_view(), name='book4'),
    
  • 返回数据

    [
        {
            "title": "地狱公寓",
            "isbn": "777777",
            "author": "黑色火种"
        }
    ]
    

3.3 用viewsets+Router的方式实现视图封装

  • 视图

    class IsDeverloper(BasePermission):
        message = '查无此人啊'
        def has_permission(self, request, view):
            ApiKey = request.query_params.get('apikey', 0)
            developer = UserProfile.objects.filter(APIkey=ApiKey).first()
            if developer:
                return True
            else:
                print(self.message)
                return False
    
    class EnoughMoney(BasePermission):
        message = '该充钱啦'
        def has_permission(self, request, view):
            APIKey = request.query_params.get('apikey', 0)
            developer = UserProfile.objects.filter(APIkey=APIKey).first()
            balance = developer.money
            if balance:
                developer.money -= 1
                developer.save()
                return True
            else:
                return False
    
    class BookModelViewSet(ModelViewSet):
        authentication_classes = []
        permission_classes = [IsDeverloper, EnoughMoney]
        queryset = Book.objects.all()
        serializer_class = BookModelSerializer
        def get_queryset(self):
            isbn = self.request.query_params.get('isbn', 0)
            books = Book.objects.filter(isbn=int(isbn))
            queryset = books
            return queryset
    
  • 路由

    from django.conf.urls import url, include
    from . import views
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()
    router.register(r'apibook5', views.BookModelViewSet)
    urlpatterns = [
        url(r'^apibook1/', views.BookAPIView1.as_view(), name='book1'),
        url(r'^apibook2/', views.BookAPIView2.as_view(), name='book2'),
        url(r'^apibook3/', views.BookMixinView1.as_view(), name='book3'),
        url(r'^apibook4/', views.BookMixinView2.as_view(), name='book4'),
        url('', include(router.urls))
    ]
    
  • 返回数据

    [
        {
            "title": "地狱公寓",
            "isbn": "777777",
            "author": "黑色火种"
        }
    ]
    
  • 连续10次

    {
        "detail": "该充钱啦"
    }
    

    注:Django REST framework 的权限组件有一个原则,即没有认证就没有权限!加入authentication_class = []是必须的,否则虽然权限组件起作用,但权限不通过的时候detail不会显示自定义的message内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值