安装django及djangorestframework
pip install django
pip install djangorestframework
(注:本文环境python2.7.9, django 1.9.2, djangorestframework 3.3.3)
创建django rest project及app
与创建django项目方法一样
修改setting.py
INSTALLED_APPS添加rest_framework
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
...
]
添加rest全局配置REST_FRAMEWORK
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
'PAGE_SIZE': 10
}
添加自己的model
models.py文件中定义question model:
class Question(mongoengine.document.Document):
question = mongoengine.fields.StringField(max_length = 10240, required = True)
answer = mongoengine.fields.BooleanField(required = True)
reason = mongoengine.fields.StringField(max_length = 1024)
meta = {"db_alias": "wdbk", 'clooection': 'question'}
定义序列化类
新建serializers.py文件,定义序列化类,有三种方法:
(1) 继承于rest_framework.serializers.Serializer
class QuestionSerializer(serializers.Serializer):
def create(self, validated_data):
return Question.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.question = validated_data.get('question', instance.question)
instance.answer = validated_data.get('answer', instance.answer)
instance.reason = validated_data.get('reason', instance.reason)
instance.save()
return instance
(2) 继承 于rest_framework.serializers.ModelSerializer
class QuestionSerializer(<span style="font-family: Arial, Helvetica, sans-serif;">serializers.ModelSerializer</span>):
class Meta:
model = Question
fields = ('question', 'answer', 'reason')
(3) 在视图中自己实现序列化
定义REST视图
有以下几种方法实现rest视图:
(1) 函数视图
自定义函数,使用api_view装饰器,在函数内部处理GET、POST等请求
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from models import Question
from serializers import QuestionSerializer
@api_view(['GET', 'POST'])
def question_list(request):
if request.method == 'GET':
questions = Question.objects.all()
serializer = QuestionSerializer(questions, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = QuestionSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
然后在urls.py中加入视图函数即可
(2) 基于类的视图
定义类继承于APIView,并实现get, post等方法
from models import Question
from serializers import QuestionSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class QuestionList(APIView):
def get(self, request, format=None):
questions = Question.objects.all()
serializer = QuestionSerializer(questions, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = QuestionSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
在urls.py中添加视图类
urlpatterns += [
url(r'^questions/$', views.QuestionList.as_view()),
]
(3)使用ViewSet
参考
http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#tutorial-6-viewsets-routers
下面是我的ViewSet
from models import Question
from permission import IsOwnerOrReadOnly
from rest_framework.response import Response
from rest_framework import status
class QuestionViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`, `update` and `destroy` actions.
Additionally we also provide an extra `initialization` and `random` action.
"""
queryset = Question.objects.all()
serializer_class = QuestionSerializer
#permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
# IsOwnerOrReadOnly,)
@list_route()
def initialization(self, request):
try:
excel = xlrd.open_workbook(settings.STATIC_ROOT + '/data/question.xls')
sheet_question = excel.sheet_by_name('question')
except Exception, e:
return Response(data={'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
try:
Question.objects().delete()
for i in range(1, sheet_question.nrows):
values = sheet_question.row_values(i)
c = Question.objects(question=values[0])
c.update(upsert = True,
set__question = values[0],
set__answer = bool(values[1]),
set__reason = values[2])
except Exception, e:
return Response(data={'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response(status=status.HTTP_201_CREATED)
def get_object(self):
pk = self.kwargs.get('pk')
question = Question.objects(question=pk)
#if question:
return question[0]
@list_route()
def random(self, request):
total = Question.objects.count()
# 随机生成index
randimskip = random.randint(1, total-1)
# 查询问题
questions = Question.objects.skip(randimskip).limit(20)
serializer = self.get_serializer(questions, many=True)
return Response(serializer.data)
上面的viewset中,有两个函数使用的@list_router装饰器,其表示这是用户自定义的额外的方法。因为有时候viewset自动生成的list, create, retrive, update, delete等方法不能满足我们的需要,比如需要随机查询数据库中的20条记录。在django debug输出中可以看到自定义方法的rest资源路径:
Using the URLconf defined in web.urls, Django tried these URL patterns, in this order:
^static/(?P<path>.*)$
^admin/
^rest/ ^ ^$ [name='api-root']
^rest/ ^ ^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']
^rest/ ^ ^questions/$ [name='question-list']
^rest/ ^ ^questions\.(?P<format>[a-z0-9]+)/?$ [name='question-list']
^rest/ ^ ^questions/initialization/$ [name='question-initialization']
^rest/ ^ ^questions/initialization\.(?P<format>[a-z0-9]+)/?$ [name='question-initialization']
^rest/ ^ ^questions/random/$ [name='question-random']
^rest/ ^ ^questions/random\.(?P<format>[a-z0-9]+)/?$ [name='question-random']
^rest/ ^ ^questions/(?P<pk>[^/.]+)/$ [name='question-detail']
^rest/ ^ ^questions/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='question-detail']
^rest/ ^api-auth/
路由
在urls.py中添加如下代码:
from views import QuestionViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
# need to set base_name,otherwise will report:AttributeError: 'QuerySet' object has no attribute 'model'
router.register(r'questions', QuestionViewSet, r'question')
urlpatterns = [
...
url(r'^', include(router.urls)),
]
REST安全认证
客户端有三种认证方式
BasicAuthentication、SessionAuthentication、TokenAuthentication
需要在setting.py中配置默认的认证类:
REST_FRAMEWORK = {
# three auth way: session,basic,token
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
'PAGE_SIZE': 10
}
具体可以参考
http://www.django-rest-framework.org/api-guide/authentication/#authentication
如果要使用djangorestframe提供的认证登录界面,需要在urlpatterns中添加:
urlpatterns = [
...
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
monodb + djangorestframework
为了在djangorestframework中使用mongodb, 需要mongoengine和django-rest-framework-mongoengine
pip install mongoengine
pip install django-rest-framework-mongoengine
序列化类这样写
# -*- coding: utf-8 -*-
from rest_framework_mongoengine.serializers import DocumentSerializer
from models import *
class QuestionSerializer(DocumentSerializer):
"""
使用ModelSerializer实现序列化
"""
class Meta:
model = Question
fields = ('question', 'answer', 'reason')
#depth = 2
def create(self, validated_data):
return Question.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.question = validated_data.get('question', instance.question)
instance.answer = validated_data.get('answer', instance.answer)
instance.reason = validated_data.get('reason', instance.reason)
instance.save()
return instance
当然最笨的方法就是不使用mongoengine,利用pymongo自己封装model,并且不使用viewset实现视图