Data Migration from ImageField to ImageField using South
I'm trying for several hours now to do the silliest migration using South, but somehow, I've been failing miserably.. I'm trying to migrate to Sorl-Thumbnail.
Here is my transitional model:
class Deal(models.Model):
image = ImageWithThumbsField(upload_to='deal_images',null=True,blank=True,sizes=(200,150),))
new_image = ImageField(upload_to='new_deal_images',default='deal_images/thumb_deal_noimg.gif')
And my foward migration it's this so far:
def forwards(self, orm):
for deal in orm.Deal.objects.all():
try:
image_name = deal.image.name.split('/')[1]
file_ = File(deal.image.open()) # I've also tried the method read()
deal.new_image.save('new_deal_images/'+image_name,file_,save=False)
except:
deal.new_image = None # For the default image kick in
deal.save()
this is the most recent version of this code. All the others, mostly failed to put the image file in the new directory properly.
Help... :)
Time goes by....
Alright... After several tests i got this code:
def forwards(self, orm):
for deal in orm.Deal.objects.all():
file_content = ContentFile(deal.image.read())
deal.new_image.save(deal.image.name,file_content) *
deal.save()
The images are being copied and saved in the new column (new_image), but the thing is that all the files are being saved in the MEDIA_ROOT root, not in the desired sub-directory ('new_deal_images'). I tried this in the * line, 开发者_如何学Gobut still no luck:
deal.new_image.save('new_ideal_images/'+deal.image.name,file_content)
Sill not working...
Please help... :)
Anothe time goes by....
ok... I think that there is some serious problem with South:
This code works perfectly in the Django Shell, copying all the files to the correct place :
15 for deal in Deal.objects.all():
16 image_path = deal.image.path·
17 file_ = File(open(image_path,'rb'))
18 deal.new_image.save(deal.image.name,file_)
19 deal.save()
But this code in the Migration file, does not, dumping all files in the MEDIA_ROOT root directory, without moving it to the correct subdirectory:
15 for deal in orm.Deal.objects.all():
16 image_path = deal.image.path·
17 file_ = File(open(image_path,'rb'))
18 deal.new_image.save(deal.image.name,file_)
19 deal.save()
You can override the field's generate_filename method in the South orm. For example, this would copy all the images in the 'image' field over to the 'new_image' field, just changing the directory they're stored in.
for deal in orm.Deal.objects.all():
deal._meta.get_field('new_image').generate_filename = \
lambda inst, fn: os.path.join('new_deal_images', fn)
img_data = SimpleUploadedfile(deal.image.name, deal.image.read())
deal.image.close()
setattr(deal, 'new_image', img_data)
deal.save()
I had the same problem, and worked around it by assigning a path directly, instead of a file:
for tagged in orm.ImageTag.objects.all():
with tagged.image.content:
filename = tagged.image.content.name.split('/')[1]
path = default_storage.save('taggedImages/' + filename, tagged.image.content)
tagged.imageFile = path
tagged.save()
Thanks to Mike Fogel's answer I solve this problem in a migrations that moves images from one model into another:
from south.v2 import DataMigration
from .. models import upload_by_conditions
from .. models import RESOURCE_IMAGE
class Migration(DataMigration):
def forwards(self, orm):
src = orm['uploader.Queue']
dst = orm['uploader.Resource']
for item in src.objects.all():
obj = dst(
kind=RESOURCE_IMAGE,
file_name=item.file_name,
file_type=item.file_type,
file_size=item.file_size,
)
# here I assign own handler!!!
obj._meta.get_field('resource').generate_filename = upload_by_conditions
obj.resource.save(item.file_name, item.image, save=True)
Migrating an ImageField from One Storage to a Different Storage with South
Although this might not directly answer your question, I figured I'd add some potentially helpful insight to the django south imageField migration topic (which this question is currently the best resource on the web for).
One significant problem with South's handling of image fields in data migrations is that, as a side effect of its ORM-Freezing, it seems to assume the DefaultStorage will be used, since the storage doesn't get frozen. So if you've got a custom storage for your field things probably won't work as expected. But @cberner provides some great insight in that you can save and manipulate the files directly by utilizing the storage directly and then simply set the path to the field and save it. Piggybacking off of his answer, I came up with the following datamigration to migrate all of my existing images from one storage (default, filesystem storage) to a new storage (AWS):
here's the migration from the default storage to the new storage (run simultaneously with adding the new storage to the field definition):
def forwards(self, orm):
new_images_storage = ImagesS3Storage()
for food in orm.Food.objects.all():
try:
new_file = ContentFile(food.image.read()) # south will use the default storage
new_file_path = new_images_storage.save(food_image_upload_to(food, food.image.name), new_file)
food.image = new_file_path
food.save()
except ValueError: # food with no image
continue
(For reference, here's my custom storage with a different bucket than the default AWS bucket, which is already used by my static files storage)
class ImagesS3Storage(S3BotoStorage):
def __init__(self, *args, **kwargs):
kwargs['bucket'] = getattr(settings, 'FOOD_IMAGES_BUCKET_NAME')
super(FoodImagesS3Storage, self).__init__(*args, **kwargs)
Hopefully that will help some lost soul.
精彩评论