为什么Django FieldFIle Readline()返回十六进制版本的文本文件?

人气:117 发布:2023-01-03 标签: python django readline boto

问题描述

有奇怪的问题。

我有一个Django应用程序,它打开一个文件(表示为DjangoFieldFile),并使用readline()读取每一行,如下所示:

with file.open(mode='r') as f:
  row = f.readline()
  # do something with row...

该文件为文本,采用UTF-8编码,行以 结尾。

问题是每一行都被读取为字符串的十六进制表示形式,因此我得到的不是"Hello",而是"48656c6c6f"。

一些奇怪的事情:

它以前工作正常,但在某个时候被更新破坏了(我尝试回滚到以前的提交,但它仍然不稳定,所以可能是依赖项已经更新,而不是我的requirements.txt中的内容)。在我的测试中遗漏了它,因为它位于应用程序中很少使用的部分。

如果我使用readlines()而不是readline()读取相同的文件,我会看到[b'...']

中包含的文件的正确字符串表示形式

如果我直接使用Pythonopen()readline()从解释器读取文件,则文件可以正常读取

使用mode='rt'强制文本模式不会改变行为,mode='rb'

文件存储在mini存储桶中,因此默认存储是storages.backends.s3boto3.S3Boto3Storagefromdjango-storages,而不是默认的Django存储类。这意味着boto3botocores3fs也混合在一起,这给我的调试带来了更大的困惑。

抓挠我的头,不知道为什么以前这样做有效,我做错了什么。

环境是在Docker容器中运行的Python3.8、Django 2.2.8和3.0(结果相同)。

编辑

让我指出,解决此问题的方法只需使用

row = f.readline().decode()

但我还是想弄清楚发生了什么事。

编辑%2

此外,FieldFile.open()将文件读取为二进制文件,而普通的Python Open()将文件读取为文本文件。

推荐答案

这似乎非常奇怪。 我想你在尝试下面的答案后会立即看到解决方案(如果确实无济于事,我会更新我的答案或删除它,但我很有信心)

假设有一些代码,即monkeypatting file.open或Django视图函数。

我的建议是:

从made.py runserver开始您的代码 将以下代码添加到made.py(作为第一行)

import file
print("ID of file.open at manage startup is", id(file.open)

然后将代码直接添加到视图file.open上方的一行

    print("ID of file.open before opening is", id(file.open)

如果这两个ID不同,那么一定有什么东西篡改了您的打开函数。 如果两者相同,则问题一定出在其他地方。

如果您没有看到这两个打印的输出,则可能是某些东西破坏了您的视图。

如果这不起作用,则尝试使用 open()而不是file.open()

您使用file.open()

有什么特殊原因吗

附录1:

那么您所说的是,该文件是一个类的对象实例,它是一个Filefield吗? 在任何情况下,您都可以获取文件的名称并使用正常的open()打开它,以查看是只有file.open()才做有趣的事情,还是open()也是以这种奇怪的方式读取它。 您是使用cat filename从命令行打开文件(还是使用type filename在Windows下打开文件?

如果这不起作用,我们可以在正在执行的源代码的每一行后面添加跟踪。

附录2:

如果您无法在manage.py runserver中尝试此操作,如果您尝试使用manage.py shell读取文件,会发生什么情况?

只需打开外壳并键入类似以下内容:

from <your_application>.models import <YourModel>
entry = <YourModel>.objects.get(id=<idofentry>)
line1 = entry.<filefieldname>.open("r").read().split("
")[0]
print("line1 = %r" % line1)

如果仍不确定(但仅当您可以用命令行管理程序重现该问题时),则创建一个包含行的小文件。

from <your_application>.models import <YourModel>
entry = <YourModel>.objects.get(id=<idofentry>)
import pdb; pdb.set_trace()
line1 = entry.<filefieldname>.open("r").read().split("
")[0]
print("line1 = %r" % line1)

并从命令行管理程序导入它。 代码应该进入调试器,现在您可以单步通过打开函数,并查看您是否在某个monkeypatch中使用了类似的奇怪函数。

21