Disclaimer: I can wipe out the database anytime. So while answering this, please don’t care about migrations and stuff.
Imagine me having a model with multiple values:
class Compound(models.Model): color = models.CharField(max_length=20, blank=True, default="") brand = models.CharField(max_length=200, blank=True, default="") temperature = models.FloatField(null=True, blank=True) melting_temp = models.FloatField(null=True, blank=True) # more (~20) especially numeric values as model fields
Now I want to add a comment to be stored for every value of that model. For example I want to add a comment “measured in winter” to the temperature
model field.
What is the best approach to do that?
My brainstorming came up with:
- By hand add 20 more model fields like
temperature_comment = ...
but that sounds not very DRY - Add one big json field which stores every comment. But how do I create a Form with such a json field? Because I want to separate each input field for related value. I would probably have to use javascript which I would want to avoid.
- Add a model called
Value
for every value and connect them toCompound
viaOneToOneField
s. But how do I then create a Form forCompound
? Because I want to create aCompound
utilizing one form. I do not want to create everyValue
on its own. Also it is not as easy as before, to access and play around with the values inside theCompound
model.
I guess this is a fairly abstract question for a usecase that comes up quite often. I do not know why I did not find resources on how to accomplish that.
Advertisement
Answer
The Pythonic way is to use a metaclass:
class CommentModelMeta(models.base.ModelBase): def __new__(mcs, name, bases, attrs, **kwargs): for attr_name, attr in list(attrs.items()): if isinstance(attr, models.Field): attrs[f'{attr_name}_comment'] = models.CharField(max_length=20, blank=True, default="") return super().__new__(mcs, name, bases, attrs, **kwargs) class Compound(models.Model, metaclass=CommentModelMeta): color = models.CharField(max_length=20, blank=True, default="") ...
If you need code completion, you can use type hints:
class Compound(models.Model, metaclass=CommentModelMeta): color = models.CharField(max_length=20, blank=True, default="") brand = models.CharField(max_length=200, blank=True, default="") temperature = models.FloatField(null=True, blank=True) melting_temp = models.FloatField(null=True, blank=True) # more (~20) especially numeric values as model fields color_comment: models.CharField temperature_comment: models.CharField