1
0
Fork 1
mirror of https://github.com/coddingtonbear/django-mailbox.git synced 2026-07-09 22:38:19 +02:00

Fixing bad migration; keeping migration for rebuilding associations as a management command.

This commit is contained in:
Adam Coddington 2013-06-11 22:23:27 -07:00
parent 7e957a7bcf
commit 4b70cf6f21
5 changed files with 78 additions and 104 deletions

View file

@ -0,0 +1,71 @@
import email
import hashlib
import logging
from django.core.management.base import BaseCommand
from django_mailbox.models import MessageAttachment, Message
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
class Command(BaseCommand):
""" Briefly, a bug existed in a migration that may have caused message
attachments to become disassociated with their messages. This management
command will read through existing message attachments and attempt to
re-associate them with their original message.
This isn't foolproof, I'm afraid. If an attachment exists twice, it will
be associated only with the most recent e-mail message. That said,
I'm quite sure that the bug in the migration is gone (and you'd have to
have been quite unlucky to have ran the bad migration).
"""
def handle(self, *args, **options):
ATTACHMENT_HASH_MAP = {}
attachments_without_messages = MessageAttachment.objects.filter(
message=None
).order_by(
'id'
)
if attachments_without_messages.count() < 1:
return
for attachment in attachments_without_messages:
md5 = hashlib.md5()
for chunk in attachment.document.file.chunks():
md5.update(chunk)
ATTACHMENT_HASH_MAP[md5.hexdigest()] = attachment.pk
for message_record in Message.objects.all().order_by('id'):
message = email.message_from_string(message_record.body)
if message.is_multipart():
for part in message.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
md5 = hashlib.md5()
md5.update(part.get_payload(decode=True))
digest = md5.hexdigest()
if digest in ATTACHMENT_HASH_MAP:
attachment = MessageAttachment.objects.get(
pk=ATTACHMENT_HASH_MAP[digest]
)
attachment.message = message_record
attachment.save()
logger.info(
"Associated message %s with attachment %s (%s)",
message_record.pk,
attachment.pk,
digest
)
else:
logger.info(
"%s(%s) not found in currently-stored attachments",
part.get_filename(),
digest
)

View file

@ -30,7 +30,7 @@ class Migration(SchemaMigration):
}, },
u'django_mailbox.message': { u'django_mailbox.message': {
'Meta': {'object_name': 'Message'}, 'Meta': {'object_name': 'Message'},
'attachments_old': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'messages_old'", 'blank': 'True', 'to': u"orm['django_mailbox.MessageAttachment']"}), 'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'messages_old'", 'blank': 'True', 'to': u"orm['django_mailbox.MessageAttachment']"}),
'body': ('django.db.models.fields.TextField', [], {}), 'body': ('django.db.models.fields.TextField', [], {}),
'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
@ -47,7 +47,7 @@ class Migration(SchemaMigration):
'Meta': {'object_name': 'MessageAttachment'}, 'Meta': {'object_name': 'MessageAttachment'},
'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': u"orm['django_mailbox.Message']"}) 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments_new'", 'null': 'True', 'to': u"orm['django_mailbox.Message']"})
} }
} }

View file

