1 from django.db import models
2 from django.forms import ValidationError
4 from taggit.managers import TaggableManager
7 SI = 'SI (International System of Units)'
8 US = 'US (United States of America customary units)'
10 class Unit (models.Model):
11 "Kilograms, pounds, liters, gallons, etc."
23 name = models.CharField(max_length=40)
24 abbrev = models.CharField('abbreviation', max_length=6)
25 type = models.CharField(max_length=1, choices=TYPES)
26 system = models.CharField(max_length=2, choices=SYSTEMS)
27 scale = models.DecimalField(
28 'SI/X-equivalent ratio (e.g. 453.6 for lb (g), 3.78 for gal (L))',
29 max_digits=30, decimal_places=15)
30 offset = models.DecimalField(
31 'X when SI-equivalent is zero (e.g. 32 for degrees Farenheit (C))',
32 max_digits=30, decimal_places=15)
34 def __unicode__(self):
35 return u'{0.abbrev}'.format(self)
37 def convert_from_si(self, value):
38 return value / self.scale + self.offset
40 def convert_to_si(self, value):
41 return (value - self.offset) * self.scale
44 class Amount (models.Model):
45 "1 kg, 2-3 lb., 0.5 (0.3-0.6) gal., etc."
46 value = models.DecimalField(
47 max_digits=10, decimal_places=4, null=True, blank=True)
48 min_value = models.DecimalField(
49 'minimum value', max_digits=10, decimal_places=4,
50 null=True, blank=True)
51 max_value = models.DecimalField(
52 'maximum value', max_digits=10, decimal_places=4,
53 null=True, blank=True)
54 unit = models.ForeignKey(Unit, null=True, blank=True)
59 def format_amount(self):
61 for v in [self.value, self.min_value, self.max_value]:
63 raise ValidationError(v)
65 if self.value is None:
66 if self.min_value is None:
67 if self.max_value is not None:
68 raise ValidationError('cannot only set max_value')
71 if self.max_value is None:
72 raise ValidationError('cannot only set min_value')
73 fmt = u'{0.min_value}-{0.max_value} {0.unit}'
75 if self.min_value is None:
76 if self.max_value is None:
77 fmt = u'{0.value} {0.unit}'
79 fmt = u'{0.value}-{0.max_value} {0.unit}'
81 if self.max_value is None:
82 fmt = u'{0.min_value}-{0.value} {0.unit}'
84 fmt = u'{0.value} ({0.min_value}-{0.max_value}) {0.unit}'
85 return fmt.format(self)
88 if self.value is None:
89 if self.min_value is None and self.max_value is not None:
90 raise ValidationError('cannot only set max_value')
91 elif self.min_value is not None and self.max_value is None:
92 raise ValidationError('cannot only set min_value')
93 if self.value is not None and self.unit is None:
94 raise ValidationError('values must have units')
95 super(Amount, self).clean()
98 class Directions (models.Model):
99 directions_markdown = models.TextField(
100 'directions', help_text='Markdown syntax', blank=True, null=True)
101 directions = models.TextField('directions as HTML', blank=True, null=True)
107 # https://code.djangoproject.com/wiki/UsingMarkup
108 if self.directions_markdown is None:
109 self.directions = None
111 self.directions = markdown.markdown(self.directions_markdown)
112 super(Directions, self).save()
115 class Recipe (Amount, Directions):
116 name = models.CharField(max_length=200)
117 author = models.CharField(max_length=200, null=True, blank=True)
118 source = models.CharField(max_length=200, null=True, blank=True)
119 url = models.URLField('URL', null=True, blank=True)
120 tags = TaggableManager(blank=True)
125 def __unicode__(self):
126 return u'{0.name}'.format(self)
129 class IngredientBlock (Directions):
130 name = models.CharField(max_length=200)
131 recipe = models.ForeignKey(Recipe)
132 position = models.PositiveIntegerField()
135 ordering = ['recipe', 'position']
137 def __unicode__(self):
138 return u'{0.name}'.format(self)
141 class Ingredient (Amount):
142 "1 kg, 2 lb., 3.4 L, 0.5 gal., etc."
143 name = models.CharField(max_length=200)
144 note = models.CharField(max_length=200, null=True, blank=True)
145 block = models.ForeignKey(IngredientBlock)
146 position = models.PositiveIntegerField()
149 ordering = ['block', 'position']
151 def __unicode__(self):
155 fmt = '{amount} ' + fmt
156 args['amount'] = self.format_amount()
159 return fmt.format(**args)