Django REST为文件属性输出完整URL的方法


Posted in Python onDecember 18, 2017

前言

我的 App 项目的 API 部分是使用 Django REST Framework 来搭建的,它可以像搭积木一样非常方便地搭出 API,兼具方便和灵活。

django是一个神奇的框架,而restframework又是遵循了这个框架的另一个神奇的框架,然而由于restframework的文档稀烂无比,很多时候你必须看源码才能写出科学的代码,这挡住了很多新手的路。

在使用的过程中我也积累了一些小技巧,这里写一则关于如何为文件属性输出完整 URL 的字段。

实现方法

一个典型的案例是,当请求 /profile/ 这个 API 的时候,返回类似于这样的结果:

{
 "id": 1,
 "nickname": "管理员",
 "mobilephone": "1234567890",
 "avatar": "/media/profiles/2017/12/17/avatar.png"
}

在 Django REST 的定义中,我使用了自定义的一个扩展自 rest_framework.views.APIView 的 ProfileView 类型,实现了它的 get 方法,来给认证的用户返回一个 Profile 对象:

class ProfileView(APIView):
 def get(self, request):
  user = request.user
  if user.is_authenticated:
   profile = Profile.objects.get(user=user)
   return Response(ProfileSerializer(profile).data)
  else:
   raise exceptions.AuthenticationFailed('Not authenticated user!')

这里的逻辑很简单,判断请求当前 API 的用户是不是已经验证过的用户,如果是的话,再得到它的 Profile,再通过 ProfileSerializer 把 profile 实例序列化成 JSON 对象。如果不是已验证用户,则会返回 401 验证失败相关信息。

以上输出的内容,交给 Web 前端使用是没什么问题的,但如果是给 App 使用,那么 avatar 这个文件属性的相对 URL 不太合适,于是我们要改造一下这个 API,使其能输出绝对 URL。

如何做呢?只需要将上面的 get 方法,稍加修改即可:

-class ProfileView(APIView):
+class ProfileView(generics.GenericAPIView):
  parser_classes = (MultiPartParser, FormParser)
+ serializer_class = ProfileSerializer
  def get(self, request):
   user = request.user
   if user.is_authenticated:
    profile = Profile.objects.get(user=user)
-   return Response(ProfileSerializer(profile).data)
+   serializer = self.get_serializer(profile)
+   return Response(serializer.data)
   else:
    raise exceptions.AuthenticationFailed('Not authenticated user!')

不同于之前继承自 APIView,现在继承自 generics.GenericAPIView,这是一个更通用的类,可以看到,这里通过手动构建 ProfileSerializer 改成通过 self.get_serializer 来进行,这里有什么不同呢?

还得看看 Django REST 的源码,GenericAPIView 这个类的 get_serializer 在做什么。

def get_serializer(self, *args, **kwargs):
    """
    Return the serializer instance that should be used for validating and
    deserializing input, and for serializing output.
    """
    serializer_class = self.get_serializer_class()
    kwargs['context'] = self.get_serializer_context()
    return serializer_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 出来,而不是默认存在数据库里的相对地址。

def to_representation(self, value):
  if not value:
   return None
  use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL)
  if use_url:
   if not getattr(value, 'url', None):
    # If the file has not been saved it may not have a URL.
    return None
   url = value.url
   request = self.context.get('request', None)
   if request is not None:
    return request.build_absolute_uri(url)
   return url
  return value.name

这就是为什么通过 GenericAPIView 来输出 API 对象,文件属性默认有绝对路径而不是相对路径的原因了~

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python在命令行下使用google翻译(带语音)
Jan 16 Python
python抓取文件夹的所有文件
Feb 27 Python
python 使用正则表达式按照多个空格分割字符的实例
Dec 20 Python
python 绘制拟合曲线并加指定点标识的实现
Jul 10 Python
用python给自己做一款小说阅读器过程详解
Jul 11 Python
python使用原始套接字发送二层包(链路层帧)的方法
Jul 22 Python
python爬虫 2019中国好声音评论爬取过程解析
Aug 26 Python
简单介绍django提供的加密算法
Dec 18 Python
PyCharm 2020 激活到 2100 年的教程
Mar 25 Python
PyCharm+Pipenv虚拟环境开发和依赖管理的教程详解
Apr 16 Python
python3排序的实例方法
Oct 20 Python
Python Django路径配置实现过程解析
Nov 05 Python
Python3计算三角形的面积代码
Dec 18 #Python
利用python解决mysql视图导入导出依赖的问题
Dec 17 #Python
python 3.5实现检测路由器流量并写入txt的方法实例
Dec 17 #Python
python中闭包Closure函数作为返回值的方法示例
Dec 17 #Python
django模板语法学习之include示例详解
Dec 17 #Python
详解python string类型 bytes类型 bytearray类型
Dec 16 #Python
python使用os.listdir和os.walk获得文件的路径的方法
Dec 16 #Python
You might like
php $_SERVER当前完整url的写法
2009/11/12 PHP
PHP中获取变量的变量名的一段代码的bug分析
2011/07/07 PHP
php使用Cookie控制访问授权的方法
2015/01/21 PHP
老生常谈PHP 文件写入和读取(必看篇)
2017/05/22 PHP
thinkphp5 模型实例化获得数据对象的教程
2019/10/18 PHP
js 图片等比例缩放代码
2010/05/13 Javascript
jQuery$命名冲突怎么办如何解决
2014/01/16 Javascript
js获取判断上传文件后缀名的示例代码
2014/02/19 Javascript
javascript删除一个html元素节点的方法
2014/12/20 Javascript
jquery获取当前日期的方法
2015/01/14 Javascript
Javascript中的Prototype到底是什么
2016/02/16 Javascript
Javascript数组Array方法解读
2016/03/13 Javascript
仅一个form表单 js实现注册信息依次填写提交功能
2016/06/12 Javascript
nodejs实例解析(输出hello world)
2017/01/03 NodeJs
vuejs2.0实现分页组件使用$emit进行事件监听数据传递的方法
2017/02/22 Javascript
BootstrapValidator实现注册校验和登录错误提示效果
2017/03/10 Javascript
Django中使用jquery的ajax进行数据交互的实例代码
2017/10/15 jQuery
元素全屏的设置与监听实例
2017/11/28 Javascript
webpack 模块热替换原理
2018/04/09 Javascript
关于vue-router的那些事儿
2018/05/23 Javascript
浅析Vue项目中使用keep-Alive步骤
2018/07/27 Javascript
Vue 样式绑定的实现方法
2019/01/15 Javascript
mongodb初始化并使用node.js实现mongodb操作封装方法
2019/04/02 Javascript
用Python编写一个基于终端的实现翻译的脚本
2015/04/24 Python
python数据处理实战(必看篇)
2017/06/11 Python
Tensorflow 自带可视化Tensorboard使用方法(附项目代码)
2018/02/10 Python
python实现简单遗传算法
2018/03/19 Python
Python中asyncio与aiohttp入门教程
2018/10/16 Python
解决python调用自己文件函数/执行函数找不到包问题
2020/06/01 Python
CSS3过渡transition效果实例介绍
2016/05/03 HTML / CSS
美国知名日用品连锁超市:Dollar General(多来店)
2017/01/14 全球购物
大学教师年终总结的自我评价
2013/10/29 职场文书
课程改革实施方案
2014/03/16 职场文书
企业总经理助理岗位职责
2014/09/12 职场文书
先进个人主要事迹范文
2015/11/04 职场文书
详解Mysq MVCC多版本的并发控制
2022/04/29 MySQL