前文 这两天研究Django,简单入门了Django。相关笔记如下。其中Django REST framework部分没来及的做记录,也很简单,直接看官方档案即可。
今日目标 今天就利用Django authentication system 和Django REST framework 做一个允许用户上传图片至七牛的API。用户可以查看所有用户上传图片的信息,但只能编辑和删除自己上传的。
API
初始化 首先按照Django学习笔记 创建Project:qiniu
和App:api
。python manage.py shell
进入Shell模式并创建两个用户
1 2 3 4 5 6 7 >>> from django.contrib.auth.models import User>>> user = User.objects.create_user('john' , 'lennon@thebeatles.com' , 'john' )>>> user.save()>>> user2 = User.objects.create_user('tom' , 'lennon@thebeatles.com' , 'tom' )>>> user2.save()>>> User.objects.all ()[<User: john>, <User: tom>]
注释掉qiniu/qiniu/setting.py
中的
登入View 按照官方Authentication in Web requests 章节提供的内容,利用一下代码做登录view。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from django.contrib.auth import authenticate, loginfrom django.views.decorators.csrf import csrf_exempt@csrf_exempt def login_func (request ): username = request.POST['username' ] password = request.POST['password' ] user = authenticate(username=username, password=password) if user is not None : if user.is_active: login(request, user) return HttpResponse(u"成功登入,记得保存Cookie" ) else : return HttpResponse(u"帐号被禁止" ) else : return HttpResponse(u"信息有误" )
如果使用了非POST模式或者POST中没有包含username和password信息则会出现服务器错误,因此我们需要增加try方法和统一规范的错误信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 def responseJson (jsonDic ): return HttpResponse(JSON.dumps(jsonDic)) @csrf_exempt def login_func (request ): ressult = {} try : username = request.POST['username' ] password = request.POST['password' ] user = authenticate(username=username, password=password) if user is not None : if user.is_active: login(request, user) ressult = { 'info' : u"成功登入,记得保存Cookie" , 'success' : True , 'code' : 200 } return responseJson(ressult) else : ressult = { 'info' : u"用户被禁止登录" , 'success' : False , 'code' : 300 } return responseJson(ressult) else : ressult = { 'info' : u"帐号或密码不正确" , 'success' : False , 'code' : 300 } return responseJson(ressult) except : ressult = { 'info' : u"请使用POST请求并包含所有参数" , 'success' : False , 'code' : 400 } return responseJson(ressult)
登录功能完成,对于其他需要登录的页面只需要在view函数前增加一行即可。
1 2 3 4 5 from django.contrib.auth.decorators import login_required@login_required(login_url='/login/' ) def view (request ): ...
获取七牛token 以下是我写的生成七牛token的文件qiniutoken.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import timefrom hashlib import sha1import hmacfrom base64 import urlsafe_b64encode, urlsafe_b64decode,b64encodeAccessKey = 'YOUR-AccessKey' SecretKey = 'YOUR-SecretKey' Scope = "Bucket-name" def getUploadPolicyJosn (): deadline = int (time.time()) + 3600 return ('{"scope":"%s","deadline":%s}' % (Scope,deadline)) def encodePolicy (policty ): return urlsafe_b64encode(policty) def hmac_md5 (key, msg ): return hmac.new(key, msg, digestmod=sha1).digest() def getUploadToken (): uploadPolicy = getUploadPolicyJosn() encodedPolicy = urlsafe_b64encode(uploadPolicy) hmac_md5_accessKeyAndPolicty = hmac_md5(SecretKey,encodedPolicy) encoded_signed = urlsafe_b64encode(hmac_md5_accessKeyAndPolicty) uploadToken = "%s:%s:%s" %(AccessKey,encoded_signed,encodedPolicy) return { 'token' :uploadToken }
创建响应的View和URL路由表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from . import qiniuurl(r'^getuploadtoken/$' , views.getQiniuUploadToken , name='getQiniuUploadToken' ) def getUploadToken (): result = { 'info' : u"成功获取" , 'success' : True , 'code' : 200 , 'data' : qiniu.getUploadToken() } return responseJson(result)
图片列表 首先我们按照Django REST framework 规范创建图片Model和Serializer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from django.db import modelsclass Picture (models.Model): created = models.DateTimeField(auto_now_add=True ) title = models.CharField(max_length=100 , blank=True , default='' ) anonymous = models.BooleanField(default=False ) picname = models.CharField(max_length=100 ) owner = models.ForeignKey('auth.User' , related_name='pictures' ) from rest_framework import serializersclass PictureSerializer (serializers.ModelSerializer): class Meta : model = Picture owner = serializers.ReadOnlyField(source='owner.username' ) fields = ('id' , 'title' , 'created' , 'anonymous' , 'picname' )
配置好Model和Serializer后更新数据库
1 2 python manage.py makemigrations apipython manage.py migrate
接下来配置相关View
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from rest_framework.renderers import JSONRendererfrom rest_framework.parsers import JSONParserfrom api.models import Picturefrom api.serializers import PictureSerializer@login_required(login_url='/api/login/' ) def picturelist (request ): if request.method == 'GET' : pictures = Picture.objects.all () serializer = PictureSerializer(pictures, many=True ) result = { 'status' :201 , 'success' :True , 'data' :serializer.data } return responseJson(result) elif request.method == 'POST' : data = JSONParser().parse(request) serializer = PictureSerializer(data=data) if serializer.is_valid(): current_user = request.user serializer.save(owner=request.user) result = { 'status' :201 , 'success' :True , } return responseJson(result) result = { 'status' :400 , 'success' :False , } return responseJson(result)
编辑具体图片 此时view和model没什么要求改的,主要在view中操作即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 @login_required(login_url='/api/login/' ) def picdetail (request,pic_id ): if request.method == 'GET' : try : pic = Picture.objects.get(pk = pic_id) serializer = PictureSerializer(pic, many=False ) result = { 'status' :201 , 'success' :True , 'info' :'' , 'data' :serializer.data } return responseJson(result) except Picture.DoesNotExist : result = { 'status' :404 , 'success' :False , 'info' :u'图片不存在' , 'data' :'' } return responseJson(result) result = { 'status' :404 , 'success' :False , 'info' :u'未知错误' , 'data' :'' } return responseJson(result) elif request.method == 'POST' : try : pic = Picture.objects.get(pk = pic_id) data = JSONParser().parse(request) serializer = PictureSerializer(pic,data=data,partial=True ) if serializer.is_valid(): current_user = request.user serializer.save() result = { 'status' :201 , 'success' :True , } return responseJson(result) result = { 'status' :400 , 'success' :False , } return responseJson(result) except Picture.DoesNotExist : result = { 'status' :404 , 'success' :False , 'info' :u'图片不存在' , 'data' :'' } return responseJson(result) result = { 'status' :404 , 'success' :False , 'info' :u'未知错误' , 'data' :'' } return responseJson(result)
功能都实现了,但是有大量的重复代码。需要进一步优化完善。
优化 之前重复代码太多,特别是返回信息部分,所以重构了一下responseJson
函数
1 2 3 4 5 6 7 8 9 10 11 def responseJson (code=201 ,info="" ,data={},success = True ): if code != 201 : success = False jsonDic = { 'status_code' :code, 'info' :info, 'data' :data, 'success' :success } return HttpResponse(JSON.dumps(jsonDic))
这样一来代码行数大大缩减,比如前面的编辑和查看具体图片的view从原来的66行缩短为25行,少了近2/3。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 def picdetail (request,pic_id ): if request.method == 'GET' : try : pic = Picture.objects.get(pk = pic_id) serializer = PictureSerializer(pic, many=False ) return responseJson(data = serializer.data) except Picture.DoesNotExist : return responseJson(info = u'图片不存在' ,code = 404 ) return responseJson(info = u'未知错误' ,code = 404 ) elif request.method == 'POST' : try : pic = Picture.objects.get(pk = pic_id) data = JSONParser().parse(request) serializer = PictureSerializer(pic,data=data,partial=True ) if serializer.is_valid(): if pic.id == request.user.id : serializer.save() return responseJson(info = u'更新成功' ) return responseJson(info = u'无权修改' ,code=404 ) return responseJson(info = u'信息错误' ,code = 404 ) except Picture.DoesNotExist : return responseJson(info = u'图片不存在' ,code = 404 ) return responseJson(info = u'未知错误' ,code = 404 )