36

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")
Safari
  • 11,437
  • 24
  • 91
  • 191

3 Answers3

41

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.

ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193
Sebastian Wozny
  • 16,943
  • 7
  • 52
  • 69
  • 2
    I didn't know PolymorphicModel ...probably this can help me. I not understand one aspect: PolymorphicModel is based on GenericRelation? When is necessary to use GenericRelation (content-type) instead of PolymorphicModel? Probably this question is out of the context of my original question... – Safari May 20 '15 at 09:07
  • 1
    Generic relations are unrelated to Polymorphic models. Generic relations are useful for the kind of generic Model that you have in your applications and that have Foreign Keys to fundamentally different models. I have a generic Image model in my application, and both Event and Team models can have Images. This is a generic relationship. I also have a InternationalTeam model that inherits from Team, and then also Teams will have Images, without me having to specify that explicitly in the model. – Sebastian Wozny May 20 '15 at 09:15
20

When I faced a situation like that where I have to make ForeignKeys to different models I choose to use GenericForeignKey you can check official docs here: Django ContentTypes: Generic Relations

The docs explain quite well how to use it:

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class TaggedItem(models.Model):
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):              # __unicode__ on Python 2
        return self.tag
  • The field content_type store the model that the generic foreign key is pointing to

  • The field object_id store the ID of the foreign key,

  • The field content_object helps you to access directly to the related object based on the other 2 fields

It is not the best solution but it saves me in some projects

Example of using it:

from django.contrib.auth.models import User
guido = User.objects.get(username='Guido')
t = TaggedItem(content_object=guido, tag='bdfl')
t.save()
t.content_object
<User: Guido>
AlvaroAV
  • 10,335
  • 12
  • 60
  • 91
4

Apart from the nice answer with GenericForeignKey, with which I am not quite familiar, sometimes (just sometimes, whenever possible), it pays off to simplify your models with using one-to-one relationships to your 'base' model.

Makes foreign keys management easier after that. If I remember well, foreign key on an abstract class is not possible.

Wtower
  • 18,848
  • 11
  • 103
  • 80