coddingtonbear / django-measurement

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

Unable to save through Django admin #112

Open devenvyas opened 1 year ago

devenvyas commented 1 year ago

Hi,

When I try to save an admin form which has a MeasurementField, I get the following error:

    change_message = self.construct_change_message(
  File "/home/ubuntu/ENTER/envs/athena39/lib/python3.9/site-packages/django/contrib/admin/options.py", line 1179, in construct_change_message
    return construct_change_message(form, formsets, add)
  File "/home/ubuntu/ENTER/envs/athena39/lib/python3.9/site-packages/django/contrib/admin/utils.py", line 522, in construct_change_message
    changed_data = form.changed_data
  File "/home/ubuntu/ENTER/envs/athena39/lib/python3.9/site-packages/django/utils/functional.py", line 49, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/home/ubuntu/ENTER/envs/athena39/lib/python3.9/site-packages/django/forms/forms.py", line 484, in changed_data
    return [name for name, bf in self._bound_items() if bf._has_changed()]
  File "/home/ubuntu/ENTER/envs/athena39/lib/python3.9/site-packages/django/forms/forms.py", line 484, in <listcomp>
    return [name for name, bf in self._bound_items() if bf._has_changed()]
  File "/home/ubuntu/ENTER/envs/athena39/lib/python3.9/site-packages/django/forms/boundfield.py", line 154, in _has_changed
    return field.has_changed(initial_value, self.data)
  File "/home/ubuntu/ENTER/envs/athena39/lib/python3.9/site-packages/django/forms/fields.py", line 1164, in has_changed
    initial = self.widget.decompress(initial)
  File "/home/ubuntu/ENTER/envs/athena39/lib/python3.9/site-packages/django_measurement/forms.py", line 36, in decompress
    unit = value.STANDARD_UNIT
AttributeError: 'str' object has no attribute 'STANDARD_UNIT'

The decompress function in the widget at times will receive the value arg as str => 120.0 g and breaks because it expects the value to be an instance of measurement class. This is preventing me from saving changes through the Django admin panel.

The following (adding an instance check) seems to be working although I am not sure if this is the right way to handle the issue:

def decompress(self, value):
    if value:
        if isinstance(value, str):
            magnitude, unit = [v.strip() for v in value.split(' ')]
            return [float(magnitude), unit]
        elif isinstance(value, MeasureBase):
            choice_units = set([u for u, n in self.unit_choices])
            unit = value.STANDARD_UNIT
            if unit not in choice_units:
                unit = choice_units.pop()

            magnitude = getattr(value, unit)
            return [magnitude, unit]

    return [None, None]

You can recreate this issue with the following model:

from django.db import models
from django_measurement.models import MeasurementField
from measurement.measures import Weight

def zero_weight():
  return Weight(kg=0)

class Product(models.Model):
  net_weight = MeasurementField(measurement=Weight, default=zero_weight)