Kaydet (Commit) 8ee1eddb authored tarafından Claude Paroz's avatar Claude Paroz

Add a BinaryField model field

Thanks Michael Jung, Charl Botha and Florian Apolloner for review
and help on the patch.
üst 0f306cad
......@@ -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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment