13

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 :)

Rijo John
  • 131
  • 1
  • 3
  • Try the following: In create method in UserProfileSerializer, before the for loop, explicitly save user1 -> user1.save() – Jaakko Jun 01 '15 at 13:15
  • Does this answer your question? [How to add data to nested serializers?](https://stackoverflow.com/questions/62603654/how-to-add-data-to-nested-serializers) – neferpitou Jul 03 '20 at 12:26
  • Calling `filter()` will return a queryset, not a single object... Try replacing it with `IngFamily.objects.get(name=allergy.get('name'))`. – Bernhard Vallant May 31 '22 at 12:01

4 Answers4

1

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
Farid Chowdhury
  • 2,766
  • 1
  • 26
  • 21
1

You should but .save() inside loop and edit your query to be like:

def create(self, validated_data):

    allergies_data =validated_data.pop('allergies', [])
    user1=UserProfile.objects.create(**validated_data)
    for allergy in allergies_data:

      al=IngFamily.objects.get(name=allergy['name'])
      user1.allergies.add(al)
    #here you should add it
      user1.save()
lemon
  • 14,875
  • 6
  • 18
  • 38
Hashem
  • 595
  • 1
  • 3
  • 8
0

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
iankit
  • 8,806
  • 10
  • 50
  • 56
0

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/