Django model relationship to multiple models
Asked Answered
S

2

6

Suppose there is an abstract model CarOwner: whereas a Person or a Business can be a CarOwner. In addition a Car with a certain VIN can belong (relate) to either a Person or a Business, but not both (mutually exclusive case). At the very end of the following code I presented two possibilities (see comments in the code "# 1. SHOULD I HAVE THIS???" and "# 2. ...OR SHOULD I HAVE THIS???"). In the first possibility a Many-to-One relationship is established to an abstract model and I am not sure if this is the right way. In the second case two relationships are established and I am not sure if that is correct either, especially it is not clear how to make them mutually exclusive. So which one is right and if neither, please, provide the right answer if you could. Thanks.

class CarOwner(models.Model):
    location = models.CharField(max_length=50, blank=True)

    class Meta:
        abstract = True

class Person(CarOwner):
    name = models.CharField(max_length=50, blank=True)

class Business(CarOwner):
    business_id = models.CharField(max_length=50, blank=True)

class Car(models.Model):
    vin = models.CharField(max_length=50, blank=True)

    # 1. SHOULD I HAVE THIS??? (CarOwner is abstract)
    carowner = models.ForeignKey(CarOwner, blank=True, null=True)

    # 2. ...OR SHOULD I HAVE THIS???
    person = models.ForeignKey(Person, blank=True, null=True)
    business = models.ForeignKey(Business, blank=True, null=True)
Stately answered 4/1, 2014 at 20:58 Comment(0)
W
9

Like jproffitt mentioned, generic relations might be a good solution for you. Alternatively you can use #2 and make it a bit more convenient by creating property and adding some simple logic to it:

class Car(models.Model):
    vin = models.CharField(max_length=50, blank=True)
    person = models.ForeignKey(Person, blank=True, null=True)
    business = models.ForeignKey(Business, blank=True, null=True)

    @property
    def carowner(self):
        return self.person or self.business

    @carowner.setter
    def carowner(self, obj):
        if type(obj) == Person:
            self.person = obj
            self.business = None
        elif type(obj) == Business:
            self.business = obj
            self.person = None
        else:
            raise ValueError("obj parameter must be an object of Business or Person class")

However for queries you'll have to use person or business.

Woosley answered 5/1, 2014 at 15:44 Comment(2)
Thanks. So, using two ForeignKeys in the same model is actually allowed? From my past explorations of Django doc website I had previously understood that it was only allowed for some cases of Many-to-Many relationships.Stately
Of course it is, I'm not sure which part of Django documentation could suggest something else.Woosley
P
4

Since CarOwner is abstract, you cannot do #1. You could make CarOwner concrete (db table inheritance), and then that would work, but table inheritance brings its own set of complications. You could either do #2 or use a generic foreign key:

carowner_content_type = models.ForeignKey(ContentType)
carowner_object_id = models.PositiveIntegerField()
carowner = generic.GenericForeignKey('carowner_content_type', 'carowner_object_id')
Pelasgian answered 4/1, 2014 at 22:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.