Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
42 changes: 36 additions & 6 deletions docs/topics/storing.rst → docs/topics/04-storing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ you'd add it to your log like so::
beer.volume = Volume(us_pint=1)
beer.save()

print beer # '1 us_pint of Total Domination'
print(beer) # '1 us_pint of Total Domination'

Perhaps you next recklessly dove into your stash of terrible,
but nostalgia-inducing Russian beer and had a half-liter of
Expand All @@ -40,17 +40,47 @@ you'd add it to your log like so::
another_beer.volume = Volume(l=0.5)
another_beer.save()

print beer # '0.5 l of #9'
print(beer) # '0.5 l of #9'

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::

print beer.volume # '1 us_pint'
print another_beer.volume # '0.5 l'
print beer.volume > another_beer.volume # False
print(beer.volume) # '1 us_pint'
print(another_beer.volume) # '0.5 l'
print(beer.volume > another_beer.volume) # False


How is this data stored?
------------------------

Since django-measurement v2.0 there value will be stored in a single float field.
Since django-measurement v2.0 the value will be stored in a single float field.
This is of course unit less, but the value stored will be interpreted to be
in the ``STANDARD_UNIT`` of the ``measurement.measures`` class.

For example, for a measure that looks like so::

class Production(MeasureBase):
STANDARD_UNIT = "g"
UNITS = {
"g": 1.0,
"t": 1000000.0,
"lb": 453.59237,
"cwt": 45359.237,
}
ALIAS = {
"gram": "g",
"metric tonne": "t",
"metric ton": "t",
"tonne": "t",
"pound": "lb",
"hundredweight": "cwt",
"short hundredweight": "cwt",
}
SI_UNITS = ["g"]

the value will be stored in the database in grams, because that is the
``STANDARD_UNIT``.

For a ``BidimensionalMeasure`` the value in the database is stored in units
that correspond to the ``STANDARD_UNIT`` of the ``PRIMARY_DIMENSION`` over the
``STANDARD_UNIT`` of the ``REFERENCE_DIMENSION``.
90 changes: 90 additions & 0 deletions docs/topics/05-converting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
Converting existing Fields to MeasurementField
==============================================

Field is already in the 'Standard' Unit
---------------------------------------

This is the trivial case. We can just replace the existing field with a
measurement field::

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

class BeerConsumptionLogEntry(models.Model):
name = models.CharField(max_length=255)
volume = models.FloatField(
help_text="Volume of beer consumed in litres"
) # Field to be converted

def __str__(self):
return '%s of %s' % (self.name, self.volume)

goes to::

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

class BeerConsumptionLogEntry(models.Model):
name = models.CharField(max_length=255)
volume = MeasurementField(measurement=Volume)

def __str__(self):
return '%s of %s' % (self.name, self.volume)

Because ``Volume`` uses the litre as its ``STANDARD_UNIT`` everything is happy.
Run ``makemigrations`` and everyone is happy.

Field in non 'Standard' Units
-----------------------------

In this case it's necessary to first add a data migration that converts the
field into standard units. Say we have a model like this::

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

class BeerConsumptionLogEntry(models.Model):
name = models.CharField(max_length=255)
volume = models.FloatField(
help_text="Volume of beer consumed in hectolitres"
) # Field to be converted

def __str__(self):
return '%s of %s' % (self.name, self.volume)

We first need to convert the volume field to liters like so::

litres_per_hectolitre = 100

def convert_field(apps, _):
BeerConsumptionLogEntry = apps.get_model("beer", "BeerConsumptionLogEntry")

BeerConsumptionLogEntry.all_objects.update(
volume=Subquery(
BeerConsumptionLogEntry.objects.filter(id=OuterRef("id"))
.annotate(converted_volume=F("volume") * litres_per_hectolitre)
.values("converted_volume")[:1]
)
)


def convert_field_reverse(apps, _):
BeerConsumptionLogEntry = apps.get_model("beer", "BeerConsumptionLogEntry")

BeerConsumptionLogEntry.all_objects.update(
volume=Subquery(
BeerConsumptionLogEntry.objects.filter(id=OuterRef("id"))
.annotate(converted_volume=F("volume") / litres_per_hectolitre)
.values("converted_volume")[:1]
)
)

And then hook those two functions up into a RunPython migration as per usual.
Having an actual reverse migration here is critical. It is also be critical
that it is legitimately the inverse. Multiplying and or dividing by the exact
same constant ensures this.

Now you can just convert the field to a measurement field as above.
4 changes: 2 additions & 2 deletions docs/topics/use.rst → docs/topics/06-use.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ and use it for easily handling measurements like so::
from measurement.measures import Weight

w = Weight(lb=135) # Represents 135lbs
print w # '135.0 lb'
print w.kg # '61.234919999999995'
print(w) # '135.0 lb'
print(w.kg) # '61.234919999999995'

See `Python-measurement's documentation <http://python-measurement.readthedocs.org/en/latest/topics/use.html>`_
for more information about interacting with measurements.
Expand Down
Loading