coddingtonbear / django-measurement

Easily store, retrieve, and convert measurements of weight, volume, distance, area and more.
MIT License
145 stars 33 forks source link

Form fields are using wrong default unit #87

Closed MidasJade closed 5 years ago

MidasJade commented 5 years ago

This may be related to #30 but I'm not sure. That issue said it was related to the inclusion of Bootstrap 3. I was using Bootstrap 4 and having the same issue. I decided to make a barebones version of a form without any bootstrapping or additional funny stuff and I have the same problem. My quick and dirty code is available here: https://github.com/MidasJade/beer_sample. It's probably something stupid I'm doing but I cannot figure it out.

For the quantity field in my form, no matter what type I use when I create it, it displays ounces in my list view and detail view, but displays grams in the update view. Based on this from the docs "Note that although the original unit specified is stored for display, that the unit is abstracted to the measure’s standard unit for storage and comparison" I'm assuming whatever was used to create it or update it should be what is displayed, right?

codingjoe commented 5 years ago

Hi @MidasJade,

thanks for reaching out. Let me try to explain. This library does not store any information about what unit you initially put in. It translates whatever the users inputs into a SI standard unit and stores the data.

When the data is displayed, as it is in the update view, it uses the default SI unit. In the case of weight this unit is gram.

I hope that answer explains the behavior of the package a bit more.

Best -Joe

MidasJade commented 5 years ago

Hi @codingjoe,

Thanks for the explanation. I misread that in the docs. I guess that leaves me with a couple more questions, but don't want to take up too much of your time.

1) If it saves it in the SI unit, why was my list view showing it in ounces when update view was showing it in grams? 2) It sounds like I won't be able to use built-in Django forms or crispy forms (crispy is what I'm trying to use right now) because there doesn't appear to be a way to programatically influence the measurement type that gets displayed?

Thanks for the help!

codingjoe commented 5 years ago
  1. If it saves it in the SI unit, why was my list view showing it in ounces when update view was showing it in grams?

Once the value in received from the database, it is converted into a measurement – a separate data type that is aware of its unit. So if you display the field value in a list view, it will be displaying that data type. If you don't specify it any other way, it should use Measure.__str__, which should give you grams not ounces. Seealso: https://github.com/coddingtonbear/python-measurement/blob/3efdc3e4304939a73fc55494dcaac9f539a55753/measurement/base.py#L203-L207

In any case, you can also call the unit attribute to get the value in the desired unit, eg:

<p>{{ object.my_field.g }}g</p>
  1. It sounds like I won't be able to use built-in Django forms or crispy forms (crispy is what I'm trying to use right now) because there doesn't appear to be a way to programatically influence the measurement type that gets displayed?

Why not? I have a big project running on crispy forms and this package.

MidasJade commented 5 years ago

I guess I shouldn't say I can't use it with crispy, I just can't use it with the quick FormHelper and Layout method. My Form:

class FermentableForm(ModelForm): class Meta: model = Fermentable exclude = ()

name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Fermentable name'}))
description = forms.CharField(widget=forms.Textarea(attrs={'placeholder': 'Description of the fermentable', 'rows':2 }))
color = forms.DecimalField(widget=forms.TextInput(attrs={'placeholder': 'Lovibond color value'}))
extract = forms.DecimalField(widget=forms.TextInput(attrs={'placeholder': 'Extract % (ie: .75)'}))
moisture = forms.DecimalField(widget=forms.TextInput(attrs={'placeholder': 'Moisture % (ie: .075)'}))
mash_required = forms.BooleanField(widget=forms.CheckboxInput())
quantity = MeasurementField(measurement=Weight, unit_choices=(("oz", "oz"), ("lb", "lb"), ("g", "g"), ("kg", "kg")))

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.helper = FormHelper()
    self.helper.layout = Layout(
        'name',
        'description',
        'color',
        'extract',
        'moisture',
        'mash_required',
        'quantity',
        Submit('submit', 'Save')
    )

And my template:

{% extends "base_generic.html" %} {% load crispy_forms_tags %}

{% block content %} {% crispy form %} {% endblock %}

codingjoe commented 5 years ago

@MidasJade what's the result? Mind adding a screenshot?

MidasJade commented 5 years ago

@CodingJoe

I figured it out. You can't see my imports in what I pasted, but I had imported MeasurementField from models instead of forms. It renders properly and gives you all the unit_choices you specified for the model. If I import the correct module and use the MeasurementField class from forms, then I can limit the unit_choices to just what I want to see (lbs). Then it displays correctly. Thanks again for the help! Great app!