Frederik
Frederik

Reputation: 14556

Can a Django ModelForm display a custom representation of stored values?

In my model, percentage data is stored as fractional values in the 0.0-1.0 range. Because they are percentages, I want the user to view and edit the values in the 0.0-100.0 range.

What is the preferred way of transforming data between its stored value and the displayed value? Note that the format depends on another instance variable.

My model looks like this:

class MyModel(models.Model):
    format = models.CharField(choices=(('percentage', 'Percentage'), ('absolute', 'Absolute'))
    my_value = models.FloatField()

    def _get_my_value_display(self):
        if self.format == 'percentage':
            return self.my_value * 100
        else:
            return self.my_value

    def _set_my_value_display(self, v):
        if self.format == 'percentage':
            return v / 100
        else:
            return self.my_value

    my_value_display = property(_get_my_value, _set_my_value)

In the form, I tried to use the display property as a form field, but that doesn't work (Unknown field(s) (my_value_display) specified for MyModel):

class MyForm(forms.ModelForm):

    class Meta:
        model = MyModel
        fields = ('my_value_display', )

Upvotes: 1

Views: 1288

Answers (1)

koniiiik
koniiiik

Reputation: 4382

Personally, I'd probably try to create a django.forms.FloatField subclass and a django.forms.TextInput subclass, kind of like this:

class PercentageInput(TextInput):
    def render(self, name, value, attrs=None):
        try:
            value = float(value)
            value *= 100
        except ValueError:
            pass
        return super(PercentageInput, self).render(name, value, attrs)

class PercentageField(FloatField):
    widget = PercentageInput
    def to_python(self, value):
        value = super(PercentageField, self).to_python(value)
        return value / 100

Then just define your ModelForm this way:

class MyForm(forms.ModelForm):
    my_value = PercentageField()

It might fail in a few corner cases, maybe also when the user supplies an invalid value, but the basic idea should be all right.

Upvotes: 5

Related Questions