In Django/South HOWTO create an instance of a model from a different app during DataMigration
Asked Answered
H

4

7

I need to perform a datamigration of a model Answer in app Question. In that script there is a dependency such that I need to create an instance of a model Chapter which is in the app Journal. So, I coded it as follows:

def forwards(self, orm):
    for answer_object in orm.Answer.objects.all():

        #This Works.
        blog, is_created = orm['blog.Post'].objects.get_or_create(title=answer_object.answer[:100])
        blog.save()

        #This DOES NOT work
        chapter, is_created = orm['journal.Chapter'].objects.get_or_create(content_object=blog)
        chapter.save()
        #cleanup task, not relevant to this question below
        answer_object.chapter_ptr = chapter
        answer_object.save()

But as expected this throws an error on " orm['journal.Chapter'].objects.get_or_create(content_object=blog)" saying that

django.core.exceptions.FieldError: Cannot resolve keyword 'content_object' into field.

This is presumably due to content_object being a GenericForeignKey so some operations are not allowed. But I also tried other alternatives for creating the "chapter" object like,

chapter = orm['journal.Chapter'](content_object=blog)
ERROR > TypeError: 'content_object' is an invalid keyword argument for this function

and

chapter = orm.journal.Chapter(content_object=blog)
 ERROR > AttributeError: The model 'journal' from the app 'questions' is not available in this migration. (Did you use orm.ModelName, not orm['app.ModelName']?)

So where am I going wrong? Any pointers appreciated. Thanks.

UPDATE

So since my earlier approach was failing I tried a new tack. The model whose instantiation was failing in my code above i.e. Chapter in the Journal app, I decided to create a datamigration for that instead. I also made sure to --freeze the models I am referring to in the forwards definition. Now this should have been straight forward, I would think. I have my forward code as follows -

def forwards(self, orm):

    for answer_object in orm['questions.Answer'].objects.all():

        #Works, AGAIN!
        blog, is_created = orm['blog.Post'].objects.get_or_create(title=answer_object.answer[:100])
        blog.save()

        # DOES NOT WORK, AGAIN!
        chapter = orm.Chapter(rank=1, content_object=blog)       
        chapter.save()

I would have thought that now since I am creating instance of a model (Chapter) which exists in the subject app (Journal) everything should have worked out. But i am getting the same error.

TypeError: 'content_object' is an invalid keyword argument for this function

It fails at the same point, namely, "content_object". I will post below the model definition if that might help.

class Chapter(models.Model):

    rank = models.IntegerField()

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey()

UPDATE 2 Wanted to add that all the models being touched in these forwards methods, namely - blog, chapter, questions; are fully defined in the 00n_*.py files created by South's schemamigration.

Harlandharle answered 8/8, 2011 at 0:54 Comment(0)
H
8

After getting help from Rob and folks on the South & Django user groups I was able to resolve this issue. Below is the definition of my forwards datamigration script.

def forwards(self, orm):

    for answer_object in orm['questions.Answer'].objects.all():


        blog, is_created = orm['blog.Post'].objects.get_or_create(title=answer_object.answer[:100])
        blog.save()

        #I have to manually lookup the content_type ans set it in the chapter creation.
        ct = orm['contenttypes.ContentType'].objects.get(app_label="blog", model="post")    
        chapter = orm.Chapter(rank=1, content_type=ct, object_id=blog.id)

        chapter.save()
Harlandharle answered 13/8, 2011 at 19:3 Comment(0)
S
1

This has sort of been answered before here Django South: Creating schemamigration for more than one app

Basically south doesn't currently support multiple applications.

If you don't want to entangle your applications even more than they are, I would use raw SQL in a db.execute as a quick fix.

It looks like blog and journal are very inter-related. Are you sure you want them in separate applications?

Stu answered 8/8, 2011 at 1:5 Comment(6)
But I am confused about why did it allow the "blog" instantiation, and not allow the "chapter" instantiation? Re: your question about putting blog & journal together - actually I watered down my example here for the sake of simplicity. There are other models like audio, image, video etc that can be in the journal. So I need to keep them separate.Harlandharle
Now that I think of it, I am not really migrating the other apps in my subject app. I am just creating new instances during the data-migration of the subject app.Harlandharle
The problem is that south only creates orm entries for the current application and some select django specific models (auth and sites for example). You are referencing entities that don't exist in that .py file. That's what the fabriate example in the other answer tries to rectify.Stu
Hmmm. I think I am too tired to make sense of this. Sorry. I repeatedly get stuck at the point "it created blog, but it does not create chapter" part. Why such inconsistency? Maybe I am just misunderstanding something very basic here.Harlandharle
The basic misunderstanding is that South doesn't use your models.py definitions, it has the entire model expressed in the 000n_some_migration.py. And it only has the current applications models plus a few from django such as auth, contenttypes, etc.Stu
Rob, if you can kindly look at the Update I just posted & let me know what you think, that will be great, thanks for your time till now.Harlandharle
B
0

In order to create a migration via manage.py, pass --freeze=other_app as arguments to add the models definitions of this other_app to the migration itself.

Bashan answered 31/1, 2014 at 16:26 Comment(0)
H
0

This is a comment on Chantz's answer, which worked great for me (Don't have the rep to post this as a comment)

To save some others some time, you also need to freeze the contenttype app when you create your migration:

python manage.py datamigration yourapp migrationname --freeze contenttypes
Hugely answered 20/10, 2014 at 22:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.