@ -9,14 +9,14 @@ class Migration(SchemaMigration):
def forwards(self, orm): def forwards(self, orm):
for message in orm['django_mailbox.Message'].objects.all(): for message in orm['django_mailbox.Message'].objects.all():
for attachment in message.attachments_old.all(): for attachment in message.attachments.all():
attachment.message = message attachment.message = message
attachment.save() attachment.save()
def backwards(self, orm): def backwards(self, orm):
for attachment in orm['django_mailbox.MessageAttachment'].objects.all(): for attachment in orm['django_mailbox.MessageAttachment'].objects.all():
if attachment.message: if attachment.message:
attachment.message.attachments_old.add( attachment.message.attachments.add(
attachment attachment
) )
@ -31,7 +31,7 @@ class Migration(SchemaMigration):
}, },
u'django_mailbox.message': { u'django_mailbox.message': {
'Meta': {'object_name': 'Message'}, 'Meta': {'object_name': 'Message'},
'attachments_old': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'messages_old'", 'blank': 'True', 'to': u"orm['django_mailbox.MessageAttachment']"}), 'attachments': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'messages_old'", 'blank': 'True', 'to': u"orm['django_mailbox.MessageAttachment']"}),
'body': ('django.db.models.fields.TextField', [], {}), 'body': ('django.db.models.fields.TextField', [], {}),
'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
@ -48,7 +48,7 @@ class Migration(SchemaMigration):
'Meta': {'object_name': 'MessageAttachment'}, 'Meta': {'object_name': 'MessageAttachment'},
'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': u"orm['django_mailbox.Message']"}) 'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments_new'", 'null': 'True', 'to': u"orm['django_mailbox.Message']"})
} }
} }

View file

@ -1,97 +0,0 @@
# -*- coding: utf-8 -*-
import datetime
import email
import hashlib
import logging
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
logger = logging.getLogger(__name__)
class Migration(SchemaMigration):
def forwards(self, orm):
ATTACHMENT_HASH_MAP = {}
attachments_without_messages = orm[
'django_mailbox.messageattachment'
].objects.filter(message=None).order_by('id')
if attachments_without_messages.count() < 1:
return
for attachment in attachments_without_messages:
md5 = hashlib.md5()
for chunk in attachment.document.file.chunks():
md5.update(chunk)
ATTACHMENT_HASH_MAP[md5.hexdigest()] = attachment.pk
for message_record in orm['django_mailbox.message'].objects.all().order_by('id'):
message = email.message_from_string(message_record.body)
if message.is_multipart():
for part in message.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
md5 = hashlib.md5()
md5.update(part.get_payload(decode=True))
digest = md5.hexdigest()
if digest in ATTACHMENT_HASH_MAP:
attachment = orm[
'django_mailbox.messageattachment'
].objects.get(pk=ATTACHMENT_HASH_MAP[digest])
attachment.message = message_record
attachment.save()
logger.info(
"Associated message %s with attachment %s (%s)",
message_record.pk,
attachment.pk,
digest
)
else:
logger.info(
"%s(%s) not found in currently-stored attachments",
part.get_filename(),
digest
)
def backwards(self, orm):
# No backward migration necessary
pass
models = {
u'django_mailbox.mailbox': {
'Meta': {'object_name': 'Mailbox'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'from_email': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'uri': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'})
},
u'django_mailbox.message': {
'Meta': {'object_name': 'Message'},
'body': ('django.db.models.fields.TextField', [], {}),
'from_header': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'in_reply_to': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'replies'", 'null': 'True', 'to': u"orm['django_mailbox.Message']"}),
'mailbox': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'messages'", 'to': u"orm['django_mailbox.Mailbox']"}),
'message_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'outgoing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'processed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'read': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'subject': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'to_header': ('django.db.models.fields.TextField', [], {})
},
u'django_mailbox.messageattachment': {
'Meta': {'object_name': 'MessageAttachment'},
'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attachments'", 'null': 'True', 'to': u"orm['django_mailbox.Message']"})
}
}
complete_apps = ['django_mailbox']

View file

@ -7,7 +7,7 @@ tests_require=[
setup( setup(
name='django-mailbox', name='django-mailbox',
version='1.10.3', version='1.10.4',
url='http://bitbucket.org/latestrevision/django-mailbox/', url='http://bitbucket.org/latestrevision/django-mailbox/',
description='Import mail from POP3, IMAP, local mailboxes or directly from Postfix or Exim4 into your Django application automatically.', description='Import mail from POP3, IMAP, local mailboxes or directly from Postfix or Exim4 into your Django application automatically.',
author='Adam Coddington', author='Adam Coddington',