Model inheritance with constraints

204 views
Skip to first unread message

Arnab Banerji

unread,
May 25, 2016, 3:32:08 PM5/25/16
to Django users
Hi all,

I currently have a set of models associated with my Django app, with the database already containing data with respect to these tables (models). 

What I have
=========

class MyFunModel(models.Model):
    my_foo_field = <blah>

What I am attempting to add
=====================

class MyAnotherFunModel(MyFunModel):
    my_another_foo_field = <blah>

Such that the migration gives me *only* <app>_myanotherfunmodel table with fields "my_foo_field" and "my_another_foo_field", *without* touching any data in the <app>_myfunmodel table. 

None of the options in https://docs.djangoproject.com/en/dev/topics/db/models/#model-inheritance seem to address this case, or maybe I am missing something?

Thanks,
AB

Akhil Lawrence

unread,
May 28, 2016, 10:21:49 AM5/28/16
to Django users
Its already addressed.

You can simply run ./manage.py makemigrations and ./manage.py migrate

it will create migrations only for MyAnotherFunModel

James Schneider

unread,
May 29, 2016, 3:05:42 AM5/29/16
to django...@googlegroups.com

What I have
=========

class MyFunModel(models.Model):
    my_foo_field = <blah>

What I am attempting to add
=====================

class MyAnotherFunModel(MyFunModel):
    my_another_foo_field = <blah>

Such that the migration gives me *only* <app>_myanotherfunmodel table with fields "my_foo_field" and "my_another_foo_field", *without* touching any data in the <app>_myfunmodel table. 

None of the options in https://docs.djangoproject.com/en/dev/topics/db/models/#model-inheritance seem to address this case, or maybe I am missing something?

If I understand your requirements, then yes, you are missing something. I believe you are being affected by a consequence of multi-table inheritance in Django:


The first paragraph talks about an 'automatic' OneToOne relationship between the parent and child tables being created (which is annoying IMO). This is likely what you are seeing in your migrations. I don't think that MyFunModel is being directly affected (at least in the DB), but you are likely seeing an extra table being built for this new relationship with MyAnotherFunModel.

To get around this, you would need to redefine your common fields into an abstract model, and then your two concrete models would inherit from your new abstract model:


class FunAbstractModel(models.Model):
    my_foo_field = <blah>
    
    class Meta:
        abstract = True

class MyFunModel(FunAbstractModel):
    pass

class MyAnotherFunModel(FunAbstractModel):
    my_another_foo_field = <blah>

Both of your concrete models (MyFunModel and MyAnotherFunModel) will contain a 'my_foo_field' field, but MyAnotherFunModel will also contain 'my_another_foo_field'. There will also be no automatic relationship built between the two models since they no longer inherit from each other (both inherit from FunAbstractModel, which is abstract so it doesn't directly affect your database). 

You should be able to make this change transparently and get the results you want if you replicate all of the appropriate fields in FunAbstractModel that are currently used by MyFunModel.

I personally never use MTI and always use abstract models to handle these cases. If I need a OneToOne, I enter it manually in the code so that I don't have to remember it is there down the road, since I usually only look at the model definition and not check through the inheritance tree for magic relationships. I haven't personally found a use-case where I was fine with an implicit relationship rather than an explicit one.

-James

Arnab Banerji

unread,
Jun 1, 2016, 9:40:58 AM6/1/16
to Django users
Hi Akhil - my issue was how to avoid the new OneToOne relationships, which James addressed on this post.

Hi James - thanks a bunch for your help, much appreciated. While I like the WYSIWYG nature of this solution, I am not sure if the creation of the abstract base class will cause the data already existing in MyFunModel table to be modified in any way. 

Cheers
AB

Arnab Banerji

unread,
Jun 1, 2016, 3:45:21 PM6/1/16
to Django users
Nevermind my last comment on this thread, when I refactored my existing model into an abstract base class with overrides, and then ran "makemigrations", Django said "no changed detected", so it is merely treated as a code refactor and not a database related change.

Thanks
AB

James Schneider

unread,
Jun 2, 2016, 2:26:20 AM6/2/16
to django...@googlegroups.com
On Wed, Jun 1, 2016 at 12:45 PM, Arnab Banerji <arn...@gmail.com> wrote:
Nevermind my last comment on this thread, when I refactored my existing model into an abstract base class with overrides, and then ran "makemigrations", Django said "no changed detected", so it is merely treated as a code refactor and not a database related change.


Yep, that was the whole intention of using the abstract classes. Glad it worked out for you.

-James 
Reply all
Reply to author
Forward
0 new messages