I've written a short unit test with the following code:
my_object = MyObject()
my_object.data = b'12345'my_object.save()
saved_object = MyObject.objects.first()assert saved_object.data == my_object.data
where MyObject
is defined as:
class MyObject(models.Model):data = models.BinaryField(default=None)
I would expect the assert to pass, as I'm just saving some byte data and then retrieving it. However, I end up with:
AssertionError: assert <memory at 0x10e2abc48> == b'12345'
I'm guessing that it has to do with directly saving the byte string to the binary field. On the other hand, it seems strange that the save succeeds at all then. And I'm having a bit of difficultly finding some good example uses of Django's BinaryField
. Can anyone explain to me what's happening here or what I'm doing wrong? Thank you much.
Django normalizes the content of the BinaryField
to a buffer. In Python2 to buffer
and memoryview
in Python3 to be specific.
You can see that in source code:
to_python
source
six.memoryview
source
In Python2 buffer
does not implement comparison logic which is why your assertion fails:
Python 2.7.11
>>> m = buffer(b'hello')
>>> m == b'hello'
False
>>> bytes(m) == b'hello'
True
However it does implement other operations such as slicing and length:
>>> len(m)
5
>>> m[1:]
'ello'
In Python 3 the story is much better since it implements all expected operations:
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>
Im not sure the reason why Django does that however my guess would be for efficiency. Buffers are much more efficient at handling memory. For example when you slice, it can reuse the same memory vs allocating more memory.