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

作者:TUALATRIX 时间:2023-07-29 02:42:42 

前言

我的 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 对象,文件属性默认有绝对路径而不是相对路径的原因了~

来源:https://imtx.me/archives/2397.html

标签:django,rest,输出URL
0
投稿

猜你喜欢

  • vscode调试container中的程序的方法步骤

    2022-03-06 14:20:25
  • python生成ppt的方法

    2021-11-08 11:50:48
  • python实现简单温度转换的方法

    2021-04-12 10:14:51
  • mysql数据库单表最大存储依据详解

    2024-01-16 03:02:24
  • 对Python 2.7 pandas 中的read_excel详解

    2023-09-10 04:14:49
  • git中submodule子模块的添加、使用和删除的示例代码

    2023-11-24 01:31:36
  • 判断数据库表是否存在以及修改表名的方法

    2024-01-22 09:21:24
  • python GUI库图形界面开发之PyQt5图片显示控件QPixmap详细使用方法与实例

    2023-05-31 17:41:29
  • GoLang 逃逸分析的机制详解

    2023-08-06 16:46:43
  • jupyter notebook 参数传递给shell命令行实例

    2023-08-28 06:52:55
  • 在Python中使用Neo4j的方法

    2023-01-16 04:12:39
  • python海龟绘图之画国旗实例代码

    2022-03-06 14:57:23
  • MySQL source命令的使用简介

    2024-01-15 14:35:18
  • 阿里云服务器新建用户具体方法

    2024-01-22 19:08:51
  • Python实现模拟分割大文件及多线程处理的方法

    2021-08-26 10:55:12
  • python [:3] 实现提取数组中的数

    2022-06-29 07:46:19
  • mysql中如何优化表释放表空间

    2024-01-18 09:58:27
  • Python 为什么推荐蛇形命名法原因浅析

    2021-09-12 14:24:53
  • 微信小程序开发之数据存储 参数传递 数据缓存

    2024-04-19 09:49:44
  • 小程序实现横向滑动日历效果

    2024-04-10 16:19:46
  • asp之家 网络编程 m.aspxhome.com