Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ForeignKey field related to abstract model in Django

Tags:

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")
like image 865
Safari Avatar asked May 20 '15 07:05

Safari


People also ask

What is Django ForeignKey model?

ForeignKey is a Django ORM field-to-column mapping for creating and working with relationships between tables in relational databases.

What is Onetoone field in Django?

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.

What is the difference between ForeignKey and OneToOneField?

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.

What is an abstract model Django?

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.


3 Answers

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.

like image 99
Sebastian Wozny Avatar answered Sep 28 '22 04:09

Sebastian Wozny


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> 
like image 22
AlvaroAV Avatar answered Sep 28 '22 04:09

AlvaroAV


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.

like image 31
Wtower Avatar answered Sep 28 '22 03:09

Wtower