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.
|
# If a column type is set to None, it won't be included in the output.
|
||||||
data_types = {
|
data_types = {
|
||||||
'AutoField': 'integer AUTO_INCREMENT',
|
'AutoField': 'integer AUTO_INCREMENT',
|
||||||
|
'BinaryField': 'longblob',
|
||||||
'BooleanField': 'bool',
|
'BooleanField': 'bool',
|
||||||
'CharField': 'varchar(%(max_length)s)',
|
'CharField': 'varchar(%(max_length)s)',
|
||||||
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
||||||
|
|
|
@ -17,6 +17,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
|
|
||||||
data_types = {
|
data_types = {
|
||||||
'AutoField': 'NUMBER(11)',
|
'AutoField': 'NUMBER(11)',
|
||||||
|
'BinaryField': 'BLOB',
|
||||||
'BooleanField': 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
|
'BooleanField': 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
|
||||||
'CharField': 'NVARCHAR2(%(max_length)s)',
|
'CharField': 'NVARCHAR2(%(max_length)s)',
|
||||||
'CommaSeparatedIntegerField': 'VARCHAR2(%(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.
|
# If a column type is set to None, it won't be included in the output.
|
||||||
data_types = {
|
data_types = {
|
||||||
'AutoField': 'serial',
|
'AutoField': 'serial',
|
||||||
|
'BinaryField': 'bytea',
|
||||||
'BooleanField': 'boolean',
|
'BooleanField': 'boolean',
|
||||||
'CharField': 'varchar(%(max_length)s)',
|
'CharField': 'varchar(%(max_length)s)',
|
||||||
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
||||||
|
|
|
@ -9,6 +9,7 @@ class DatabaseCreation(BaseDatabaseCreation):
|
||||||
# schema inspection is more useful.
|
# schema inspection is more useful.
|
||||||
data_types = {
|
data_types = {
|
||||||
'AutoField': 'integer',
|
'AutoField': 'integer',
|
||||||
|
'BinaryField': 'BLOB',
|
||||||
'BooleanField': 'bool',
|
'BooleanField': 'bool',
|
||||||
'CharField': 'varchar(%(max_length)s)',
|
'CharField': 'varchar(%(max_length)s)',
|
||||||
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
|
||||||
|
|
|
@ -1291,3 +1291,30 @@ class URLField(CharField):
|
||||||
}
|
}
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return super(URLField, self).formfield(**defaults)
|
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"
|
_iterlists = "lists"
|
||||||
_assertRaisesRegex = "assertRaisesRegex"
|
_assertRaisesRegex = "assertRaisesRegex"
|
||||||
_assertRegex = "assertRegex"
|
_assertRegex = "assertRegex"
|
||||||
|
memoryview = memoryview
|
||||||
else:
|
else:
|
||||||
_iterlists = "iterlists"
|
_iterlists = "iterlists"
|
||||||
_assertRaisesRegex = "assertRaisesRegexp"
|
_assertRaisesRegex = "assertRaisesRegexp"
|
||||||
_assertRegex = "assertRegexpMatches"
|
_assertRegex = "assertRegexpMatches"
|
||||||
|
# memoryview and buffer are not stricly equivalent, but should be fine for
|
||||||
|
# django core usage (mainly BinaryField)
|
||||||
|
memoryview = buffer
|
||||||
|
|
||||||
|
|
||||||
def iterlists(d):
|
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
|
guaranteed to fit numbers from -9223372036854775808 to 9223372036854775807. The
|
||||||
default form widget for this field is a :class:`~django.forms.TextInput`.
|
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``
|
``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
|
<django.db.models.query.QuerySet.datetimes>` to perform time zone aware
|
||||||
aggregation on a :class:`~django.db.models.DateTimeField`.
|
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
|
Minor features
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,10 @@ class VerboseNameField(models.Model):
|
||||||
class DecimalLessThanOne(models.Model):
|
class DecimalLessThanOne(models.Model):
|
||||||
d = models.DecimalField(max_digits=3, decimal_places=3)
|
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
|
# FileField
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ from django.utils import six
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
|
|
||||||
from .models import (Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post,
|
from .models import (Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post,
|
||||||
NullBooleanModel, BooleanModel, Document, RenamedField, VerboseNameField,
|
NullBooleanModel, BooleanModel, DataModel, Document, RenamedField,
|
||||||
FksToBooleans)
|
VerboseNameField, FksToBooleans)
|
||||||
|
|
||||||
from .imagefield import (ImageFieldTests, ImageFieldTwoDimensionsTests,
|
from .imagefield import (ImageFieldTests, ImageFieldTwoDimensionsTests,
|
||||||
TwoImageFieldTests, ImageFieldNoDimensionsTests,
|
TwoImageFieldTests, ImageFieldNoDimensionsTests,
|
||||||
|
@ -424,3 +424,25 @@ class FileFieldTests(unittest.TestCase):
|
||||||
field = d._meta.get_field('myfile')
|
field = d._meta.get_field('myfile')
|
||||||
field.save_form_data(d, 'else.txt')
|
field.save_form_data(d, 'else.txt')
|
||||||
self.assertEqual(d.myfile, '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