1

I was working on an app that is supposed to send newsletters to customers. I had a model defined like this

from django.auth.models import User

class Newsletter(models.Model):                                                                  
    owner = models.ForeignKey(User, related_name='+', blank=False)                                                                                                                                                  
    sent = models.BooleanField(default=False)                                                                                                                                                                       
    date_created = models.DateTimeField(auto_now_add=True)                                                                                                                                                          
    date_sent = models.DateTimeField(null=True)                                                                                                                                                                     
    subject = models.CharField(max_length=255)                                                                                                                                                                      
    content = HTMLField()                                                                                                                                                                                           
    recipients = models.ManyToManyField(User, related_name='+')                                                                                                                                                    

Later on, I found out that I might need to send these to people who do not have user accounts, so I defined an email model

class Email(models.Model):                                                                                                                                                                                          
    email = models.CharField(max_length=255)                                                          

and changed the recipients field to read

recipients = models.ManyToManyField(Email, related_name='+')

After this, I ran schemamigration command, but South claimed that there are no changes. I have made several attempts to manually manipulate tables and indexes, but at some point figured that since it is a new app, I can just drop all the existing tables, remove all igrations and recreate initial migration from scratch. This poses a question though, how do I do a migration like this if I really need to preserve the data.

Mad Wombat
  • 14,490
  • 14
  • 73
  • 109
  • Did south notice the new `Email` model? – ptrck Dec 07 '12 at 03:24
  • Yes, it did. But it didn't notice anything about the M2M link. – Mad Wombat Dec 07 '12 at 07:55
  • See the previous migration's freeze and see if the change isn't there already. Sometimes if you make changes to model and do some datamigrations meanwhile this happens. – Krzysztof Szularz Dec 07 '12 at 10:26
  • There were no other migrations, just the initial and the change I described. – Mad Wombat Dec 07 '12 at 12:32
  • Try creating a Newsletter instance in shell and see if you get any error, I remember experiencing something similar and in creation went normal. – PepperoniPizza Dec 07 '12 at 15:14
  • When I try to run the dev server I get all sorts of errors related to M2M link and wrong field names, wrong constraints etc. They happen when I try to manipulate recipients in some way. – Mad Wombat Dec 08 '12 at 09:33
  • I'm having this same issue right now. Since South did not see the m2m change, the database still points to your old table on the m2m table. I'm searching more, but I suspect I'll have to write 3 migrations scripts for this to happen. – Danosaure Jul 15 '13 at 18:32

1 Answers1

0

If you need to preserve data you will need to split it into 3 migrations (schema, data, schema) like this:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.add_column(
            u'yourapp_newsletter_recipients',
            'email_id',
            self.gf(
                'django.db.models.fields.related.ForeignKey'
            )(to=orm.Email)
        )

Notice that you should have your old schema in the migration's models property.

Then your data migration that fills the email field. Then schema migration again:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.delete_column(u'yourapp_newsletter_recipients', 'user_id')

In this one you should already have correct m2m field for recipients field in the migration's models['newslatter']

Marek Brzóska
  • 861
  • 7
  • 7