diff --git a/docs/topics/installation.rst b/docs/topics/00-installation.rst similarity index 100% rename from docs/topics/installation.rst rename to docs/topics/00-installation.rst diff --git a/docs/topics/forms.rst b/docs/topics/01-forms.rst old mode 100755 new mode 100644 similarity index 100% rename from docs/topics/forms.rst rename to docs/topics/01-forms.rst diff --git a/docs/topics/measures.rst b/docs/topics/02-measures.rst similarity index 100% rename from docs/topics/measures.rst rename to docs/topics/02-measures.rst diff --git a/docs/topics/settings.rst b/docs/topics/03-settings.rst similarity index 100% rename from docs/topics/settings.rst rename to docs/topics/03-settings.rst diff --git a/docs/topics/storing.rst b/docs/topics/04-storing.rst similarity index 53% rename from docs/topics/storing.rst rename to docs/topics/04-storing.rst index 43b9db8..717fe65 100644 --- a/docs/topics/storing.rst +++ b/docs/topics/04-storing.rst @@ -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 @@ -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``. diff --git a/docs/topics/05-converting.rst b/docs/topics/05-converting.rst new file mode 100644 index 0000000..33e2f41 --- /dev/null +++ b/docs/topics/05-converting.rst @@ -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. diff --git a/docs/topics/use.rst b/docs/topics/06-use.rst similarity index 83% rename from docs/topics/use.rst rename to docs/topics/06-use.rst index cb0bb7a..3b3d76c 100644 --- a/docs/topics/use.rst +++ b/docs/topics/06-use.rst @@ -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 `_ for more information about interacting with measurements.