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中类的继承代码实例
Oct 28 Python
python中实现php的var_dump函数功能
Jan 21 Python
Linux下用Python脚本监控目录变化代码分享
May 21 Python
Python调用C# Com dll组件实战教程
Oct 12 Python
解析Python中的eval()、exec()及其相关函数
Dec 20 Python
python读取各种文件数据方法解析
Dec 29 Python
在Pycharm中对代码进行注释和缩进的方法详解
Jan 20 Python
对Python 获取类的成员变量及临时变量的方法详解
Jan 22 Python
pandas计算最大连续间隔的方法
Jul 04 Python
python+gdal+遥感图像拼接(mosaic)的实例
Mar 10 Python
python如何快速拼接字符串
Oct 28 Python
如何通过python检查文件是否被占用
Dec 18 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
php5.3中连接sqlserver2000的两种方法(com与ODBC)
2012/12/29 PHP
PHP实现非阻塞模式的方法分析
2018/07/26 PHP
Laravel框架中缓存的使用方法分析
2019/09/06 PHP
PHP利用curl发送HTTP请求的实例代码
2020/07/09 PHP
Laravel中如何轻松容易的输出完整的SQL语句
2020/07/26 PHP
三级下拉菜单的js实现代码
2011/05/23 Javascript
Firefox/Chrome/Safari的中可直接使用$/$$函数进行调试
2012/02/13 Javascript
自定义右键属性覆盖浏览器默认右键行为实现代码
2013/02/02 Javascript
让alert不出现弹窗的两种方法
2014/05/18 Javascript
jQuery中:animated选择器用法实例
2014/12/29 Javascript
js获取域名的方法
2015/01/27 Javascript
纯JS实现本地图片预览的方法
2015/07/31 Javascript
bootstrap flask登录页面编写实例
2016/11/01 Javascript
JavaScript中递归实现的方法及其区别
2017/09/12 Javascript
基于casperjs和resemble.js实现一个像素对比服务详解
2018/01/10 Javascript
vue中axios解决跨域问题和拦截器的使用方法
2018/03/07 Javascript
React 无状态组件(Stateless Component) 与高阶组件
2018/08/14 Javascript
Vue父子组件之间的通信实例详解
2018/09/28 Javascript
微信小程序授权登陆及每次检查是否授权实例代码
2019/09/18 Javascript
在Chrome DevTools中调试JavaScript的实现
2020/04/07 Javascript
[01:21]DOTA2 新英雄 森海飞霞
2020/12/18 DOTA
python计算最大优先级队列实例
2013/12/18 Python
Python使用指定字符长度切分数据示例
2019/12/05 Python
NumPy排序的实现
2020/01/21 Python
使用canvas生成含有微信头像的邀请海报没有微信头像问题
2019/10/29 HTML / CSS
香港现代设计家具品牌:Ziinlife Furniture
2018/11/13 全球购物
蹦床仓库:Trampoline Warehouse
2018/12/06 全球购物
什么是索引指示器
2012/08/20 面试题
大学新生军训感言
2014/02/25 职场文书
厂区绿化方案
2014/05/08 职场文书
实习单位鉴定意见
2015/06/04 职场文书
工作汇报材料难写?方法都在这里了!
2019/07/01 职场文书
vue引入Excel表格插件的方法
2021/04/28 Vue.js
Java 使用类型为Object的变量指向任意类型的对象
2022/04/13 Java/Android
python pygame 开发五子棋双人对弈
2022/05/02 Python
向Spring IOC 容器动态注册bean实现方式
2022/07/15 Java/Android