Django REST framework 是用于构建Web API 的强大而灵活的工具包。本质上DRF是django的一个app(startproject)
DRF中文文档:http://www.sinodocs.cn/
安装:安装在想用的Python解释器下,例如:在终端cd到/python/lib/site-packages,然后执行 pip install djangorestframework
DRF的一些组件:
1 APIView (所有的功能都是基于APIView的)*****
2 解析器组件 *****
3 序列化组件 *****
4 视图组件
5 认证组件 *****
6 权限组件 *****
7 频率组件
8 url注册器组件
9 响应器组件
10 分页器组件
11 filter:过滤、查找、排序
1.APIView
views.py(导入APIView模块,继承APIView) from rest_framework.views import APIView class BookView(APIView): def get(self, request): pass def post(self, request): pass urls.py(没变化) from django.urls import path, include, re_path from classbasedview import views urlpatterns = [ re_path('login/$', views.LoginView.as_view()), ]
1. 启动django:Python manage.py runserver 127.0.0.1:8000 2. 加载settings: 2.1 加载models.py 2.2 加载views.py 2.3 加载urls.py 2.3.1 re_path('BookView/$', views.BookView.as_view()), 开始执行as_view()方法 2.3.2 urls.py加载完毕,url和视图函数之间的绑定关系已经建立好了 3. 等待用户请求 4. 接收到用户请求:127.0.0.0:8000/books/ 5. 开始查找url和视图函数之间的绑定关系,根据用户请求的url找到对应的视图函数 6. 开始执行视图函数view(request) 7. 开始执行self.dispatch() 8. 将view函数的返回结果返回给客户端浏览器
class View: http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) @classonlymethod def as_view(cls, **initkwargs): def view(request, *args, **kwargs): # 实例化一个对象,BookView的实例对象 self = cls(**initkwargs) # self表示BookView的实例化对象 # 把原始的request请求对象赋值给self.request self.request = request self.args = args self.kwargs = kwargs # view函数的返回结果就是self.dispatch() return self.dispatch(request, *args, **kwargs) # 此时的cls是BookView view.view_class = cls view.view_initkwargs = initkwargs return view class APIView(View): @classmethod def as_view(cls, **initkwargs): # cls是BookView view = super(APIView, cls).as_view(**initkwargs) # View:view view.cls = cls view.initkwargs = initkwargs # 返回一个view函数 return csrf_exempt(view) def dispatch(self, request, *args, **kwargs): try: if request.method.lower() in self.http_method_names: handler = getattr(self,request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) return self.response class BookView(APIView): def get(self, request): pass def post(self, request): pass 1. 启动django:Python manage.py runserver 127.0.0.1:8000 2. 加载settings: 2.1 加载models.py 2.2 加载views.py 2.3 加载urls.py 2.3.1 re_path('BookView/$', views.BookView.as_view()), 开始执行as_view()方法 2.3.2 urls.py加载完毕,url和视图函数之间的绑定关系已经建立好了 3. 等待用户请求 4. 接收到用户请求:127.0.0.0:8000/books/ 5. 开始查找url和视图函数之间的绑定关系,根据用户请求的url找到对应的视图函数 6. 开始执行视图函数view(request) 7. 开始执行self.dispatch() 8. 将view函数的返回结果返回给客户端浏览器
2.解析器组件
views.py(导入并继承APIView,使用解析后的数据request.data) from rest_framework.views import APIView class BookView(APIView): def get(self, request): pass def post(self, request): origin_data = request.data ... return HttpResponse({}) urls.py(无变化) from django.urls import path, include, re_path from classbasedview import views urlpatterns = [ re_path('login/$', views.LoginView.as_view()), ]
class LoginView(APIView): def get(self, request): pass def post(self, request): request.data # 新的request对象 @property return class APIView(View): @classmethod def as_view(cls, **initkwargs): pass super(APIView, cls).as_view(**initkwargs) def initialize_request(self, request, *args, **kwargs): from rest_framework.request import Request return Request( request, # 原生request对象 parsers=self.get_parsers(), # authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) def dispatch(self): pass request = self.initialize_request(request, *args, **kwargs) self.request = request def get_parsers(self): return [parser() for parser in self.parser_classes] 1. views.LoginView.as_view() 2. LoginView里面没有as_view方法,到父类APIView去找 3. 执行View里面的as_view()方法,返回view函数 def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) 4. url和视图函数之间的绑定关系建立完毕 { "login": view},等待用户请求 5. 接收到用户请求:login,到建立好的绑定关系里面执行对应的视图函数:view(request) 6. 视图函数的执行结果是什么就返回给用户什么:self.dispatch(), self.dispatch()的执行结果是什么,就返回给用户什么 7. 此时的self代表的是LoginView的实例化对象 8. 开始找dispatch方法,self里面没有,LoginView里面也没有,在APIView里面有 9. 开始执行APIView里面的dispatch 10. 最后找到http方法(GET,POST,PUT,DELETE),根据请求类型查找(request.method.lower()) 11. 开始执行找到的方法(GET),self.get(), self此时代表LoginView的实例化对象 11.1 假设接收到的是POST请求, 执行request.data 11.2 根据分析,所有的解析工作都在request.data里面实现,且data是一个方法(被@property装饰后的) 11.2 开始执行request.data @property def data(self): if not _hasattr(self, '_full_data'): self._load_data_and_files() return self._full_data 11.3 执行self._load_data_and_files 11.4 执行self._data, self._files = self._parse() 11.5 parser = self.negotiator.select_parser(self, self.parsers) 11.5.1 开始找self.parsers 11.5.2 self.get_parses() [ parse() for parse in self.parser_classes ] 11.5.2.1 parser_classes = api_settings.DEFAULT_PARSER_CLASSES 11.5.2.2 from rest_framework import api_settings 11.5.2.3 api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) 11.5.2.4 class APISettings():pass 11.5.2.5 找不到DEFAULT_PARSER_CLASSES,__getattr__ 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser' ), 11.5.2.6 首先找程序的settings 11.5.2.7 然后找rest_framework的settings 11.5.3 11.6 self._data就是我们想要的数据 11.7 DRF将self._data = data 11.8 request.data 12. 在LoginView里面找到了对应的方法,执行该方法,最后返回给用户 - DRF的所有功能都是在as_view()和dispatch里面重写的 - 而解析器组件在dispatch方法里面重写了,具体是在重新封装的Request对象里面 class ParseJson(): pass def ParseForm(): def parse(self, request): """csrfmiddlewaretoken=ef2GXp26EM0VZfpBxoQaAKcYtGqoc3wTQqtv9RwqCsa2dE7vJ02qf74aSycxMm0Z&user=&password=""" data = request.body.decode('utf-8') data.split('&') return data def select_parse(request, ParseJson, ParseForm): return
1. 启动django:Python manage.py runserver 127.0.0.1:8000 2. 加载settings: 2.1 加载models.py 2.2 加载views.py 2.3 加载urls.py 2.3.1 re_path('BookView/$', views.BookView.as_view()), 开始执行as_view()方法 2.3.2 urls.py加载完毕,url和视图函数之间的绑定关系已经建立好了 3. 等待用户请求 4. 接收到用户请求:127.0.0.0:8000/books/ POST 5. 开始self.post() 5.1 request.data触发解析操作 5.2 获取返回值 6. 将view函数的返回结果返回给客户端浏览器 class View: http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) @classonlymethod def as_view(cls, **initkwargs): def view(request, *args, **kwargs): # 实例化一个对象,BookView的实例对象 self = cls(**initkwargs) # self表示BookView的实例化对象 # 把原始的request请求对象赋值给self.request self.request = request self.args = args self.kwargs = kwargs # view函数的返回结果就是self.dispatch() return self.dispatch(request, *args, **kwargs) # 此时的cls是BookView view.view_class = cls view.view_initkwargs = initkwargs return view class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): self._request = request self.parsers = parsers or () # self.get_parsers()的执行结果 # 触发解析操作 @property def data(self): if not _hasattr(self, '_full_data'): self._load_data_and_files() return self._full_data def _load_data_and_files(self): """ Parses the request content into `self.data`. """ if not _hasattr(self, '_data'): # 开始执行self._parse() self._data, self._files = self._parse() # parsed_data if self._files: self._full_data = self._data.copy() self._full_data.update(self._files) else: self._full_data = self._data # if a form media type, copy data & files refs to the underlying # http request so that closable objects are handled appropriately. if is_form_media_type(self.content_type): self._request._post = self.POST self._request._files = self.FILES def _parse(self): """ Parse the request content, returning a two-tuple of (data, files) May raise an `UnsupportedMediaType`, or `ParseError` exception. """ media_type = self.content_type parser = self.negotiator.select_parser(self, self.parsers) if not parser: raise exceptions.UnsupportedMediaType(media_type) try: parsed = parser.parse(stream, media_type, self.parser_context) except Exception: # If we get an exception during parsing, fill in empty data and # re-raise. Ensures we don't simply repeat the error when # attempting to render the browsable renderer response, or when # logging the request or similar. self._data = QueryDict('', encoding=self._request._encoding) self._files = MultiValueDict() self._full_data = self._data raise # Parser classes may return the raw data, or a # DataAndFiles object. Unpack the result as required. try: return (parsed.data, parsed.files) except AttributeError: empty_files = MultiValueDict() return (parsed, empty_files) class APIView(View): from rest_framework.settings import api_settings parser_classes = api_settings.DEFAULT_PARSER_CLASSES @classmethod def as_view(cls, **initkwargs): # cls是BookView view = super(APIView, cls).as_view(**initkwargs) # View:view view.cls = cls view.initkwargs = initkwargs # 返回一个view函数 return csrf_exempt(view) def initialize_request(self, request, *args, **kwargs): from rest_framework.request import Request return Request( request, parsers=self.get_parsers(), ) def get_parsers(self): # [<class 'rest_framework.parsers.JSONParser'>, # <class 'rest_framework.parsers.FormParser'>, # <class 'rest_framework.parsers.MultiPartParser'>] return [parser() for parser in self.parser_classes] def dispatch(self, request, *args, **kwargs): # 初始化request,将原来的request对象传递给初始化函数 request = self.initialize_request(request, *args, **kwargs) self.request = request try: if request.method.lower() in self.http_method_names: handler = getattr(self,request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) return self.response class BookView(APIView): def get(self, request): pass def post(self, request): parsed_data = request.data
3.序列化组件(serializers)的使用及接口设计
GET 127.0.0.1:8000/books/ # 获取所有数据,返回值: [{}, {}] GET 127.0.0.1:8000/books/{id} # 获取一条数据,返回值:{} POST 127.0.0.1:8000/books/ # 新增一条数据,返回值:{} PUT 127.0.0.1:8000/books/{id} # 修改数据,返回值:{} DELETE 127.0.0.1:8000/books/{id} # 删除数据,返回空
get接口使用序列化 - 导入模块:from rest_framework import serializers - 建立一个序列化类 class BookSerializer(serializers.Serializer): # 字段可以自定义 nid = serializers.CharField(max_length=10) title = serializers.CharField(max_length=20) publish_name = serializers.CharField(read_only=True, source="publish.name") authors_list = serializers.SerializerMethodField() def get_authors_list(self, book_obj): pass - 获取queryset origin_data = Book.objects.all() - 开始序列化 serialized_data = BookSerializer(origin_data, many=True) - 获取序列化后的数据,返回给客户端 return Response(serialized_data.data)
models.py from django.db import models class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) # 外键字段 publish = models.ForeignKey(to="Publish", related_name="book", related_query_name="book_query", on_delete=models.CASCADE) # 多对多字段 authors = models.ManyToManyField(to="Author") urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view()), ] app_serializers.py from rest_framework import serializers from .models import Book # 第二步, 创建一个序列化类,字段类型不一定要跟models的字段一致 class BookSerializer(serializers.Serializer): # nid = serializers.CharField(max_length=32) title = serializers.CharField(max_length=128) price = serializers.DecimalField(max_digits=5, decimal_places=2) publish = serializers.CharField() # 外键字段, 显示__str__方法的返回值 publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name') publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') # authors = serializers.CharField(max_length=32) # book_obj.authors.all() # 多对多字段需要自己手动获取数据,SerializerMethodField() authors_list = serializers.SerializerMethodField() def get_authors_list(self, book_obj): author_list = list() for author in book_obj.authors.all(): author_list.append(author.name) return author_list views.py # 第一步,导入模块 from rest_framework.views import APIView from rest_framework.response import Response from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookView(APIView): def get(self, request): # 第三步,获取queryset origin_data = Book.objects.all() # 第四步,开始序列化 serialized_data = BookSerializer(origin_data, many=True) return Response(serialized_data.data)
post接口 使用序列化 流程 - 导入模块:from rest_framework import serializers - 建立一个序列化类 问题: 1. serializers.Serializer无法插入数据,只能自己实现create 2. 字段太多,不能自动序列化 所以使用serializers.ModelSerializer class BookSerializer(serializers.ModelSerializer): 字段可以自定义 - 获取客户端请求数据 - 开始序列化 serialized_data = BookSerializer(origin_data, many=True) - 写入数据库 - 获取序列化后的数据,返回给客户端 return Response(serialized_data.data)
models.py from django.db import models class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) # 外键字段 publish = models.ForeignKey(to="Publish", related_name="book", related_query_name="book_query", on_delete=models.CASCADE) # 多对多字段 authors = models.ManyToManyField(to="Author") urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view()), ] app_serializers.py # 导入模块 from rest_framework import serializers from .models import Book """ # 创建一个序列化类,字段类型不一定要跟models的字段一致 class BookSerializer(serializers.Serializer): # nid = serializers.CharField(max_length=32) title = serializers.CharField(max_length=128) price = serializers.DecimalField(max_digits=5, decimal_places=2) publish = serializers.CharField() # 外键字段, 显示__str__方法的返回值 publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name') publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') # authors = serializers.CharField(max_length=32) # book_obj.authors.all() # 多对多字段需要自己手动获取数据,SerializerMethodField() authors_list = serializers.SerializerMethodField() def get_authors_list(self, book_obj): author_list = list() for author in book_obj.authors.all(): author_list.append(author.name) return author_list def create(self, validated_data): # {'title': 'Python666', 'price': Decimal('66.00'), 'publish': '2'} validated_data['publish_id'] = validated_data.pop('publish') book = Book.objects.create(**validated_data) return book """ class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ('title', 'price', 'publish', 'authors', 'author_list', 'publish_name', 'publish_city' ) extra_kwargs = { 'publish': {'write_only': True}, 'authors': {'write_only': True} } publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name') publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') author_list = serializers.SerializerMethodField() def get_author_list(self, book_obj): # 拿到queryset开始循环 [{}, {}, {}, {}] authors = list() for author in book_obj.authors.all(): authors.append(author.name) return authors views.py from rest_framework.views import APIView from rest_framework.response import Response from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookView(APIView): def post(self, request): verified_data = BookSerializer(data=request.data) if verified_data.is_valid(): book = verified_data.save() authors = Author.objects.filter(nid__in=request.data['authors']) book.authors.add(*authors) return Response(verified_data.data) else: return Response(verified_data.errors)
- get单条数据接口设计 1. 定义url 2. 获取数据对象 3. 开始序列化:serialized_data = BookSerializer(book_obj, many=False) 4. 返回数据:serialized_data.data - delete - put 1. 定义url 2. 获取数据对象 book_obj = Book.objects.get(pk=1) 3. 开始序列化(验证数据,save()) verified_data = BookSerializer(instance=book_obj, many=False) 4. 验证成功写入数据库,验证失败返回错误 verified_data.is_valid()
models.py(同上) app_serializers.py(同上) urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), ] views.py from rest_framework.views import APIView from rest_framework.response import Response from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookFilterView(APIView): def get(self, request, nid): book_obj = Book.objects.get(pk=nid) serialized_data = BookSerializer(book_obj, many=False) return Response(serialized_data.data) def put(self, request, nid): book_obj = Book.objects.get(pk=nid) verified_data = BookSerializer(data=request.data, instance=book_obj) if verified_data.is_valid(): verified_data.save() return Response(verified_data.data) else: return Response(verified_data.errors) def delete(self, request, nid): book_obj = Book.objects.get(pk=nid).delete() return Response()
models.py(同上) app_serializers.py(同上) urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view()), re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), ] views.py from rest_framework.mixins import ( ListModelMixin, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin ) from rest_framework.generics import GenericAPIView from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookView(ListModelMixin, CreateModelMixin, GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer 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 BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer def get(self, request, *args, **kwargs): # print(self.kwargs) {'pk': '1'} return self.retrieve(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs)
models.py(同上) app_serializers.py(同上) urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view()), re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), ] views.py from rest_framework import generics from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookView(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializer class BookFilterView(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer
models.py(同上) app_serializers.py(同上) urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view({ 'get': 'list', 'post': 'create' })), re_path(r'books/(?P<pk>\d+)/$', views.BookView.as_view({ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' })) ] views.py from rest_framework.viewsets import ModelViewSet from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer
4.视图组件(mixin,genericview,viewset)
视图组件是用来优化接口逻辑的
(1) 使用视图组件的mixin进行接口逻辑优化
- 导入mixin from rest_framework.mixinx import ( ListModelMix, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin ) from rest_framework.generics import GenericAPIView - 定义序列化类 Class BookSerializer(serializers.ModelSerializer): class Meta: Book fields = () extra_kwargs = {"field_name": {"write_only": True}} - 导入序列化类 from .app_serializers import BookSerializer - 定义视图类 class BookView(ListModelMix, CreateModelMixin, GenericAPIView): # queryset和serializer_class是固定的写法 queryset = Book.objects.all() serializer_class = BookSerializer def get(): return self.list() def post(): return self.create() class BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer def get(): return self.retrieve() def delete(): return self.destroy() def put(): return self.update() 注意:单条数据操作的url是这样的:re_path(r'books/(?P<pk>\d+)/$, views.BookFilterView.as_view())
models.py(同上) app_serializers.py(同上) urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view()), re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), ] views.py from rest_framework.mixins import ( ListModelMixin, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin ) from rest_framework.generics import GenericAPIView from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookView(ListModelMixin, CreateModelMixin, GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer 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 BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer def get(self, request, *args, **kwargs): # print(self.kwargs) {'pk': '1'} return self.retrieve(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs)
(2) 使用视图组件的view进行接口逻辑优化
- 导入模块 from rest_framework import generics - 写视图类 class BookView(generics.ListCreateAPIView) queryset = Book.objects.all() serializer_class = BookSerializer class BookFilterView(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer
models.py(同上) app_serializers.py(同上) urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view()), re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), ] views.py from rest_framework import generics from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookView(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializer class BookFilterView(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer
(3) 使用视图组件的viewset进行接口逻辑优化
- 导入模块 from rest_framework.viewsets import ModelViewSet - 设计url re_path(r'books/$, views.BookView.as_view({ 'get': 'list', 'post': 'create' })), re_path(r'books/(?P<pk>\d+)/$', views.BookView.as_view({ 'get': 'retrieve', 'delete': 'destroy', 'put': 'update' })) - 设计视图类 class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer
models.py(同上) app_serializers.py(同上) urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view({ 'get': 'list', 'post': 'create' })), re_path(r'books/(?P<pk>\d+)/$', views.BookView.as_view({ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' })) ] views.py from rest_framework.viewsets import ModelViewSet from .models import ( Book, Publish, Author, ) from .app_serializers import BookSerializer class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response from api.models import * from api.utils.serializer import ( CourseSerializer, CourseDetailSerializer, CourseCategorySerializer ) from api.utils.auth import LoginAuth from api.utils.filter import CourseFilter class CourseView(ModelViewSet): authentication_classes = [LoginAuth] queryset = Course.objects.all() serializer_class = CourseSerializer filter_backends = [CourseFilter, ] def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response({"code": 0, "data": serializer.data}) class CourseDetailView(ModelViewSet): authentication_classes = [LoginAuth] queryset = CourseDetail.objects.all() serializer_class = CourseDetailSerializer class CourseCategoryView(ModelViewSet): queryset = CourseCategory.objects.all() serializer_class = CourseCategorySerializer def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) serializer = self.get_serializer(queryset, many=True) return Response({"error_no": 0, "data": serializer.data})
5.认证组件(token)
1 使用方式介绍 - 定义一个认证类 class UserAuth(): def authenticate_header(self, request): pass def authenticate(self, request): pass - 在需要认证的数据接口里面指定认证类 class BookView(ModelViewSet): authentication_classes = [UserAuth] queryset = Book.objects.all() serializer_class = BookSerializer 2 源码剖析 - self.dispatch() # self BookView的实例化对象 - self.initial() # self BookView的实例化对象 - self.perform_authentication() # self BookView的实例化对象 - request.user # self Request的实例化对象 - self._authenticate() # self Request的实例化对象 self.user = xxx
app_serializers.py(同上) models.py(多加了一张UserToken表,与User表一对一) from django.db import models class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) user_type_entry = ( (1, 'Delux'), (2, 'SVIP'), (3, 'VVIP') ) user_type = models.IntegerField(choices=user_type_entry) class UserToken(models.Model): user = models.OneToOneField('User', on_delete=models.CASCADE) token = models.CharField(max_length=128) class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) # 外键字段 publish = models.ForeignKey(to="Publish", related_name="book", related_query_name="book_query", on_delete=models.CASCADE) # 多对多字段 authors = models.ManyToManyField(to="Author") urls.py from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view({ 'get': 'list', 'post': 'create' })), re_path(r'books/(?P<pk>\d+)/$', views.BookView.as_view({ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' })), re_path(r'user/$', views.UserView.as_view()), ] utils/get_token.py(获取随机token数值) import uuid def generate_token(): random_str = str(uuid.uuid4()).replace('-', '') return random_str authentication_classes.py(认证类) from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import APIException from .models import UserToken # 第一步:定义认证类 class UserAuth(BaseAuthentication): # 所有的认证逻辑都在authenticate def authenticate(self, request): user_token = request.query_params.get("token") try: token = UserToken.objects.get(token=user_token) # 后面权限会用到以下两值(其实此处可返回任意两值) return token.user, token.token except Exception: raise APIException("没有认证") views.py from django.http import JsonResponse from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet from .models import ( Book, Publish, Author, User, UserToken ) from .app_serializers import BookSerializer from .utils import get_token from .authentication_classes import UserAuth class BookView(ModelViewSet): # 第二步:指定认证类 authentication_classes = [UserAuth] queryset = Book.objects.all() serializer_class = BookSerializer class UserView(APIView): def post(self, request): # 定义返回消息体 response = dict() # 定义需要的用户信息 fields = {"username", "password"} # 定义一个用户信息字典 user_info = dict() if fields.issubset(set(request.data.keys())): # username = request.data.get("username") # password = request.data.get("password") for key in fields: user_info[key] = request.data[key] user_instance = User.objects.filter(**user_info).first() if user_instance is not None: access_token = get_token.generate_token() UserToken.objects.update_or_create(user=user_instance, defaults={ 'token': access_token }) response["status_code"] = 200 response["status_message"] = "登录成功" response["access_token"] = access_token response["user_role"] = user_instance.get_user_type_display() else: response["status_code"] = 201 response["status_message"] = "登录失败,用户名或密码错误" return JsonResponse(response)
全局认证需在settings.py中加上以下代码: REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'serializer.authentication_classes.UserAuth', ) } models.py(同上) app_serializers.py(同上) urls.py(同上) utils/get_token.py(同上) authentication_classes.py(同上) views.py from django.http import JsonResponse from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet from .models import ( Book, Publish, Author, User, UserToken ) from .app_serializers import BookSerializer from .utils import get_token from .authentication_classes import UserAuth class BookView(ModelViewSet): # 全局认证这里无需再指定认证类,因为在settings中已指定全局认证类 # authentication_classes = [UserAuth] queryset = Book.objects.all() serializer_class = BookSerializer class UserView(APIView): def post(self, request): # 定义返回消息体 response = dict() # 定义需要的用户信息 fields = {"username", "password"} # 定义一个用户信息字典 user_info = dict() if fields.issubset(set(request.data.keys())): # username = request.data.get("username") # password = request.data.get("password") for key in fields: user_info[key] = request.data[key] user_instance = User.objects.filter(**user_info).first() if user_instance is not None: access_token = get_token.generate_token() UserToken.objects.update_or_create(user=user_instance, defaults={ 'token': access_token }) response["status_code"] = 200 response["status_message"] = "登录成功" response["access_token"] = access_token response["user_role"] = user_instance.get_user_type_display() else: response["status_code"] = 201 response["status_message"] = "登录失败,用户名或密码错误" return JsonResponse(response)
# 定义认证类 class UserAuth(BaseAuthentication): # 所有的认证逻辑都在authenticate def authenticate(self, request): user_token = request.query_params.get("token") try: token = UserToken.objects.get(token=user_token) # 多个认证类时,只需在最后一个认证类中返回值即可 return token.user, token.token except Exception: raise APIException("没有认证") # 指定认证类 authentication_classes = [UserAuth,...]
6.权限组件
- 定义一个权限类 class UserPerm(): def has_permission(self, request, view): return None - 指定权限验证类 class BookView(APIView): authentication_classes = [UserAuth] permission_classes = [UserPerm] queryset = Book.objects.all() serializer_class = BookSerializer
models.py(同上) app_serializers.py(同上) urls.py(同上) utils/get_token.py(获取随机token数值)(同上) authentication_classes.py(认证类)(同上) views.py from django.http import JsonResponse from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet from .models import ( Book, Publish, Author, User, UserToken ) from .app_serializers import BookSerializer from .utils import get_token from .authentication_classes import UserAuth class UserPerm(): message = "您没有查看该数据的权限!" def has_permission(self, request, view): if request.user.user_type == 3: return True return False class BookView(ModelViewSet): # 指定认证类 authentication_classes = [UserAuth] # 指定权限类 permission_classes = [UserPerm] queryset = Book.objects.all() serializer_class = BookSerializer class UserView(APIView): def post(self, request): # 定义返回消息体 response = dict() # 定义需要的用户信息 fields = {"username", "password"} # 定义一个用户信息字典 user_info = dict() if fields.issubset(set(request.data.keys())): # username = request.data.get("username") # password = request.data.get("password") for key in fields: user_info[key] = request.data[key] user_instance = User.objects.filter(**user_info).first() if user_instance is not None: access_token = get_token.generate_token() UserToken.objects.update_or_create(user=user_instance, defaults={ 'token': access_token }) response["status_code"] = 200 response["status_message"] = "登录成功" response["access_token"] = access_token response["user_role"] = user_instance.get_user_type_display() else: response["status_code"] = 201 response["status_message"] = "登录失败,用户名或密码错误" return JsonResponse(response)
7.频率组件
-导入模块 from rest_framework.throttling import SimpleRateThrottle -定义并继承SimpleRateThrottle class RateThrottle(SimpleRateThrottle): # 指定访问频率 rate = '5/m' # 指定通过什么方式来区分用户 def get_cache_key(self, request, view): return self.get_ident(request) -指定频率类 class BookView(APIView): throttle_classes = [RateThrottle]
utils/app_throttles.py from rest_framework.throttling import SimpleRateThrottle class RateThrottle(SimpleRateThrottle): # 指定访问频率 rate = "5/m" # 指定通过什么方式来区分用户 def get_cache_key(self, request, view): return self.get_ident(request) views.py from rest_framework.viewsets import ModelViewSet from .models import Book from .app_serializers import BookSerializer from .utils import app_throttles class BookView(ModelViewSet): # authentication_classes = [UserAuth] # permission_classes = [UserPerm] # 指定频率类 throttle_classes = [app_throttles.RateThrottle] queryset = Book.objects.all() serializer_class = BookSerializer
-导入模块 from rest_framework.throttling import SimpleRateThrottle -定义并继承SimpleRateThrottle class RateThrottle(SimpleRateThrottle): # 指定访问频率 scope = 'visit_rate' # 指定通过什么方式来区分用户 def get_cache_key(self, request, view): return self.get_ident(request) -在settings里指定频率类和访问频率 REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": ('serializer.utils.app_throttles.RateThrottle',), "DEFAULT_THROTTLE_RATES": { "visit_rate": "5/m" } }
全局使用频率组件需要在settings里指定频率类和访问频率 REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": ('serializer.utils.app_throttles.RateThrottle',), "DEFAULT_THROTTLE_RATES": { "visit_rate": "5/m" } } utils/app_throttles.py from rest_framework.throttling import SimpleRateThrottle class RateThrottle(SimpleRateThrottle): # 指定访问频率 scope = 'visit_rate' # 指定通过什么方式来区分用户 def get_cache_key(self, request, view): return self.get_ident(request) views.py(不需要在views中指定频率类,因为在settings里已经配置了频率类和访问频率)
8.url注册器组件
-导入模块 from django.urls import re_path, include from serializer import views from rest_framework import routers -生成一个注册器实例对象 router = routers.DefaultRouter() -将需要自动生成url的接口注册 router.register(r"books", views.BookView) -开始自动生成url urlpattern = [ re_path('^', include(router.urls)), ]
urls.py from django.urls import re_path, include from serializer import views from rest_framework import routers router = routers.DefaultRouter() router.register('books', views.BookView) urlpatterns = [ re_path('^', include(router.urls)), ]
9.响应器组件
-导入模块 from rest_framework.views import APIView from rest_framework.renderers import JSONRender -指定返回类 class BookView(APIView): render_classes = [JsonRender]
10.分页器组件
分页器组件的局部实现 -导入模块 from rest_framework.pagination import PageNumberPagination -自定义一个分页类并继承PageNumberPagination class MyPagination(PageNumberPagination): page_size = 2 page_query_param = 'p' page_size_query_param = 'size' max_page_size = 5 -实例化一个分页类对象 paginater = MyPagination() -开始分页 paged_books = paginater.paginate_queryset(books, request) -开始序列化 serialized_books = BookSerializer(paged_books, many=True) -返回数据 return Response(serialized_books.data)
utils/app_paginates.py from rest_framework.pagination import PageNumberPagination class MyPagination(PageNumberPagination): page_size = 3 page_query_param = 'page' page_size_query_param = 'size' max_page_size = 5 views.py from rest_framework.viewsets import ModelViewSet from .models import Book from .app_serializers import BookSerializer from .utils import app_paginates class BookView(ModelViewSet): pagination_class = app_paginates.MyPagination queryset = Book.objects.all() serializer_class = BookSerializer
注:全局 使用分页器组件 需要在settings里配置如下代码: REST_FRAMEWORK = { "PAGE_SIZE": 4 } -导入模块 from rest_framework.pagination import PageNumberPagination -自定义一个分页类并继承PageNumberPagination class MyPagination(PageNumberPagination): # page_size = 2 page_query_param = 'p' page_size_query_param = 'size' max_page_size = 5 -实例化一个分页类对象 paginater = MyPagination() -开始分页 paged_books = paginater.paginate_queryset(books, request) -开始序列化 serialized_books = BookSerializer(paged_books, many=True) -返回数据 return Response(serialized_books.data)
注意:在settings里配置如下代码: REST_FRAMEWORK = { "PAGE_SIZE": 4 } utils/app_paginates.py from rest_framework.pagination import PageNumberPagination class MyPagination(PageNumberPagination): page_query_param = 'page' page_size_query_param = 'size' max_page_size = 5 views.py from rest_framework.viewsets import ModelViewSet from .models import Book from .app_serializers import BookSerializer from .utils import app_paginates class BookView(ModelViewSet): pagination_class = app_paginates.MyPagination queryset = Book.objects.all() serializer_class = BookSerializer
11.filter
字段过滤
utils/filter.py(自定义的过滤器) from rest_framework.filters import BaseFilterBackend class CourseFilter(BaseFilterBackend): """ 课程展示 过滤器 """ def filter_queryset(self, request, queryset, view): extra = {} # request.query_params等同于request.GET category_id = str(request.query_params.get("category_id")) # 如果分类ID不是数字或分类ID传输的为0 if not category_id.isdigit() or category_id == "0": extra = extra else: extra.update({"course_category_id": category_id}) return queryset.filter(**extra) views/course.py from rest_framework.viewsets import ModelViewSet from rest_framework.response import Response from api.models import Course from api.utils.serializer import CourseSerializer from api.utils.auth import LoginAuth from api.utils.filter import CourseFilter class CourseView(ModelViewSet): # authentication_classes = [LoginAuth] queryset = Course.objects.all() serializer_class = CourseSerializer filter_backends = [CourseFilter, ] def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response({"code": 0, "data": serializer.data})
一般过滤可以重写get_queryset方法实现,这时候就可以去掉queryset这个属性了:
class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): serializer_class = GoodsSerializer pagination_class = GoodsPagination # 覆写GenericAPIView下的该方法,以实现自己对query_set的返回加一些逻辑 def get_queryset(self): # 获取url参数中传过来的"要查询的商品的价格阈值",如果没有传就设置为0 price_min = self.request.query_params.get("price_min", 0) return Goods.objects.filter(shop_price__gt=price_min).order_by("shop_price")
现在就可以通过在url里指定参数price_min来实现对获取到的商品价格的过滤了。
注意在使用这种方法时,要在router.register里配置base_name,不然运行不了。
还可以使用django-filter,这个要把它注册为app,然后就能用了(因为这时候不需要重写get_queryset方法了,所以就需要把queryset这个属性拿回来)。然后可以自定义一个filters.py用来写自定义过滤规则的过滤器:
from django_filters import rest_framework as filters from .models import Goods class GoodsFilter(filters.FilterSet): """商品的过滤类""" # 区间查询,指定区间的最大最小值 min_price = filters.NumberFilter(field_name="shop_price", lookup_expr='gte') max_price = filters.NumberFilter(field_name="shop_price", lookup_expr='lte') # 模糊查询,这里带i是忽略大小写 name = filters.CharFilter(field_name="name", lookup_expr="icontains") class Meta: model = Goods fields = ['min_price', 'max_price', 'name']
然后在views.py的相关视图类里指定filter_class为这个自定义的过滤器类:
from .filters import GoodsFilter class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): serializer_class = GoodsSerializer pagination_class = GoodsPagination filter_backends = (DjangoFilterBackend,) queryset = Goods.objects.all() # filter_fields = ('name', 'shop_price') filter_class = GoodsFilter
在这个例子里配置完了之后,效果就是可以在url里指定min_price和max_price的值,来控制所返回的JSON中上shop_price值所在的区间,实现区间过滤;使用name实现模糊查询。
查找
要实现查找,只要在视图层配置filters.SearchFilter和search_fields=要查找的字段元组即可:
from rest_framework import filters class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): serializer_class = GoodsSerializer pagination_class = GoodsPagination filter_backends = (DjangoFilterBackend, filters.SearchFilter) queryset = Goods.objects.all() # filter_fields = ('name', 'shop_price') filter_class = GoodsFilter search_fields = ('name', 'goods_brief', 'goods_desc')
一些符号可以实现复杂的查找方式,比如以。。开头,全文搜索之类的。
排序
要实现排序,只要在视图层配置filters.OrderingFilter和ordering_fields=要排序的字段即可:
class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): serializer_class = GoodsSerializer pagination_class = GoodsPagination filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) queryset = Goods.objects.all() # filter_fields = ('name', 'shop_price') filter_class = GoodsFilter search_fields = ('name', 'goods_brief', 'goods_desc') ordering_fields = ('sold_num', 'add_time')
使用REST框架的一些原因:
- Web浏览API对于开发人员来说是一个巨大的可用性。
- 认证策略包括OAuth1a和OAuth2的包。
- 支持ORM和非ORM数据源的序列化。
- 如果你不需要更强大的功能,就可以使用常规的基于功能的视图。
- 广泛的文档和良好的社区支持。
- 包括Mozilla、Red Hat、Heroku和Eventbrite在内的国际知名公司使用和信任。
Funding
REST framework is a collaboratively(合作地) funded project(基金项目). If you use REST framework commercially we strongly encourage you to invest(投资) in its continued development(可持续发展) by signing up for a paid plan.(注册付费计划)
Every single sign-up helps us make REST framework long-term financially sustainable(财务上可持续发展)
Many thanks to all our wonderful sponsors(赞助商), and in particular to our premium backers(优质的支持者), Rover, Sentry, Stream, Machinalis, and Rollbar.
Requirements
REST framework requires the following:
- Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)
- Django (1.10, 1.11, 2.0 alpha)
The following packages are optional:
- coreapi (1.32.0+) - Schema generation support.
- Markdown (2.1.0+) - Markdown support for the browsable API.
- django-filter (1.0.1+) - Filtering support.
- django-crispy-forms - Improved HTML display for filtering.
- django-guardian (1.1.1+) - Object level permissions support.
以下软件包是可选的:
- coreapi(1.32.0+) - 支持模式生成。
- Markdown(2.1.0+) - 可浏览API的Markdown支持。
- django-filter(1.0.1+) - 过滤支持。
- django-crispy-forms - 改进的HTML显示过滤。
- django-guardian(1.1.1+) - 对象级权限支持。
Installation
Install using pip
, including any optional packages you want...
1
2
3
|
pip install djangorestframework pip install markdown # Markdown support for the browsable API. pip install django - filter # Filtering support |
...or clone the project from github.
1
|
git clone git@github.com:encode / django - rest - framework.git |
Add 'rest_framework'
to your INSTALLED_APPS
setting.(记得在setting文件里面添加rest_framework,当然,你还得先安装djangorestframework)
1
2
3
4
|
INSTALLED_APPS = ( ... 'rest_framework' , ) |
If you're intending to use the browsable API you'll probably also want to add REST framework's login and logout views. Add the following to your root urls.py
file.
如果您打算使用可浏览的API,您可能还需要添加REST框架的登录和注销视图。将以下内容添加到您的根urls.py
文件中。
1
2
3
4
|
urlpatterns = [ ... url(r '^api-auth/' , include( 'rest_framework.urls' , namespace = 'rest_framework' )) ] |
Note that the URL path can be whatever you want, but you must include 'rest_framework.urls'
with the 'rest_framework'
namespace. You may leave out the namespace in Django 1.9+, and REST framework will set it for you.
请注意,URL路径可以是任何你想要的,但你必须包括'rest_framework.urls'
与'rest_framework'
命名空间。您可以在Django 1.9+中省略命名空间,REST框架将为您设置。
Quickstart
Can't wait to get started? The quickstart guide is the fastest way to get up and running, and building APIs with REST framework.
说了一堆,直接来个demo,快速上手,看看效果。官网请看:http://www.django-rest-framework.org/tutorial/quickstart/
首先肯定得先创建django程序啦,接着创建APP,这里我创建了一个quickstart的app。
Now sync your database for the first time:同步数据库
1
|
python manage.py migrate |
创建超级用户用于登陆。We'll also create an initial user named admin
with a password of password123
. We'll authenticate as that user later in our example.
1
|
python manage.py createsuperuser |
Serializers
首先我们要定义一些序列化程序。在quickstart这个APP下创建serializers文件,用于展示数据。
First up we're going to define some serializers. Let's create a new module named tutorial/quickstart/serializers.py
that we'll use for our data representations.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from django.contrib.auth.models import User, Group from rest_framework import serializers class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User fields = ( 'url' , 'username' , 'email' , 'groups' ) class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ( 'url' , 'name' ) |
Notice that we're using hyperlinked relations in this case, with HyperlinkedModelSerializer
. You can also use primary key and various other relationships, but hyperlinking is good RESTful design.
请注意,在这种情况下,我们正在使用超链接关系HyperlinkedModelSerializer
。您还可以使用主键和各种其他关系,但超链接是好的RESTful设计。
Views
Right, we'd better write some views then. Open tutorial/quickstart/views.py
and get typing. 写一些视图,查询数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from django.contrib.auth.models import User, Group from rest_framework import viewsets from tutorial.quickstart.serializers import UserSerializer, GroupSerializer class UserViewSet(viewsets.ModelViewSet): """ API endpoint that allows users to be viewed or edited. """ queryset = User.objects. all ().order_by( '-date_joined' ) serializer_class = UserSerializer class GroupViewSet(viewsets.ModelViewSet): """ API endpoint that allows groups to be viewed or edited. """ queryset = Group.objects. all () serializer_class = GroupSerializer |
Rather than write multiple views we're grouping together all the common behavior into classes called ViewSets
.
We can easily break these down into individual views if we need to, but using viewsets keeps the view logic nicely organized as well as being very concise.
我们不是编写多个视图,而是将所有常见的行为组合到一个名为viewset的类中。
如果需要的话,我们可以很容易地将它们分解为单独的视图,但是使用viewset使视图逻辑组织得很好,并且非常简洁。
URLs
Okay, now let's wire up the API URLs. On to tutorial/urls.py
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from django.conf.urls import url, include from rest_framework import routers from tutorial.quickstart import views <br> router = routers.DefaultRouter() router.register(r 'users' , views.UserViewSet) router.register(r 'groups' , views.GroupViewSet) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ url(r '^' , include(router.urls)), url(r '^api-auth/' , include( 'rest_framework.urls' , namespace = 'rest_framework' )) ] |
Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.
我们可以通过简单地使用路由器类注册该视图来自动生成API的URL conf。
Again, if we need more control over the API URLs we can simply drop down to using regular class-based views, and writing the URL conf explicitly.
再次,如果我们需要对API URL的更多控制,我们可以简单地将其下拉到使用常规的基于类的视图,并明确地编写URL conf。
Finally, we're including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browsable API.
最后,我们将包括默认登录和注销视图,以便与可浏览的API一起使用。这是可选的,但如果您的API需要身份验证,并且您想要使用可浏览的API,那么这是非常有用的。
Settings
We'd also like to set a few global settings. We'd like to turn on pagination, and we want our API to only be accessible to admin users. The settings module will be in tutorial/settings.py
我们也想设置一些全局设置。我们想打开分页,我们希望我们的API只能由管理员使用
1
2
3
4
5
6
7
8
9
10
11
|
INSTALLED_APPS = ( ... 'rest_framework' , ) REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES' : [ 'rest_framework.permissions.IsAdminUser' , ], 'PAGE_SIZE' : 10 } |
Okay, we're done.
效果图:
主界面,好像啥也没有……
用超级用户登陆后的界面。
有增删改查的功能。
快速了解REST framework组件
接下来了解下rest framework 的所有组件,并且得知它们是如何组合在一起的,这是非常值得去学习的。
- 1 - Serialization 序列化
- 2 - Requests & Responses 请求 & 响应
- 3 - Class-based views 基于类的视图
- 4 - Authentication & permissions 身份验证 & 权限
- 5 - Relationships & hyperlinked APIs
- 6 - Viewsets & routers 视图和路由
- 7 - Schemas & client libraries 模式和客户端库
Serialization 序列化
这里不对普通的序列化作介绍。接下来使用 ModelSerializers model序列化让代码更少,更简洁。Django提供了form和modelform一样,REST框架包括了序列化器类和模型序列化器类。
例如 models 文件中有一个关于文章的表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class Article(models.Model): """文章资讯""" title = models.CharField(max_length = 255 , unique = True , db_index = True , verbose_name = "标题" ) source = models.ForeignKey( "ArticleSource" , verbose_name = "来源" ) article_type_choices = (( 0 , '资讯' ), ( 1 , '视频' )) article_type = models.SmallIntegerField(choices = article_type_choices, default = 0 ) brief = models.TextField(max_length = 512 , verbose_name = "摘要" ) head_img = models.CharField(max_length = 255 ) content = models.TextField(verbose_name = "文章正文" ) pub_date = models.DateTimeField(verbose_name = "上架日期" ) offline_date = models.DateTimeField(verbose_name = "下架日期" ) status_choices = (( 0 , '在线' ), ( 1 , '下线' )) status = models.SmallIntegerField(choices = status_choices, default = 0 , verbose_name = "状态" ) order = models.SmallIntegerField(default = 0 , verbose_name = "权重" , help_text = "文章想置顶,可以把数字调大" ) comment_num = models.SmallIntegerField(default = 0 , verbose_name = "评论数" ) agree_num = models.SmallIntegerField(default = 0 , verbose_name = "点赞数" ) view_num = models.SmallIntegerField(default = 0 , verbose_name = "观看数" ) collect_num = models.SmallIntegerField(default = 0 , verbose_name = "收藏数" ) tags = models.ManyToManyField( "Tags" , blank = True , verbose_name = "标签" ) date = models.DateTimeField(auto_now_add = True , verbose_name = "创建日期" ) def __str__( self ): return "%s-%s" % ( self .source, self .title) |
接下来,只需要写一个序列化器,便可以轻松对数据的进行获取,而且代码看起来特别简洁。
1
2
3
4
5
6
7
8
9
10
11
12
|
# 在 serilallzer.py 文件可以这样写 # 如果想使用哪个model进行序列化,照此类推即可 # fields 如果想要获取所有字段, 使用"__all__" # fields 如果只是想要获取一部分数据呢, 那么在 fields 中加入所序列化的model的字段即可 from rest_framework.serializers import ModelSerializer class ArticleSerializer(ModelSerializer): class Meta: model = models.Article fields = ( "id" , "title" , "article_type" , "content" , ) or "__all__" |