Index: django/db/models/fields/__init__.py =================================================================== --- django/db/models/fields/__init__.py (revision 8020) +++ django/db/models/fields/__init__.py (working copy) @@ -518,6 +518,25 @@ defaults.update(kwargs) return super(CharField, self).formfield(**defaults) +class BinaryField(Field): + """Sometimes we have fields that need to store a small amount of binary + data. This is different then a varchar on postgresql at least because it + will not allow fields that are just nul bytes. + """ + + def get_internal_type(self): + return "BinaryField" + + +class BlobField(Field): + """Sometimes we have fields that need to store a large amounts of binary + data. + """ + + def get_internal_type(self): + return "BlobField" + + # TODO: Maybe move this into contrib, because it's specialized. class CommaSeparatedIntegerField(CharField): def get_manipulator_field_objs(self): Index: django/db/backends/postgresql/introspection.py =================================================================== --- django/db/backends/postgresql/introspection.py (revision 8020) +++ django/db/backends/postgresql/introspection.py (working copy) @@ -71,6 +71,7 @@ # Maps type codes to Django Field types. DATA_TYPES_REVERSE = { 16: 'BooleanField', + 17: 'BlobField', 21: 'SmallIntegerField', 23: 'IntegerField', 25: 'TextField', Index: django/db/backends/postgresql/creation.py =================================================================== --- django/db/backends/postgresql/creation.py (revision 8020) +++ django/db/backends/postgresql/creation.py (working copy) @@ -4,6 +4,8 @@ # If a column type is set to None, it won't be included in the output. DATA_TYPES = { 'AutoField': 'serial', + 'BinaryField': 'bytea', + 'BlobField': 'bytea', 'BooleanField': 'boolean', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', Index: django/db/backends/sqlite3/creation.py =================================================================== --- django/db/backends/sqlite3/creation.py (revision 8020) +++ django/db/backends/sqlite3/creation.py (working copy) @@ -3,6 +3,8 @@ # schema inspection is more useful. DATA_TYPES = { 'AutoField': 'integer', + 'BinaryField': 'BLOB', + 'BlobField': 'BLOB', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', Index: django/db/backends/mysql/base.py =================================================================== --- django/db/backends/mysql/base.py (revision 8020) +++ django/db/backends/mysql/base.py (working copy) @@ -44,6 +44,10 @@ FIELD_TYPE.TIME: util.typecast_time, FIELD_TYPE.DECIMAL: util.typecast_decimal, FIELD_TYPE.NEWDECIMAL: util.typecast_decimal, + FIELD_TYPE.TINY_BLOB: buffer, + FIELD_TYPE.MEDIUM_BLOB: buffer, + FIELD_TYPE.LONG_BLOB: buffer, + FIELD_TYPE.BLOB: buffer, }) # This should match the numerical portion of the version numbers (we can treat Index: django/db/backends/mysql/introspection.py =================================================================== --- django/db/backends/mysql/introspection.py (revision 8020) +++ django/db/backends/mysql/introspection.py (working copy) @@ -75,7 +75,10 @@ return indexes DATA_TYPES_REVERSE = { - FIELD_TYPE.BLOB: 'TextField', + FIELD_TYPE.BLOB: 'BlobField', + FIELD_TYPE.LONG_BLOB: 'BlobField', + FIELD_TYPE.TINY_BLOB: 'BlobField', + FIELD_TYPE.MEDIUM_BLOB: 'BlobField', FIELD_TYPE.CHAR: 'CharField', FIELD_TYPE.DECIMAL: 'DecimalField', FIELD_TYPE.DATE: 'DateField', Index: django/db/backends/mysql/creation.py =================================================================== --- django/db/backends/mysql/creation.py (revision 8020) +++ django/db/backends/mysql/creation.py (working copy) @@ -4,6 +4,8 @@ # If a column type is set to None, it won't be included in the output. DATA_TYPES = { 'AutoField': 'integer AUTO_INCREMENT', + 'BinaryField': 'varbinary(%(max_length)s)', + 'BlobField': 'blob', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', Index: django/db/backends/__init__.py =================================================================== --- django/db/backends/__init__.py (revision 8020) +++ django/db/backends/__init__.py (working copy) @@ -143,8 +143,11 @@ """ from django.utils.encoding import smart_unicode, force_unicode - # Convert params to contain Unicode values. - to_unicode = lambda s: force_unicode(s, strings_only=True) + def to_unicode(s): + if isinstance(s,buffer): + return u'' % len(s) + return force_unicode(s, strings_only=True) + if isinstance(params, (list, tuple)): u_params = tuple([to_unicode(val) for val in params]) else: Index: django/db/backends/postgresql_psycopg2/introspection.py =================================================================== --- django/db/backends/postgresql_psycopg2/introspection.py (revision 8020) +++ django/db/backends/postgresql_psycopg2/introspection.py (working copy) @@ -68,6 +68,7 @@ # Maps type codes to Django Field types. DATA_TYPES_REVERSE = { 16: 'BooleanField', + 17: 'BlobField', 21: 'SmallIntegerField', 23: 'IntegerField', 25: 'TextField', Index: tests/modeltests/binary/__init__.py =================================================================== Index: tests/modeltests/binary/tests.py =================================================================== --- tests/modeltests/binary/tests.py (revision 0) +++ tests/modeltests/binary/tests.py (revision 0) @@ -0,0 +1,67 @@ +#!/usr/bin/python +# encoding: utf-8 + +from models import Document, BinaryFieldTest + +import unittest + + +class BlobFieldTests( unittest.TestCase ): + + def test_blob(self): + test_title = u'Hi there! привет!' + test_descr = u'описание документа' + + s = '' + for i in range(0,256): + s += chr(i) + s+= '\xff\xff' + + + test_data = buffer(s) + + doc = Document(title = test_title, + description = test_descr, + document = test_data) + doc.save() + doc_id = doc.id + doc = None + doc = Document.objects.get(id=doc_id) + assert doc.title == test_title, type(doc.title) + assert doc.description == test_descr, type(doc.description) + assert doc.document == test_data, type(doc.document) + +class BinaryFieldTests( unittest.TestCase ): + + def test_binary(self): + test_title = u'Hi there! привет!' + + s = '' + for i in range(0,10): + s += chr(i) + + s+= '\xff\xff' + + test_data = buffer(s) + + + tst = BinaryFieldTest(title = test_title, + binfield = test_data) + tst.save() + tst_id = tst.id + tst = None + tst = BinaryFieldTest.objects.get(id=tst_id) + assert tst.title == test_title, type(tst.title) + assert tst.binfield == test_data, type(tst.binfield) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(BlobFieldTests), + unittest.makeSuite(BinaryFieldTests) + )) + + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') Index: tests/modeltests/binary/models.py =================================================================== --- tests/modeltests/binary/models.py (revision 0) +++ tests/modeltests/binary/models.py (revision 0) @@ -0,0 +1,18 @@ +""" +BlobField and BinaryField tests + +""" + +from django.db import models + +# Create your models here. +class Document(models.Model): + title = models.CharField(max_length=50) + description = models.TextField() + document = models.BlobField() + +class BinaryFieldTest(models.Model): + title = models.CharField(max_length=20) + binfield = models.BinaryField(max_length=20) + +