'How to add data to ManyToMany field using Django Rest Framework?

I'm relatively new to DJango and I am looking to add data to a many-to-many field using serializers from rest framework.

My Model:

class IngFamily(models.Model):
 name=models.CharField(max_length=30, unique=True, verbose_name='ingredient parent')

class UserProfile(models.Model):
      user=models.ForeignKey(User)
      allergies=models.ManyToManyField(IngFamily, verbose_name='allergies', null=True)

Now, I want to add data to the UserProfile model using the rest api.

I've looked around, but couldn't find much anywhere.

So far what I've achieved:

serializer:

class IngFlySerializer(serializers.ModelSerializer):

  class Meta:
    model = IngFamily
    fields= ('name',)


class UserProfileSerializer(serializers.ModelSerializer):
  allergies=IngFlySerializer(many=True,)
  class Meta:
        model = UserProfile
        fields=('allergies',)

  def create(self, validated_data):

    allergies_data =validated_data.pop('allergies', [])

    #import pdb;pdb.set_trace()
    user1=UserProfile.objects.create(**validated_data)
    for allergy in allergies_data:

      al=IngFamily.objects.filter(name=allergy.get('name'))
      #al.name=allergy.get('name')
      user1.allergies.add(al)

    user1.save()
    return user1

When I try using this, "al" is empty.

view:

class user_profile(generics.ListCreateAPIView):


  serializer_class = UserProfileSerializer
  permission_classes = (permissions.IsAuthenticated,)

  def get_queryset(self):
        user = self.request.user
        return UserProfile.objects.filter(user=user)

  def perform_create(self, serializer):
      #import pdb; pdb.set_trace()
      serializer.save(user=self.request.user)

class UserDetail(generics.RetrieveDestroyAPIView):
    serializer_class = UserProfileSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_queryset(self):
        user1 = self.request.user
        return UserProfile.objects.filter(user=user1)

My Post request would look this:

{"allergies": [{"name": ["Beef"]},{"name": ["Pork"]}]}

I've been stuck on this for a while, any help would be appreciated :)



Solution 1:[1]

Couple of things can be tried :

  1. It might be possible your database dont have some of the allergies coming in with user data. Thats why your al is coming empty.
  2. saving user data before you add allergies to it. This is important.
  3. By using ipdb or pdb, try to understand in what data form 'allergy' and 'allergies_data' is coming out. If indeed it is what you have written, there is no reason the following code shouldnt work.

def create(self, validated_data):
    allergies_data =validated_data.pop('allergies')
    user1=UserProfile.objects.create(**validated_data)
    user1.save()
    for allergy in allergies_data:
        al=IngFamily.objects.get_or_create(name=allergy['name'])
        user1.allergies.add(al)
    return user1

Solution 2:[2]

First of all, the data format is not in the post request is wrong:

it supposed to be following:

{"allergies": [{"name": "Beef"},{"name": "Pork"}]}

Now let's modify your def create() inside your serializer

 def create(self, validated_data):

    allergies_data =validated_data.pop('allergies', [])
    names = [allergy.get('name') for allergy in allergies_data if allergy]
    al = IngFamily.objects.filter(name__in=names) # using names(list) for filtering by __in

    user1 = UserProfile.objects.create(**validated_data)
    user1.allergies.add(*al) # notice there have * before "al" (as we are putting list inside add()

    # user1.save() # we don't need to call `save()` as we are updating ManyToMany field.
    return user1

Solution 3:[3]

Use something like:

class ProjectSerializer(serializers.ModelSerializer):

    tag= serializers.StringRelatedField(many=True)

    class Meta:
       model = Song
       fields = '__all__'

Here is a reference doc - https://www.django-rest-framework.org/api-guide/relations/

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 iankit
Solution 2
Solution 3 Vaibhav Shukla