6

Suppose we have a base model:

class BaseModel(models.Model):
    pass

with some subclasses:

class Submodel1(BaseModel):
    some_field = models.TextField()

...

class Submodel9(BaseModel):
    another_field = models.TextField()

Each submodel is defined in its own Django app. New apps with new submodels can appear.

We also have another model, let's call it RelatedModel, which should have a one-to-one relation to BaseModel:

class RelatedModel(models.Model):
    the_thing = models.OneToOneField(BaseModel, null=True, blank=True)

Is it possible to do define such a relation if BaseModel.Meta.abstract == True? Or without defining BaseModel at all?

I have posted some solutions as answers below, but they seem a bit ugly to me.

utapyngo
  • 6,946
  • 3
  • 44
  • 65

2 Answers2

4

https://stackoverflow.com/a/23547494/517316

Instead of putting the relation to RelatedModel, it is possible to put it to Submodel1 .. Submodel9.

class Submodel1(models.Model):
    some_field = models.TextField()
    related_model = models.OneToOneField(RelatedModel, 
                                         null=True, blank=True, 
                                         related_name='the_thing')

...

class Submodel9(models.Model):
    another_field = models.TextField()
    related_model = models.OneToOneField(RelatedModel,
                                         null=True, blank=True,
                                         related_name='the_thing')

Or, if we make BaseModel abstract, we can define it right in BaseModel:

class BaseModel(models.Model)
    related_model = models.OneToOneField(RelatedModel, 
                                         null=True, blank=True,
                                         related_name='the_thing')

    class Meta:
        abstract = True

This would allow accessing SubmodelX from an instance of RelatedModel using a field named the_thing, just as in the multi-table inheritance example.

Community
  • 1
  • 1
utapyngo
  • 6,946
  • 3
  • 44
  • 65
  • 2
    You may need to specify different `related_name`s for each Submodel. Otherwise you may get errors like "Reverse accessor for 'Submodel1.related_model' clashes with reverse accessor for 'Submodel2.related_model". – Shaun Taylor Sep 16 '20 at 09:42
2

It is possible to achieve with GenericForeignKeys:

class RelatedModel(models.Model):
    content_type_of_the_thing = models.ForeignKey(ContentType)
    id_of_the_thing = models.PositiveIntegerField()
    the_thing = GenericForeignKey('content_type_of_the_thing', 'id_of_the_thing')

    class Meta:
        unique_together   = ('content_type_of_the_thing', 'id_of_the_thing')

    # TODO: restrict `content_type_of_the_thing` by `Submodel1 .. Submodel9` somehow
    # Take into account that new submodels can appear
utapyngo
  • 6,946
  • 3
  • 44
  • 65
  • In my experience use this one. If you put the `onetoone` field on the parents you get undesirable results like: Deleting a `RelatedModel` instance also deletes its parent (`Submodel1, Submodel2`) object. – RickyA May 13 '14 at 10:18
  • OK, how do you restrict `content_type_of_the_thing` by `Submodel1 .. Submodel9` then? I think it is OK to post it as a new answer. – utapyngo May 13 '14 at 11:25
  • I think [this](http://stackoverflow.com/questions/6335986/how-can-i-restrict-djangos-genericforeignkey-to-a-list-of-models) awnser can help with that. – RickyA May 13 '14 at 11:35
  • You could also [consider](http://stackoverflow.com/questions/16655097/django-abstract-models-versus-regular-inheritance) using abstract or inherited models. I am going to use a abstract model for this. – RickyA May 13 '14 at 11:38
  • Thank you, this answer was helpful. But how are you going to use an abstract model for this? – utapyngo May 14 '14 at 03:57