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内容。