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 相关文章推荐
开始着手第一个Django项目
Jul 15 Python
在pandas中一次性删除dataframe的多个列方法
Apr 10 Python
好的Python培训机构应该具备哪些条件
May 23 Python
python通过配置文件共享全局变量的实例
Jan 11 Python
python 通过类中一个方法获取另一个方法变量的实例
Jan 22 Python
Python生成一个迭代器的实操方法
Jun 18 Python
Python数据可视化 pyecharts实现各种统计图表过程详解
Aug 15 Python
Tensorflow分批量读取数据教程
Feb 07 Python
Python如何使用内置库matplotlib绘制折线图
Feb 24 Python
mac 上配置Pycharm连接远程服务器并实现使用远程服务器Python解释器的方法
Mar 19 Python
python+requests接口自动化框架的实现
Aug 31 Python
Django中使用Celery的方法步骤
Dec 07 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
杏林同学录(一)
2006/10/09 PHP
不用数据库的多用户文件自由上传投票系统(2)
2006/10/09 PHP
php 中的4种标记风格介绍
2012/05/10 PHP
解析PHP提交后跳转
2013/06/23 PHP
php断点续传之如何分割合并文件
2014/03/22 PHP
ThinkPHP3.1新特性之动态设置自动完成及自动验证示例代码
2014/06/23 PHP
替换php字符串中的单引号为双引号的方法
2017/02/16 PHP
PHP+redis实现微博的推模型案例分析
2019/07/10 PHP
基于jquery的一个图片hover的插件
2010/04/24 Javascript
一个背景云变换js特效 鼠标移动背景云变化
2012/12/28 Javascript
javasciprt下jquery函数$.post执行无响应的解决方法
2014/03/13 Javascript
js 弹出新页面避免被浏览器、ad拦截的一种新方法
2014/04/30 Javascript
根据配置文件加载js依赖模块
2014/12/29 Javascript
移动设备web开发首选框架:zeptojs介绍
2015/01/29 Javascript
详解JavaScript中Hash Map映射结构的实现
2016/05/21 Javascript
js replace(a,b)之替换字符串中所有指定字符的方法
2016/08/17 Javascript
axios基本入门用法教程
2017/03/25 Javascript
vue toggle做一个点击切换class(实例讲解)
2018/03/13 Javascript
详解webpack的proxyTable无效的解决方案
2018/06/15 Javascript
Vue-CLI 项目在pycharm中配置方法
2019/08/30 Javascript
[06:10]6.81新信使新套装!给你一个炫酷的DOTA2
2014/05/06 DOTA
python 2.6.6升级到python 2.7.x版本的方法
2016/10/09 Python
详解利用django中间件django.middleware.csrf.CsrfViewMiddleware防止csrf攻击
2018/10/09 Python
Python3简单实现串口通信的方法
2019/06/12 Python
python智联招聘爬虫并导入到excel代码实例
2019/09/09 Python
pygame实现烟雨蒙蒙下彩虹雨
2019/11/11 Python
python 解决flask 图片在线浏览或者直接下载的问题
2020/01/09 Python
Python异常处理机制结构实例解析
2020/07/23 Python
CSS3动画效果回调处理详解
2014/12/10 HTML / CSS
Sunglasses Shop德国站:欧洲排名第一的太阳镜网站
2017/08/01 全球购物
马来西亚和新加坡巴士票在线预订:CatchThatBus
2018/11/17 全球购物
普通党员个人剖析材料
2014/10/08 职场文书
六一领导慰问欢迎词
2015/01/26 职场文书
2015年学校教务处工作总结
2015/05/11 职场文书
趣味运动会通讯稿
2015/07/18 职场文书
Flask response响应的具体使用
2021/07/15 Python