Skip to content
Advertisement

How to add a new “comment” or “flag” field to every model field of existing model?

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:

  1. By hand add 20 more model fields like temperature_comment = ... but that sounds not very DRY
  2. 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.
  3. Add a model called Value for every value and connect them to Compound via OneToOneFields. But how do I then create a Form for Compound? Because I want to create a Compound utilizing one form. I do not want to create every Value on its own. Also it is not as easy as before, to access and play around with the values inside the Compound 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
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement