I have a base64 field that is copied from the django snippet.
https://djangosnippets.org/snippets/1669/
class Base64Field(models.TextField):
"""
https://djangosnippets.org/snippets/1669/
Example use:
class Foo(models.Model):
data = Base64Field()
foo = Foo()
foo.data = 'Hello world!'
print foo.data # will 'Hello world!'
print foo.data_base64 # will print 'SGVsbG8gd29ybGQh\n'
"""
def contribute_to_class(self, cls, name):
if not self.db_column:
self.db_column = name
self.field_name =name+ '_base64'
super(Base64Field, self).contribute_to_class(cls, self.field_name)
setattr(cls, name, property(self.get_data, self.set_data))
def get_data(self, obj):
return base64.decodestring(getattr(obj, self.field_name))
def set_data(self, obj, data):
setattr(obj, self.field_name, base64.encodestring(data))
def deconstruct(self):
ame, path, args, kwargs = super(Base64Field, self).deconstruct()
from pprint import pprint
pprint(vars(self))
return ame, path, args, kwargs
I am facing issues while migrating this field e.g.
class EmailStatus(models.Model):
attachment = Base64Field(null=True, blank=True, db_column='attachment', name="attachment", verbose_name="attachment")
The error I am getting while migrating is
raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, field_name))
django.core.exceptions.FieldDoesNotExist: EmailStatus has no field named u'attachment'
Now I can see why that is happening. But cant figure out a way around it. I think I might need to change something in the deconstruct field. I have tried multiple things for this but all of them broke.
e.g. removing the _base64. It does not work while saving and retrieving data.
I tried changing the name in the migrations file it does not work.
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(name='EmailStatus',
fields=[('attachment', gradsite.gradnotes.models.Base64Field(blank=True, null=True)),])]
I think the migrations auto-detecter is getting confused because of the change in name in contribute_to_class. I am not sure what can be a work around.
class Base64Field(models.TextField):
def contribute_to_class(self, cls, name, private_only=False):
if not self.db_column:
self.db_column = name
self.field_name = name + '_base64'
super().contribute_to_class(cls,
name)
setattr(cls, self.field_name, property(self.get_data, self.set_data))
def get_data(self, obj):
return base64.b64encode(getattr(obj, self.name).encode('utf-8'))
def set_data(self, obj, data):
setattr(obj, self.field_name, base64.b64decode(data).decode('utf-8'))
This seems to work. There was a mix-up between self.field_name
and name
in contribute_to_class
leading to the wrong value being used (hence makemigrations
not picking up the field the second time around/when using migrate
).
I have made python3 specific changes, namely the super
calls and the use of base64
functions. The set_data
method may be wrong (I didn't look into that too much, since you may be using python2 and encoding would differ), but migrations work.
Added bonus: the private_only
argument was missing from your contribute_to_class
method.
Here's what I'm getting:
from test_app import models
e = models.EmailStatus()
e.attachment = "Hello world!"
e.attachment # Prints 'Hello world!'
e.attachment_base64 # Prints b'SGVsbG8gd29ybGQh'
Useful link for you.
https://code.djangoproject.com/ticket/24563
HELPFUL TIPs:
The failing migration: https://github.com/codefisher/djangopress/blob/master/djangopress/forum/migrations/0011_auto_20150426_1821.py
The models: https://github.com/codefisher/djangopress/blob/master/djangopress/forum/models.py
It may be helps to others.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With