Add a BinaryField model field
Thanks Michael Jung, Charl Botha and Florian Apolloner for review and help on the patch.
This commit is contained in:
parent
0f306cad84
commit
8ee1eddb7e
|
@ -7,6 +7,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
|||
# If a column type is set to None, it won't be included in the output.
|
||||
data_types = {
|
||||
'AutoField': 'integer AUTO_INCREMENT',
|
||||
'BinaryField': 'longblob',
|
||||
'BooleanField': 'bool',
|
||||
'CharField': 'varchar(%(max_length)s)',
|
||||
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
||||
|
|
|
@ -17,6 +17,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
|||
|
||||
data_types = {
|
||||
'AutoField': 'NUMBER(11)',
|
||||
'BinaryField': 'BLOB',
|
||||
'BooleanField': 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
|
||||
'CharField': 'NVARCHAR2(%(max_length)s)',
|
||||
'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)',
|
||||
|
|
|
@ -11,6 +11,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
|||
# If a column type is set to None, it won't be included in the output.
|
||||
data_types = {
|
||||
'AutoField': 'serial',
|
||||
'BinaryField': 'bytea',
|
||||
'BooleanField': 'boolean',
|
||||
'CharField': 'varchar(%(max_length)s)',
|
||||
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
||||
|
|
|
@ -9,6 +9,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
|||
# schema inspection is more useful.
|
||||
data_types = {
|
||||
'AutoField': 'integer',
|
||||
'BinaryField': 'BLOB',
|
||||
'BooleanField': 'bool',
|
||||
'CharField': 'varchar(%(max_length)s)',
|
||||
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
||||
|
|
|
@ -1291,3 +1291,30 @@ class URLField(CharField):
|
|||
}
|
||||
defaults.update(kwargs)
|
||||
return super(URLField, self).formfield(**defaults)
|
||||
|
||||
class BinaryField(Field):
|
||||
description = _("Raw binary data")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['editable'] = False
|
||||
super(BinaryField, self).__init__(*args, **kwargs)
|
||||
if self.max_length is not None:
|
||||
self.validators.append(validators.MaxLengthValidator(self.max_length))
|
||||
|
||||
def get_internal_type(self):
|
||||
return "BinaryField"
|
||||
|
||||
def get_default(self):
|
||||
if self.has_default() and not callable(self.default):
|
||||
return self.default
|
||||
default = super(BinaryField, self).get_default()
|
||||
if default == '':
|
||||
return b''
|
||||
return default
|
||||
|
||||
def get_db_prep_value(self, value, connection, prepared=False):
|
||||
value = super(BinaryField, self
|
||||
).get_db_prep_value(value, connection, prepared)
|
||||
if value is not None:
|
||||
return connection.Database.Binary(value)
|
||||
return value
|
||||
|
|
|
@ -394,10 +394,14 @@ if PY3:
|
|||
_iterlists = "lists"
|
||||
_assertRaisesRegex = "assertRaisesRegex"
|
||||
_assertRegex = "assertRegex"
|
||||
memoryview = memoryview
|
||||
else:
|
||||
_iterlists = "iterlists"
|
||||
_assertRaisesRegex = "assertRaisesRegexp"
|
||||
_assertRegex = "assertRegexpMatches"
|
||||
# memoryview and buffer are not stricly equivalent, but should be fine for
|
||||
# django core usage (mainly BinaryField)
|
||||
memoryview = buffer
|
||||
|
||||
|
||||
def iterlists(d):
|
||||
|
|
|
@ -347,6 +347,22 @@ A 64 bit integer, much like an :class:`IntegerField` except that it is
|
|||
guaranteed to fit numbers from -9223372036854775808 to 9223372036854775807. The
|
||||
default form widget for this field is a :class:`~django.forms.TextInput`.
|
||||
|
||||
``BinaryField``
|
||||
-------------------
|
||||
|
||||
.. class:: BinaryField([**options])
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
A field to store raw binary data. It only supports ``bytes`` assignment. Be
|
||||
aware that this field has limited functionality. For example, it is not possible
|
||||
to filter a queryset on a ``BinaryField`` value.
|
||||
|
||||
.. admonition:: Abusing ``BinaryField``
|
||||
|
||||
Although you might think about storing files in the database, consider that
|
||||
it is bad design in 99% of the cases. This field is *not* a replacement for
|
||||
proper :ref.`static files <static-files> handling.
|
||||
|
||||
``BooleanField``
|
||||
----------------
|
||||
|
|
|
@ -53,6 +53,12 @@ UTC. This limitation was lifted in Django 1.6. Use :meth:`QuerySet.datetimes()
|
|||
<django.db.models.query.QuerySet.datetimes>` to perform time zone aware
|
||||
aggregation on a :class:`~django.db.models.DateTimeField`.
|
||||
|
||||
``BinaryField`` model field
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A new :class:`django.db.models.BinaryField` model field allows to store raw
|
||||
binary data in the database.
|
||||
|
||||
Minor features
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -106,6 +106,10 @@ class VerboseNameField(models.Model):
|
|||
class DecimalLessThanOne(models.Model):
|
||||
d = models.DecimalField(max_digits=3, decimal_places=3)
|
||||
|
||||
class DataModel(models.Model):
|
||||
short_data = models.BinaryField(max_length=10, default=b'\x08')
|
||||
data = models.BinaryField()
|
||||
|
||||
###############################################################################
|
||||
# FileField
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ from django.utils import six
|
|||
from django.utils import unittest
|
||||
|
||||
from .models import (Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post,
|
||||
NullBooleanModel, BooleanModel, Document, RenamedField, VerboseNameField,
|
||||
FksToBooleans)
|
||||
NullBooleanModel, BooleanModel, DataModel, Document, RenamedField,
|
||||
VerboseNameField, FksToBooleans)
|
||||
|
||||
from .imagefield import (ImageFieldTests, ImageFieldTwoDimensionsTests,
|
||||
TwoImageFieldTests, ImageFieldNoDimensionsTests,
|
||||
|
@ -424,3 +424,25 @@ class FileFieldTests(unittest.TestCase):
|
|||
field = d._meta.get_field('myfile')
|
||||
field.save_form_data(d, 'else.txt')
|
||||
self.assertEqual(d.myfile, 'else.txt')
|
||||
|
||||
|
||||
class BinaryFieldTests(test.TestCase):
|
||||
binary_data = b'\x00\x46\xFE'
|
||||
|
||||
def test_set_and_retrieve(self):
|
||||
data_set = (self.binary_data, six.memoryview(self.binary_data))
|
||||
for bdata in data_set:
|
||||
dm = DataModel(data=bdata)
|
||||
dm.save()
|
||||
dm = DataModel.objects.get(pk=dm.pk)
|
||||
self.assertEqual(bytes(dm.data), bytes(bdata))
|
||||
# Resave (=update)
|
||||
dm.save()
|
||||
dm = DataModel.objects.get(pk=dm.pk)
|
||||
self.assertEqual(bytes(dm.data), bytes(bdata))
|
||||
# Test default value
|
||||
self.assertEqual(bytes(dm.short_data), b'\x08')
|
||||
|
||||
def test_max_length(self):
|
||||
dm = DataModel(short_data=self.binary_data*4)
|
||||
self.assertRaises(ValidationError, dm.full_clean)
|
||||
|
|
Loading…
Reference in New Issue