Upgrade to django 1.7 - instance becomes unicode
Question: I recently moved from django 1.2.5 to 1.7.0 (A long overdue upgrade) and as
expected alot of things broke. I have been able to fix alot of things however
I am having one major issue.

I have pickled objects stored in the database. In django 1.2.5, I ran the
below commands and below are the results

    
    
    >>> from app.foo.models import MyModel as s
    >>> s.objects.get(id = 34567)
    <MyModel: Foo (bar)>
    >>> x = s.objects.get(id = 34567)
    >>> x.myObject
    <foor.bar.My Class instance at 0x3855878>
    >>> y = x.myObject 
    >>> type(y)
    <type 'instance'>
    

However on django 1.7.0 I get the below

    
    
    >>> from app.foo.models import MyModel as s
    >>> s.objects.get(id = 34567)
    <MyModel: Foo (bar)>
    >>> x = s.objects.get(id = 34567)
    >>> x.myObject
    

>
> VHJlZUFuc3dlckNob2ljZQpwMjYyCihkcDI2MwpnNDEKUydXZWJzaXRlJwpwMjY0CnNnNDMKKGxwMjY1CnNiYShpbm9lLnN1cnZleTIudHJlZXN1cnZleQpUcmVlQW5zd2VyQ2hvaWNlCnAyNjYKKGRwMjY3Cmc0MQpTJ0VwYXBlcicKcDI2OApzZzQzCihscDI2OQpzYmEoaW5vZS5zdXJ2ZXkyLnRyZWVzdXJ2ZXkKVHJlZUFuc3dlckNob2ljZQpwMjcwCihkcDI3MQpnNDEKUydETiBTYWZhcmljb20gTW9iaWxlIEFwcCcKcDI3MgpzZzQzCihscDI3MwpzYmEoaW5vZS5zdXJ2ZXkyLnRyZWVzdXJ2ZXkKVHJlZUFuc3dlckNob2ljZQpwMjc0CihkcDI3NQpnNDEKUydPdGhlcicKcDI3NgpzZzQzCihscDI3NwpzYmFzZzYKZzEKc2cxMQpOc2c2NQpJMDEKc2c5ClMnMScKc2JhZzEyMQphZzEyOQphZzE2NQphZzE3MwphZzE4MAphZzE5MgphZzQKYXNTJ2N1cnJlbnRRdWVzdGlvbicKcDI3OApnNApzUydzZW5kRW5kTWVzc2FnZScKcDI3OQpJMDEKc1MnY2FuU2VuZEVuZE1lc3NhZ2UnCnAyODAKSTAxCnNTJ2Nob2ljZVF1ZXVlJwpwMjgxCihscDI4MgpzYi4='

(This is a snippet of the actual output)

    
    
    >>> y = x.myObject 
    >>> type(y)
    <type 'unicode'>
    

As such I am not able to access the instance methods. When i check the
properties of the object as of 1.7, i get

    
    
    >>> dir(y)
    ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'islower', 'isnumeric', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
    

Here is MyModel:

    
    
    from django.db import models
    from django.contrib.auth.models import User
    from SerializedDataField import SerializedDataFiel
    
    class MyModel(models.Model):
    
        title = models.TextField()
        description = models.TextField()
        creator = models.ForeignKey(User)
        created = models.DateTimeField(auto_now_add = True)
        code = models.TextField()
        active = models.BooleanField(default = None)
    
        """Pickled fresh survey object."""
        myObject = SerializedDataField()
    
        """Can the pickled surveyObj be changed? Once a survey has been activated, myObject cannot be changed"""
        mutable = models.BooleanField(default = None)      
    
        def __unicode__(self):
            return self.title + " (" + self.code + ")"
    

And here is Serialized Data Field

    
    
    from django.db import models
    try:
        import cPickle as pickle
    except:
        import pickle
    
    import base64
    
    class SerializedDataField(models.TextField):
        """Because Django for some reason feels its needed to repeatedly call
        to_python even after it's been converted this does not support strings."""
        __metaclass__ = models.SubfieldBase
    
        def to_python(self, value):
            if value is None: return 
            if not isinstance(value, basestring): return value
            try:
                value = pickle.loads(base64.b64decode(value))
                return value
            except:
                return value
    
        def get_db_prep_save(self, value *args, **kwargs):
            if value is None: return 
            return base64.b64encode(pickle.dumps(value))
    

How can I access my instance methods if I keep getting getting a unicode
object?


Answer: _All_ text columns in Django are stored as Unicode data. If your column is
really _binary_ data, you can now migrate those to a
[`BinaryField`](https://docs.djangoproject.com/en/1.7/ref/models/fields/#binaryfield)
(the field was only added in Django 1.6).

For your existing data, you need to encode back to a binary byte string, you
can do so with the [Latin-1 (ISO
8859-1)](http://en.wikipedia.org/wiki/ISO/IEC_8859-1) codec; Unicode
codepoints U+0000 through to U+00FF map one-on-one to Latin-1 bytes:

    
    
    y = x.surveyObject.encode('latin1')
    

The mistake then, was treating binary data as text; it never was text to begin
with.

