Django BinaryField 检索为内存位置?

Posted

技术标签:

【中文标题】Django BinaryField 检索为内存位置?【英文标题】:Django BinaryField retrieved as memory position? 【发布时间】:2016-03-19 04:34:42 【问题描述】:

我用以下代码编写了一个简短的单元测试:

my_object = MyObject()
my_object.data = b'12345'

my_object.save()
saved_object = MyObject.objects.first()

assert saved_object.data == my_object.data

其中MyObject 定义为:

class MyObject(models.Model):
    data = models.BinaryField(default=None)

我希望断言能够通过,因为我只是保存一些字节数据然后检索它。但是,我最终得到:

AssertionError: assert <memory at 0x10e2abc48> == b'12345'

我猜它与直接将字节字符串保存到二进制字段有关。另一方面,保存成功似乎很奇怪。而且我很难找到 Django 的BinaryField 的一些好的示例用法。谁能向我解释这里发生了什么或我做错了什么?非常感谢。

【问题讨论】:

【参考方案1】:

Django 将BinaryField 的内容标准化为一个缓冲区。在 Python2 中以 buffer 和在 Python3 中的 memoryview 为具体。

你可以在源代码中看到:

to_pythonsource six.memoryviewsource

在 Python2 中,buffer 没有实现比较逻辑,这就是您的断言失败的原因:

Python 2.7.11
>>> m = buffer(b'hello')
>>> m == b'hello'
False
>>> bytes(m) == b'hello'
True

但它确实实现了其他操作,例如切片和长度:

>>> len(m)
5
>>> m[1:]
'ello'

在 Python 3 中,情况要好得多,因为它实现了所有预期的操作:

Python 3.5.1
>>> m = memoryview(b'hello')
>>> m
<memory at 0x109e8b108>
>>> bytes(m)
b'hello'
>>> m == b'hello'
True
>>> bytes(m) == b'hello'
True
>>> len(m)
5
>>> m[1:]
<memory at 0x109e8b1c8>

我不确定 Django 这样做的原因,但我的猜测是为了提高效率。缓冲区在处理内存方面效率更高。例如,当您切片时,它可以重用相同的内存而不是分配更多的内存。

【讨论】:

这无疑解释了问题以及解决问题的方法。但是,还有另一件事。如果我将代码更改为使用assert bytes(saved_object.data) == my_object.data,则测试通过。我正在使用 Python 3,您的示例按照您的说明运行。但是,当通过 Django 保存和检索测试时,与 memoryview 直接比较字节数据仍然失败。这不是很重要,但是您是否知道为什么在这种情况下内存视图 必须 转换回字节才能进行比较,但对于没有 Django 的情况则不然? 哪个 Python3 版本? 3.5.0 (与我用于运行上述示例的相同,如您所述)。 Django 1.8 版。 嗯。不知道。从错误消息看来,您使用的是 pytest。也许这会有所作为? 我也觉得可能是这样,所以我将您的示例代码转换为测试表单并尝试使用 pytest 运行它。在那种情况下测试通过了,所以它似乎是别的东西。无论如何,仅将 memoryview 强制转换为字节就足以满足我的情况。因此,感谢您对此和背后的理解。

以上是关于Django BinaryField 检索为内存位置?的主要内容,如果未能解决你的问题,请参考以下文章

django:使用 BInaryField 模拟标志字段

为遗传算法分配和检索按位内存值

Django数据模型--字段整理

Django的ORM字段与MySQL数据库实际字段的对应关系

Django模型类之models字段类型和参数以及元数据meta

django全文检索