2

Is there a way to inherit from an existing model in order to copy of all its fields (in a DRY manner) without invoking multitable inheritance if the model isn't abstract?

To be clear, I have a model Post, and I'd like to have another model GhostPost that mirrors Post exactly in terms of the available fields, but I do not want it to have multitable inheritance or any kind of relationship with Post. The problem is, Post isn't an abstract model, so Django will initiate multitable inheritance. Is there a way around this?

Update: What I'm looking for here is not a Python-level duplicate of the model, but an actual separate database table that mirrors those fields.

Solution: I followed @Brandon's advice and used an abstract model.

jdotjdot
  • 16,134
  • 13
  • 66
  • 118

2 Answers2

3

You need to use a Proxy model in this case. It will allow you to extend a model that is not abstract but without mutli-table inheritance: https://docs.djangoproject.com/en/1.6/topics/db/models/#proxy-models

A proxy model will keep a single-table, so if you need one table for each model, I would suggest making a common abstract class to inherit from for the Post and GhostPost models.

Brandon Taylor
  • 33,823
  • 15
  • 104
  • 144
  • The issue here is that the proxy models actually operate on the same database table, and what I need here is an entirely different table, just with the same fields. – jdotjdot Jul 26 '14 at 02:31
  • 1
    Gotcha. What about making a common abstract class that `Post` and `GhostPost` inherit from? – Brandon Taylor Jul 26 '14 at 02:34
  • I am considering that, but given I already have a lot of data stored on `Post`, I'm a bit afraid of breaking everything. I might give that a shot though. I was hoping there was a way to inherit from `Post` in an abstract manner--I'm toying right now with metaclasses changing the base class to abstract on its way in. – jdotjdot Jul 26 '14 at 02:35
  • 1
    You should be ok if you refactor from a common abstract class. That's a strategy I've used many times. – Brandon Taylor Jul 26 '14 at 02:36
  • If you edit your answer to include the common abstract class (which is likely what I'll end up going with), I'll be able to accept it. – jdotjdot Jul 26 '14 at 04:26
0

You can add an extra BooleanField is_ghost, and override the default managers on both the original model and the proxy model:

class PostManager(models.Manager):
    def get_querset(self):
        return super(PostManager, self).get_queryset().filter(is_ghost=False)

class Post(models.Model):
    ...
    is_ghost = models.BooleanField(blank=True, default=False)
    objects = PostManager()

class GhostPostManager(models.Manager):
    def get_queryset(self):
        return super(GhostPostManager, self).get_queryset().filter(is_ghost=True)

class GhostPost(Post):
    objects = GhostPostManager()

    Meta:
        proxy = True

This will keep everything in a single table, but the managers and querysets will act like they were two separate models/tables.

The other solution is to use an abstract base class for both models. If you don't change the class name of your original Post model, and inherit from e.g. AbstractPost, I don't think this should cause any problems.

knbk
  • 52,111
  • 9
  • 124
  • 122