Search code examples
pythondjangodjango-rest-frameworkdjango-serializer

Nested ManytoMany field not in the Response


I am trying to write a nested serializer which would add serialize 2 models in the same view. Serialization seems to work fine since changes get reflected in the database but I am not able to get the many-to-many related field data in the response. I have been trying to figure out what the issue might be but still no progress. Here is my code:

Model

class User(AbstractBaseUser):
    AccountName = models.ManyToManyField(Account,
                                         through='User_Account',
                                         through_fields=('user', 'acc'),
                                         related_name='AccountData',
                                         blank=True)

    EmailId = models.EmailField(max_length=128, blank=False, null=False)

    USERNAME_FIELD = 'EmailId'
    REQUIRED_FIELDS = ['AccountName']
class Account(models.Model):
    AccountName = models.TextField(max_length=100, blank=False, null=False)

Serializer

class AccountCreationSerializer(ModelSerializer):
    class Meta:
        model = Account
        fields = ["AccountName"]

class SignUpSerializer1(ModelSerializer):
    AccountData = AccountCreationSerializer(read_only=True, many=True)

    class Meta:
        model = User
        fields = ['EmailId', 'AccountData', 'password']
        extra_kwargs = {'password': {'write_only': True, 'required': True}}

    def validate(self, attrs):
        attrs = super(SignUpSerializer1, self).validate(attrs=attrs)
        attrs.update({"AccountData": self.initial_data.get("AccountData")})
        return attrs

    def create(self, validated_data):
        AccountName_data = validated_data.pop('AccountData')
        acc = Account.objects.create(AccountName=AccountName_data)
        userAcc = User.objects.create_user(**validated_data)
        if acc:
            userAcc.AccountName.add(acc)
            print("added")
        return userAcc

View

class SignUpView(APIView):
    serializer_class1 = SignUpSerializer1

    def post(self, request, *args, **kwargs):

        if request.data['CreateAccount']:
            serializer = self.serializer_class1(data=request.data)
            is_valid_serializer = serializer.is_valid(raise_exception=True)
            if is_valid_serializer:
                with transaction.atomic():
                    serializer.save()
                return Response(serializer.data, status=status.HTTP_200_OK)
            else:
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        else:
            raise Exception("Bad error")

Request1

    "EmailId" : "xyz@gmail.com",
    "AccountData":{"AccountName":"TestAcc1"},
    "CreateAccount": true,
    "password" : "xyz"

Response

    "EmailId": "xyz@gmail.com",

#After Removing read_only=true from AccountData

Request2

    "EmailId" : "xyz@gmail.com",
    "AccountData":{"AccountName":"TestAcc1"},
    "CreateAccount": true,
    "password" : "xyz"

Response

    {"AccountData":{"non_field_errors":["Expected a list of items but got type \"dict\"."]}}

Request3

    "EmailId" : "xyz@gmail.com",
    "AccountData":[{"AccountName":"TestAcc1"}],
    "CreateAccount": true,
    "password" : "xyz"

Response

 Got AttributeError when attempting to get a value for field `AccountData` on serializer `SignUpSerializer1`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'AccountData'.

There is no response data fo AccountName in the response. And when I try print(User.objects.get(EmailId = serializer.data['AccountName'])) ====> None.
How should I get the field populated in the correct way in my response? Thanks!


Solution

  • You need to specify source argument, since model's field called AccountName, not AccountData:

    class SignUpSerializer1(ModelSerializer):
        AccountData = AccountCreationSerializer(many=True, source="AccountName")