1

This happens when I list to Recipe objects. Trying to do as here, I get no errors, but the response I'm getting is as the following:

# response of Recipe.objects.all()
[
    {
        "user": 1,
        "name": "sandwich",
        "ingredients": [
            {},
            {}
        ],
        "created": "2021-01-11T00:47:04.932071-03:00",
        "modified": "2021-01-11T00:47:04.932167-03:00"
    }
]

When the models are:

class Recipe(models.Model):
    user = models.ForeignKey('users.User', on_delete=models.CASCADE, null=False)
    name = models.CharField(blank=False, max_length=50)
    ingredients = models.ManyToManyField('recipes.Ingredient', through='recipes.IngredientComposition')
    # some other fields...

class Ingredient(BaseModel):
    name = models.CharField(blank=False, max_length=25, unique=True
                            error_messages={'unique': 'Ingredient already exists'})

class IngredientComposition(models.Model):    
    ingredient = models.ForeignKey('Ingredient', on_delete=models.CASCADE, null=False)
    recipe = models.ForeignKey('recipes.Recipe', on_delete=models.CASCADE, null=False)
    quantity = models.DecimalField(max_digits=21, decimal_places=3, null=False, default=1)

And their serializers:

class RecipeSerializer(serializers.ModelSerializer):
    ingredients = IngredientCompositionSerializer(read_only=True, many=True)
    class Meta:
        model = Recipe
        fields = ['user', 'name', 'ingredients', 'created', 'modified']

class IngredientSerializer(serializers.ModelSerializer):
    class Meta:
        model = Ingredient
        fields = ['name', 'created', 'modified']

class IngredientCompositionSerializer(serializers.HyperlinkedModelSerializer):
    name = serializers.ReadOnlyField(source='ingredient.name')
    class Meta:
        model = IngredientComposition
        fields = ['name', 'quantity']

The expected response is:

[
    {
        "user": 1,
        "name": "sandwich",
        "ingredients": [
            {"name": "bread", "quantity": 2.0},
            {"name": "cheese", "quantity": 3.0}
        ],
        "created": "2021-01-11T00:47:04.932071-03:00",
        "modified": "2021-01-11T00:47:04.932167-03:00"
    }
]

What am I missing?

Gonzalo Dambra
  • 891
  • 2
  • 19
  • 34

1 Answers1

1

add a related name to recipe

class IngredientComposition(models.Model):    
    ingredient = models.ForeignKey('Ingredient', on_delete=models.CASCADE, null=False)
    recipe = models.ForeignKey('recipes.Recipe', on_delete=models.CASCADE, null=False, related_name='ingredients_list') # change here
    quantity = models.DecimalField(max_digits=21, decimal_places=3, null=False, default=1)

then add that field in RecipeSerializer

class RecipeSerializer(serializers.ModelSerializer):
    ingredients_list = IngredientCompositionSerializer(read_only=True, many=True) # change here
    class Meta:
        model = Recipe
        fields = ['user', 'name', 'ingredients_list', 'created', 'modified'] # change here 'ingredients_list'

as ingredients is already ManyToManyField in model Recipe it it causing that problem.

PS: In my opinion you should not need ingredients = models.ManyToManyField('recipes.Ingredient', through='recipes.IngredientComposition') field but I dont know rest of your business logic...

the solution should work regardless.

Ashwin Bande
  • 2,693
  • 2
  • 10
  • 22
  • Excellent! Thank you. When ot use the ManyToManyField and when not then? Since it is a m2m relationship that has a through model, I added it. But I'd like to know when it's a good idea and when it's not. I will request to add at least two ingredients when you create a new recipe, if that does counts. – Gonzalo Dambra Jan 11 '21 at 10:49
  • `ManyToManyField` as the name implies used when their is many to may relationship, your model has `ManyToManyField` relationship e.g. Each Recipe can have many Ingredient and each Ingredient can be in many Recipe. The `IngredientComposition` Model serves as `ManyToMany` relationship (mapping of recipe and Ingredient). so we use `ForeignKey` i.e `ManyToOne` relationship in mapping table. In general every `ManyToMany` relationship is represented by a mapping table. – Ashwin Bande Jan 11 '21 at 11:33
  • `ManyToMany` relationship should be used when their is no other common field in the mapping; In your case `quantity` is other third field, so automatic mapping table will not suitable (as it cant store quantity) so we create the mapping table our self with `ForeignKey` – Ashwin Bande Jan 11 '21 at 11:37