ESTful 架构(Representational State Transfer)
资源(Resources)
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
表现层(Representation)
URI只代表资源的实体,不代表它的形式。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
状态转化(State Transfer)
如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
理解RESTful架构 http://www.ruanyifeng.com/blog/2011/09/restful.html
RESTful API 设计指南 http://www.ruanyifeng.com/blog/2014/05/restful_api.html
簡明RESTful API設計要點 https://tw.twincl.com/programming/*641y
Django REST framework
Install:
pip install djangorestframework
pip install markdown # Markdown support for the browsable API.
pip install django-filter # Filtering support
设置(in settings)
INSTALLED_APPS = (
'rest_framework',
)
完成一个简单的接口主要在三个部分。
Serilizer (根据模型写个序列化类,将response内容序列化)
from django.contrib.auth.models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','username','email','first_name','last_name') #’__all__’
View 视图类,挑用接口的类方法。
class UserView(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Url 将视图类转化为具体接口进行调用。
from rest_framework import routers
from django.conf.urls import url, include
router = routers.DefaultRouter()
router.register('User',views.UserView)
urlpatterns = [
url(r'^', include(router.urls)),
]
这样就简单的完成了一个接口。
可调用的接口
GET POST /User/
GET PATCH PUT DELETE /User/pk/
Class ModelViewSet
(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet)
对应 create list retrieve update perform_update destroy
例子:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
Serializer 注意:
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('id', 'name')
class UserSerializer(serializers.ModelSerializer):
groups = GroupSerializer(many=True)
phone = serializers.CharField(source='profile.phone', read_only=True)
name = serializers.CharField(source='profile.name', read_only=True)
menus = serializers.SerializerMethodField()
is_active = serializers.BooleanField(source='profile.is_cms_active')
def get_menus(self, user):
return get_menus(user)
class Meta:
model = User
fields = ('id', 'username', 'name', 'email', 'phone', 'groups', 'menus', 'is_active')
外键直接可以引用其他的serializer,例如group,可以看到response中group是嵌套的
外键的属性可以使用source,例如phone
不希望修改的字段加上readonly(或者放在readonly_fields里面)
其他内容:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'common.authentication.DifferentTimeKeyAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
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(serializer.data)
filter_backends = (DjangoFilterBackend,)
filter_fields = ('status',)
class CityFilter(django_filters.FilterSet):
city = django_filters.Filter(name="city", lookup_type='name')
province = django_filters.Filter(name="city", lookup_type='province__name')
# need to include Meta filed
class Meta:
fields = ['city', 'province']