I'm trying to generate an object that allows values to be stored in a standard unit, but displayed in different units based on the settings for the project. Below is an example where I need to be able to store the temperature of a room. I want the database value to be in Kelvin for ease of calulations, but want the users to be able to view/enter in F or C as the project requires.
The model can take the values from a form and convert them to Kelvin on save. I know this can be done in the form, but I have it here for other data entry methods. A striped down version of the model is as follows:
class Room(models.Model):
name = models.CharField(max_length=255)
temperature = models.FloatField(default=293.15)
def save(self, *args, **kwargs):
self.temperature = convert.temperature(self.temperature,
from_unit=self.plant.temperature_scale)
super(Room, self).save(*args, **kwargs)
I created a ModelForm to use in CreateView and UpdateView. I needed the ModelForm to limit the queryset for other fields (which works fine). Here I was able to convert the temperature field to the desired units for a new object in CreateView, but I can not seem to find the way to pull this off for an existing record
class RoomForm(ModelForm):
def __init__(self, plant, *args, **kwargs):
super (RoomForm,self ).__init__(*args,**kwargs)
self.fields['temperature'].initial = convert.temperature(
value=self.fields['temperature'].initial,
from_unit='K',
to_unit=self.fields['unit'].queryset[0].plant.temperature_scale)
self.instance.temperature = convert.temperature(
value=self.instance.temperature,
from_unit='K',
to_unit=self.fields['unit'].queryset[0].plant.temperature_scale)
class Meta:
model = Room
fields = ['name', 'temperature']
For good measure, here are the views that call this form.
class NewRoom(CreateView):
model = Room
form_class = RoomForm
def get_form_kwargs(self, **kwargs):
form_kwargs = super(NewRoom, self).get_form_kwargs(**kwargs)
form_kwargs["plant"] = Plant.objects.get(slug=self.kwargs['plant'])
return form_kwargs
class EditRoom(UpdateView):
model = Room
form_class = RoomForm
template_name_suffix = "_update_form"
def get_form_kwargs(self, **kwargs):
form_kwargs = super(EditRoom, self).get_form_kwargs(**kwargs)
form_kwargs["plant"] = Plant.objects.filter(slug=self.kwargs['plant'])
return form_kwargs
In the templates (not forms) I use this to display the temperature correctly.
{{ room.temperature|convert_temperature:room.plant.temperature_scale }}
I assume I need to supplement the part where the ModelForm imports the data from the database record, but I could not identify where this happens in the django.forms source code.
I tried calling this manually in the shell
>>> from rooms.models import *
>>> from rooms.forms import *
>>> room = Room.objects.first()
>>> form = RoomForm(room.plant, instance=room)
>>> form.instance.temperature
20.0
>>> form.as_p()
and the rendered form is as follows with the incorrect value for temperature
<p>
<label for="id_name">Name:
</label>
<input id="id_name" maxlength="255" name="name" type="text" value="Testing"
required />
</p>\n
<p>
<label for="id_temperature">Temperature:
</label>
<input id="id_temperature" name="temperature"
step="any" type="number" value="293.15" required />
</p>\n
I'm using Django version 1.10.3 if it's possible this is a bug.
While lucasnadalutti let me in the right spot, it did not manage to do the trick. Overriding the instance passed to the ModelForm.__init__
and calling it again after altering.
class RoomForm(ModelForm):
def __init__(self, plant, *args, **kwargs):
super(RoomForm, self).__init__(*args,**kwargs)
self.instance.temperature = convert.temperature(
value=self.instance.temperature,
from_unit='K', to_unit=self.fields['unit'].queryset[0].plant.temperature_scale)
# Override instance passed to __init__ and call super again
kwargs['instance'] = self.instance
super(RoomForm, self).__init__(*args, **kwargs)
class Meta:
model = Room
fields = ['name', 'temperature']
To modify data in your existing record before rendering, just do it in self.instance
after calling the parent's __init__
.
def __init__(self, plant, *args, **kwargs):
super(RoomForm,self ).__init__(*args,**kwargs)
self.instance.temperature = # ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With