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")
ForeignKey is a Django ORM field-to-column mapping for creating and working with relationships between tables in relational databases.
This field can be useful as a primary key of an object if that object extends another object in some way. For example – a model Car has one-to-one relationship with a model Vehicle, i.e. a car is a vehicle. One-to-one relations are defined using OneToOneField field of django.
The only difference between these two is that ForeignKey field consists of on_delete option along with a model's class because it's used for many-to-one relationships while on the other hand, the OneToOneField, only carries out a one-to-one relationship and requires only the model's class.
An abstract model is a base class in which you define fields you want to include in all child models. Django doesn't create any database table for abstract models. A database table is created for each child model, including the fields inherited from the abstract class and the ones defined in the child model.
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.
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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With