Single 'through' model for multiple m2m fields

I'm trying to extend and refactor some legacy project. I have some set of models which represent "hardware" of some assebled device. Something like :

class ComponentA(models.Model)
    brand = models.CharField()
    model = models.CharField()
    category = models.ForeignKey(Category)

class Configuration(models.Model):
    component_a = ForeignKey(ComponentA)
    component_b = ForeignKey(ComponentB)
    component_n...

class Device(models.Model):
    configuration = models.ForeignKey(Configuration)

The goal is to extent ComponentA...ComponentN with Many2Many field which suppose to contain some extra parts and those parts quantity it consist of. However I found it weird to have through model for each Component model. All of the Component models shoud have a quantity for further statistic/calculations. Is there is some "clean" approach for implementation such a functionality?

The model ComponentListing allows you to add a quantity for each item of a configuration. The Configuration now can hold an arbitrarily chosen amount of Components through ComponentsListing which adds said quantity to it.

Spend some thought how you want to set up the on_delete functionality.

# models.py
class Component(models.Model):
    brand = models.CharField(max_length=XXX)
    model = models.CharField(max_length=XXX)
    category = models.ForeignKey(Category, on_delete=models.XXX)

class ComponentListing(models.Model):
    component = models.ForeignKey(Component, on_delete=models.XXX, related_name='listings')
    quantity = models.IntegerField()
    configuration = models.ForeignKey('Configuration', on_delete=models.XXX, related_name='component_listings')

    def __str__(self):
        return f"{self.component} - {self.quantity}"
    
class Configuration(models.Model):
    # no field required

class Device(models.Model):
    configuration = models.ForeignKey(Configuration, on_delete=models.XXX)
# python manage.py shell
d = Device.objects.get(id=XXX)

for listing in d.configuration.component_listings.all():
    print(listing)
    print(listing.quantity)
    print(listing.component.model)
    print(listing.component.category.XXX)
Back to Top