I'm tinkering around with Django Rest Framework to build an api that can handle Bills of Materials (BoM) of electronic components.
I'm using django-polymorphic and django-rest-polymorphic so I can use polymorphic models for the components (they have shared attributes, but I still want to handle them in much the same way. The polymorphic models are great for this purpose).
All is well and good until I introduce many-to-many relations with a 'through' attribute. What I would like is a BoM that has several different components, each with a quantity, e.g. BoM1 has 2x470k resistor & 3x 100uF capacitor.
models.py: (pruned a bit to keep this post from being an epic novel)
class BoM(models.Model):
"""Bill of Materials: a list of all parts and their quantities for a given pedal"""
pedal = models.ForeignKey(Pedal, on_delete=models.CASCADE)
variant = models.CharField(max_length=100, blank=True)
electronic_components = models.ManyToManyField(
'ElectronicComponent', through='ElectronicComponentQty', blank=True)
class Meta:
verbose_name = 'Bill of materials'
verbose_name_plural = 'Bills of materials'
def __str__(self):
return str(self.pedal)
class ElectronicComponent(PolymorphicModel):
"""Shared data model for electronic components"""
value = models.CharField(max_length=20)
datasheet = models.FileField(upload_to='uploads/components', blank=True)
def __str__(self):
return self.value
class ElectronicComponentQty(models.Model):
"""Combination of resistor and quantity"""
bom = models.ForeignKey(BoM, on_delete=models.CASCADE)
component = models.ForeignKey(
ElectronicComponent, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
class Meta:
verbose_name = 'Elecronic component quantity'
verbose_name_plural = 'Electronic component quantities'
class Resistor(ElectronicComponent):
"""Resistor data model"""
WATTAGES = [('1/4w', '1/4w'), ('1/8w', '1/8w')]
wattage = models.CharField(max_length=4, choices=WATTAGES, default='1/4w')
class Capacitor(ElectronicComponent):
"""Capacitors (all kinds)"""
VOLTAGE_RATINGS = [
('16V', '16V'),
('35V/50V', '35V/50V'),
]
CAP_TYPES = [
('ceramic disk', 'ceramic disk'),
('film', 'film'),
('electrolytic', 'electrolytic'),
('tantalum', 'tantalum'),
('other', 'other'),
]
capacitor_type = models.CharField(
max_length=20, choices=CAP_TYPES, default='film')
voltage_rating = models.CharField(
max_length=10, choices=VOLTAGE_RATINGS, blank=True)
serializers.py:
class ElectronicComponentSerializer(serializers.ModelSerializer):
class Meta:
model = ElectronicComponent
fields = '__all__'
class ElectronicComponentQtySerializer(serializers.ModelSerializer):
class Meta:
model = ElectronicComponentQty
fields = '__all__'
class BoMSerializer(serializers.ModelSerializer):
electronic_components = ElectronicComponentQtySerializer(
many=True, read_only=True)
class Meta:
model = BoM
fields = '__all__'
class ResistorSerializer(serializers.ModelSerializer):
class Meta:
model = Resistor
fields = '__all__'
class CapacitorSerializer(serializers.ModelSerializer):
class Meta:
model = Capacitor
fields = '__all__'
class ElectronicComponentPolySerializer(PolymorphicSerializer):
model_serializer_mapping = {
Resistor: ResistorSerializer,
Capacitor: CapacitorSerializer,
}
With this code I can create EletronicComponentQty objects no problem. However, when I try to list the BoM (through the serializer), I get:
AttributeError at /pedalparts/boms/
Got AttributeError when attempting to get a value for field bom
on
serializer ElectronicComponentQtySerializer
.
The serializer field might be named incorrectly and not match any
attribute or key on the Capacitor
instance.
Original exception text was: 'Capacitor' object has no attribute
'bom'.
Anyone know how I can solve this? I'm open to any changes that make this work.
As
electronic_components
on theBoM
model refers to theElectronicComponent
model it should not use theElectronicComponentQtySerializer
but one that can serialize the right instances, most likely theElectronicComponentSerializer
or theElectronicComponentPolySerializer
.