Domanda

First of all, I'm aware that there are many similar question like this, but the other solutions don't cover my specific case:

In my sqlite-database are existing binary data (SHA1 and similar hashes). With googling and reading the django-docs i came up with the following:

import base64

class BlobField(models.Field):
""" 
Stores raw binary data
"""

description = 'Stores raw binary data'
__metaclass__ = models.SubfieldBase

def __init__(self, *args, **kwds):
    super(BlobField, self).__init__(*args, **kwds)

def get_internal_type(self):
    return "BlobField"

def get_db_prep_value(self, value, connection=None, prepared=False):
    return base64.decodestring(value)

def to_python(self, value):
    return base64.encodestring(value)

which does what I want, the value is encoded and decoded at the right moment, but on saving the model into the database it gives me the following error:

DatabaseError: You must not use 8-bit bytestrings unless you use a text_factory that can interpret 8-bit bytestrings (like text_factory = str). It is highly recommended that you instead just switch your application to Unicode strings.

How can I fix this? (Possibly without breaking all my unicode-compatibility in the rest of the app)

I cannot change the format of the db-columns since the data is used by another application.


EDIT: As suggested by @filip-dupanovic, I adopted the BinaryField class as follows:

class BinaryField(models.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, prepared, connection=connection)
    #if value is not None:
    #    return connection.Database.Binary(value)
    return value

Notice the comments I had to insert at get_db_prep_value(), like this, it works as expected, if I uncomment the lines I get an error

TypeError: get_db_prep_value() got multiple values for keyword argument 'connection'

I could live with this, but do not fully understand the implications of leaving it out. Will it still work even without the call to super()?

È stato utile?

Soluzione

Ticket #2417, closed not long ago, added a BinaryField model field. You should look at this commit, specifically the changes related to how you can map your internal BlobField field type to an appropriate type supported by your database.

Altri suggerimenti

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top