In my Django project I have a model for a drinks recipe that allows up to 10 ingredients plus their amount. I am wondering if it's possible to make this in a more DRY way than I do now? This is the model I am using currently:
class Recipe(models.Model):
ingr_name1 = models.CharField(max_length=250, verbose_name='Ingredient')
ingr_amount1 = models.DecimalField(max_digits=5, decimal_places=1, verbose_name='Amount')
ingr_name2 = models.CharField(max_length=250, blank=True, null=True, verbose_name='Ingredient')
ingr_amount2 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
ingr_name3 = models.CharField(max_length=250,blank=True, null=True, verbose_name='Ingredient')
ingr_amount3 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
ingr_name4 = models.CharField(max_length=250,blank=True, null=True, verbose_name='Ingredient')
ingr_amount4 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
ingr_name5 = models.CharField(max_length=250,blank=True, null=True, verbose_name='Ingredient')
ingr_amount5 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
ingr_name6 = models.CharField(max_length=250,blank=True, null=True, verbose_name='Ingredient')
ingr_amount6 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
ingr_name7 = models.CharField(max_length=250,blank=True, null=True, verbose_name='Ingredient')
ingr_amount7 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
ingr_name8 = models.CharField(max_length=250,blank=True, null=True, verbose_name='Ingredient')
ingr_amount8 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
ingr_name9 = models.CharField(max_length=250,blank=True, null=True, verbose_name='Ingredient')
ingr_amount9 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
ingr_name10 = models.CharField(max_length=250,blank=True, null=True, verbose_name='Ingredient')
ingr_amount10 = models.DecimalField(max_digits=5, decimal_places=1,blank=True, null=True, verbose_name='Amount')
drink_name = models.CharField(max_length=250,blank=True, null=True, verbose_name='Drink name')
drink_story = models.TextField()
drink_picture = ResizedImageField(upload_to='drinks/', null=True, blank=True)
def __str__(self):
return self.drink_name
CodePudding user response:
One option is to make Ingredient its own model and add is as a many to many field in the recipe model. Then based on this answer you can limit the number of ingredients to 10 by adding a ingredients_changed()
function to the Recipe model.
Untested code below:
class Ingredient(model.Model):
name = models.CharField()
amount = models.CharField()
class Recipe(models.Model):
ingredients = models.ManyToManyField(Ingredient)
def ingredients_changed(recipe, **kwargs):
if kwargs['instance'].ingredients.count() > 10:
raise ValidationError("You can't assign more than 10 ingredients")
CodePudding user response:
You could think of an alternative model field such as a JSONField or a TextField. JSONFields are a good one, if you:
- Don't know how many ingr_amounts& ingr_names are used
- If you want to handle them together. Within you're current model you would have to do a clean based on each self.ingr, with a jsonfield you could do it with one self.my_json_field.
Anyways there also disadvantages and you, especially when querying and updating jsonfields it can get trickier. So before you go ahead and use it please infrom here f.ex. JSONField, json query
*You could also if it fits you're needs create a new Model with both fields and link it to the other model via a ForeignKey