kontur
kontur

Reputation: 5220

Django reusable Many-to-one definition in reverse

I'm struggling to make a Many-to-one relationship reusable.

Simplified, let's say I have:

class Car(models.Model):
    ...

class Wheel(models.Model):
    car = models.ForeignKey(Car)
    ...

Pretty straight forward. What, however, if I'd like to use my Wheel model also on another model, Bike? Can I define the relationship in reverse, on the "One" side of the relationship?

Defining this as a Many-to-many on the Vehicles would mean the same Wheel could belong to multiple Vehicles, which is not what I want.

Would I have to subclass my Wheel to CarWheel and BikeWheel only to be able to differentiate the Foreignkey for each relationship? Seems like there should be a cleaner solution.

Upvotes: 1

Views: 52

Answers (2)

Sergey Romanyuk
Sergey Romanyuk

Reputation: 21

I never used it myself, but it looks like a use case for generic relations - https://docs.djangoproject.com/en/5.1/ref/contrib/contenttypes/#generic-relations

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models


class Car(models.Model):
    ...

class Bike(models.Model):
    ...

class Wheel(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey("content_type", "object_id")
    ...
bike = Bike.objects.first()
bike_wheel = Wheel(content_object=bike, ...)

car = Car.objects.first()
car_wheel = Wheel(content_object=car, ...)

Upvotes: 1

Lord Elrond
Lord Elrond

Reputation: 15992

If I understand correctly, you will need to "subclass" from a parent model. For instance, Vehicle:

class Vehicle(models.Model):
    pass

class Bike(Vehicle):
    pass

class Wheel(models.Model):
    vehicle = models.ForeignKey(
        Vehicle, 
        on_delete=models.CASCADE,
        related_name='wheels',
    )

Now to access the Wheel instances from a Bike instance:

bike = Bike.objects.first()
bike_wheels = bike.wheels.all()

Upvotes: 1

Related Questions