Search code examples
pythondjangoforeign-keysabstract

ForeignKey field related to abstract model in Django


I have this model:

class BaseModel(models.Model):
    ....

    class Meta:
        abstract = True


class ModelA(BaseModel):
    ....

class ModelB(BaseModel):
    ....


class MyExtModel(models.Model)
    myfield = models.ForeignKey(BaseModel)

But this is not correct because I have BaseModel like Abstract. Infact I have an error when I try makemigration command.

The error is:

ERRORS:
myapp.MyExtModel.myfield: (fields.E300) Field defines a relation with model 'BaseModel', which is either not installed, or is abstract.

Is there a way to use an abstract base model?

I also tried to use:

myfield = models.ForeignKey(BaseModel, related_name="%(app_label)s_%(class)s_related")

Solution

  • It's not possible to install Foreign Keys to abstract models in Django. You can however install Foreign Keys to a non abstract base class. The only limitation is that the reverse Foreign Key relation will return the base class instances. You can circumvent this limitation by using django-polymorphic.

    Django Polymorphic allows you to query the base class objects but retrieves the child class instances:

    >>> Project.objects.create(topic="Department Party")
    >>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
    >>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
    
    >>> Project.objects.all()
    [ <Project:         id 1, topic "Department Party">,
      <ArtProject:      id 2, topic "Painting with Tim", artist "T. Turner">,
      <ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
    

    To use django polymorphic you only need to declare your models with Polymorphic Model as base class:

    from django.db import models
    from polymorphic.models import PolymorphicModel
    
    class ModelA(PolymorphicModel):
        field1 = models.CharField(max_length=10)
    
    class ModelB(ModelA):
        field2 = models.CharField(max_length=10)
    
    class ModelC(ModelB):
        field3 = models.CharField(max_length=10)
    

    Foreign keys will also return the child class instances, which is what you need I assume:

    # The model holding the relation may be any kind of model, polymorphic or not
    class RelatingModel(models.Model):
        many2many = models.ManyToManyField('ModelA')  # ManyToMany relation to a polymorphic model
    
    >>> o=RelatingModel.objects.create()
    >>> o.many2many.add(ModelA.objects.get(id=1))
    >>> o.many2many.add(ModelB.objects.get(id=2))
    >>> o.many2many.add(ModelC.objects.get(id=3))
    
    >>> o.many2many.all()
    [ <ModelA: id 1, field1 (CharField)>,
      <ModelB: id 2, field1 (CharField), field2 (CharField)>,
      <ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
    

    Take into account that these queries will be slightly less performant.