Django REST为文件属性输出完整URL的方法
前言
我的App项目的API部分是使用DjangoRESTFramework来搭建的,它可以像搭积木一样非常方便地搭出API,兼具方便和灵活。
django是一个神奇的框架,而restframework又是遵循了这个框架的另一个神奇的框架,然而由于restframework的文档稀烂无比,很多时候你必须看源码才能写出科学的代码,这挡住了很多新手的路。
在使用的过程中我也积累了一些小技巧,这里写一则关于如何为文件属性输出完整URL的字段。
实现方法
一个典型的案例是,当请求/profile/这个API的时候,返回类似于这样的结果:
{ "id":1, "nickname":"管理员", "mobilephone":"1234567890", "avatar":"/media/profiles/2017/12/17/avatar.png" }
在DjangoREST的定义中,我使用了自定义的一个扩展自rest_framework.views.APIView的ProfileView类型,实现了它的get方法,来给认证的用户返回一个Profile对象:
classProfileView(APIView): defget(self,request): user=request.user ifuser.is_authenticated: profile=Profile.objects.get(user=user) returnResponse(ProfileSerializer(profile).data) else: raiseexceptions.AuthenticationFailed('Notauthenticateduser!')
这里的逻辑很简单,判断请求当前API的用户是不是已经验证过的用户,如果是的话,再得到它的Profile,再通过ProfileSerializer把profile实例序列化成JSON对象。如果不是已验证用户,则会返回401验证失败相关信息。
以上输出的内容,交给Web前端使用是没什么问题的,但如果是给App使用,那么avatar这个文件属性的相对URL不太合适,于是我们要改造一下这个API,使其能输出绝对URL。
如何做呢?只需要将上面的get方法,稍加修改即可:
-classProfileView(APIView): +classProfileView(generics.GenericAPIView): parser_classes=(MultiPartParser,FormParser) +serializer_class=ProfileSerializer defget(self,request): user=request.user ifuser.is_authenticated: profile=Profile.objects.get(user=user) -returnResponse(ProfileSerializer(profile).data) +serializer=self.get_serializer(profile) +returnResponse(serializer.data) else: raiseexceptions.AuthenticationFailed('Notauthenticateduser!')
不同于之前继承自APIView,现在继承自generics.GenericAPIView,这是一个更通用的类,可以看到,这里通过手动构建ProfileSerializer改成通过self.get_serializer来进行,这里有什么不同呢?
还得看看DjangoREST的源码,GenericAPIView这个类的get_serializer在做什么。
defget_serializer(self,*args,**kwargs): """ Returntheserializerinstancethatshouldbeusedforvalidatingand deserializinginput,andforserializingoutput. """ serializer_class=self.get_serializer_class() kwargs['context']=self.get_serializer_context() returnserializer_class(*args,**kwargs)
可以看到,这个方法在创建serializer的时候,会把context传进去,而get_serializer_context也是一个固定方法,它会把request、view和format这些信息包含在里面。
那么request、view和format这些信息,是如何用在serializer里面,最后把一个文件对象的全路径展开的呢?
省略中间serializer一系列序列化过程,当它遇到FileField的时候,会通过判断context里面有没有reuqest,有的话,就调用request.build_absolute_uri(url)方法,把绝对地址build出来,而不是默认存在数据库里的相对地址。
defto_representation(self,value): ifnotvalue: returnNone use_url=getattr(self,'use_url',api_settings.UPLOADED_FILES_USE_URL) ifuse_url: ifnotgetattr(value,'url',None): #IfthefilehasnotbeensaveditmaynothaveaURL. returnNone url=value.url request=self.context.get('request',None) ifrequestisnotNone: returnrequest.build_absolute_uri(url) returnurl returnvalue.name
这就是为什么通过GenericAPIView来输出API对象,文件属性默认有绝对路径而不是相对路径的原因了~
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。