I have this relationship between my models:
The model called MyModel might have a foreign key to model A or model B, How can I do this in Django? I heard that the solution called Polymorphic relationship but could not find the correct way to implement it.
My code:
from django.db import models
class A(models.Model):
email = models.EmailField()
national_id = models.IntegerField()
class B(models.Model):
name = models.CharField(max_length=255)
age = models.IntegerField()
class MyModel(models.Model):
name = models.CharField(max_length=255)
provider = models.ForeignKey(to=A, on_delete=models.CASCADE)
CodePudding user response:
It would be against sql rules.
If it does not have to be strictly set in ForeignKey
, you can change approach a bit to get the proper object by MyModel.provider()
function:
1st option:
class MyModel(models.Model):
...
a = models.ForeignKey(to=A, on_delete=models.CASCADE)
b = models.ForeignKey(to=B, on_delete=models.CASCADE)
def provider(self):
return self.a if self.a else self.b
2nd option:
class MyModel(models.Model):
CHOICES = (('a', 'a'), ('b', 'b'))
...
a = models.ForeignKey(to=A, on_delete=models.CASCADE)
b = models.ForeignKey(to=B, on_delete=models.CASCADE)
provider_type = models.CharField(max_length=255, choices=CHOICES)
def provider(self):
return getattr(self, self.provider_type)
CodePudding user response:
It's likely you'll want to use django-polymorphic. To use their example, I think it shows what you're looking for;
from polymorphic.models import PolymorphicModel
class Project(PolymorphicModel):
topic = models.CharField(max_length=30)
class ArtProject(Project):
artist = models.CharField(max_length=30)
class ResearchProject(Project):
supervisor = models.CharField(max_length=30)
Querying Project.objects.all()
will return all instances of Project
, ArtProject
and ResearchProject
.
Django provides an alternative as well which you may not have heard of called a generic foreign key. It makes use of django's content types to link to any type of object that has a content type. To make use of this I use a model mixin to provide the necessary fields;
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import gettext_lazy as _
class GenericForeignKeyMixin(models.Model):
"""
Abstract mixin for adding a GenericForeignKey (named reference) to a model.
"""
class Meta:
"""
Metadata
"""
abstract = True
content_type = models.ForeignKey(
verbose_name=_('Content type'),
to=ContentType,
blank=True,
null=True,
on_delete=models.CASCADE
)
object_id = models.PositiveIntegerField(
verbose_name=_('Object ID'),
blank=True,
null=True,
db_index=True,
)
reference = GenericForeignKey('content_type', 'object_id')