Index: django/db/models/base.py =================================================================== --- django/db/models/base.py (revision 7996) +++ django/db/models/base.py (working copy) @@ -170,7 +170,6 @@ def __init__(self, *args, **kwargs): dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) - # There is a rather weird disparity here; if kwargs, it's set, then args # overrides it. It should be one or the other; don't duplicate the work # The reason for the kwargs check is that standard iterator passes in by @@ -187,6 +186,8 @@ # is *not* consumed. We rely on this, so don't change the order # without changing the logic. for val, field in izip(args, fields_iter): + if field.get_internal_type() in ['BlobField', 'BinaryField']: + val = buffer(val) setattr(self, field.attname, val) else: # Slower, kwargs-ready version. Index: django/db/models/fields/__init__.py =================================================================== --- django/db/models/fields/__init__.py (revision 7996) +++ django/db/models/fields/__init__.py (working copy) @@ -157,7 +157,10 @@ # exactly which wacky database column type you want to use. data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_") try: + # print get_creation_module().DATA_TYPES, self.get_internal_type(), data + # print get_creation_module().DATA_TYPES[self.get_internal_type()] return get_creation_module().DATA_TYPES[self.get_internal_type()] % data + except KeyError: return None @@ -349,6 +352,22 @@ def get_validator_unique_lookup_type(self): return '%s__exact' % self.name + def get_manipulator_new_file_data(self, new_data, rel=False): + """ + Return the file data in order to overcome some nasty + corner cases + """ + if rel: + val = new_data.get(self.name+"_file", [self.get_default()]) + try: + return val[0] + except(IndexError): + return self.get_default() + val = new_data.get(self.name+"_file",self.get_default()) + if not self.empty_strings_allowed and val == '' and self.null: + val = None + return val + def get_manipulator_new_data(self, new_data, rel=False): """ Given the full new_data dictionary (from the manipulator), returns this @@ -518,6 +537,37 @@ 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_manipulator_field_objs(self): + # XXX We should probably use a better form field for this. Like + # XXX something that can grok colon-separated hexlify. + # + return [django.forms.TextField] + + 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" + + + def get_manipulator_field_objs(self): + # XXX We should probably use a better form field for this. Like + # XXX something that can grok colon-separated hexlify. + # + return [django.forms.TextField] + + # TODO: Maybe move this into contrib, because it's specialized. class CommaSeparatedIntegerField(CharField): def get_manipulator_field_objs(self): Index: django/db/backends/postgresql/creation.py =================================================================== --- django/db/backends/postgresql/creation.py (revision 7996) +++ 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 7996) +++ 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/introspection.py =================================================================== --- django/db/backends/mysql/introspection.py (revision 7996) +++ django/db/backends/mysql/introspection.py (working copy) @@ -75,7 +75,7 @@ return indexes DATA_TYPES_REVERSE = { - FIELD_TYPE.BLOB: 'TextField', + FIELD_TYPE.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 7996) +++ 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)', @@ -13,8 +15,8 @@ 'FileField': 'varchar(%(max_length)s)', 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', + 'IPAddressField': 'char(15)', 'IntegerField': 'integer', - 'IPAddressField': 'char(15)', 'NullBooleanField': 'bool', 'OneToOneField': 'integer', 'PhoneNumberField': 'varchar(20)', Index: django/db/backends/__init__.py =================================================================== --- django/db/backends/__init__.py (revision 7996) +++ django/db/backends/__init__.py (working copy) @@ -146,9 +146,9 @@ # Convert params to contain Unicode values. to_unicode = lambda s: force_unicode(s, strings_only=True) if isinstance(params, (list, tuple)): - u_params = tuple([to_unicode(val) for val in params]) + u_params = tuple([to_unicode(repr(val)) for val in params]) else: - u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()]) + u_params = dict([(to_unicode(k), to_unicode(repr(v))) for k, v in params.items()]) return smart_unicode(sql) % u_params 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,63 @@ +#!/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) + + + 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,19): + s += chr(i) + 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) + +