I am trying to work with a post request were I am first saving a Tag object, which then becomes the tag field of a Tagging object. However, no matter what combinations I have tried, despite the Tag Post method working, when I pass a json object like this:
{
"user_id": 1,
"gameround_id": 2015594866,
"resource_id": 2975,
"tag": {
"name": "TESTTAGGGG2222",
"language": "en"
},
"score": 0,
"origin": ""
}
I keep getting this message:
{
"name": [
"This field is required."
],
"language": [
"This field is required."
]
}
However, when I pass a Tag, it works.
This is the post method:
def post(self, request, *args, **kwargs):
if not isinstance(request.user, CustomUser):
current_user_id = 1
else:
current_user_id = request.user.pk
gameround = request.data.get('gameround', '')
random_resource = request.data.get('resource', '')
created = datetime.now()
score = 0
origin = ''
name = request.data.get('name', '')
language = request.data.get('language', '')
tag_serializer = TagSerializer(data=request.data)
tagging_serializer = TaggingSerializer(data=request.data)
if tag_serializer.is_valid(raise_exception=True):
tag_serializer.save(tag=request.data)
if tagging_serializer.is_valid(raise_exception=True):
tagging_serializer.save(tagging=request.data, tag=tag_serializer.data)
return Response({"status": "success", "data": tagging_serializer.data},
status=status.HTTP_201_CREATED)
# else:
# return Response({"status": "success", "data": tag_serializer.data},status=status.HTTP_201_CREATED)
else:
return Response({"status": "error", "data": tag_serializer.errors},
status=status.HTTP_400_BAD_REQUEST)
How do I correctly pass the nested object in the post method so that I don't get this error anymore?
models.py
class Tag(models.Model):
name = models.CharField(max_length=256)
language = models.CharField(max_length=256)
objects = models.Manager()
def __str__(self):
return self.name or ''
@property
def tags(self):
tags = self.tagging.values('tag')
return tags.values('tag_id', 'tag__name', 'tag__language')
class Tagging(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, related_name='taggings')
resource = models.ForeignKey(Resource, on_delete=models.CASCADE, related_name='taggings')
tag = models.ForeignKey(Tag, on_delete=models.CASCADE, related_name='tagging')
created = models.DateTimeField(editable=False, null=True)
score = models.PositiveIntegerField(default=0)
# media_type = models.ForeignKey(Gamemode, on_delete=models.CASCADE)
origin = models.URLField(max_length=256, blank=True, default='')
objects = models.Manager()
def __str__(self):
return str(self.tag) or ''
serializers.py
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name', 'language')
def create(self, validated_data):
tag_data = validated_data.pop('tag')
Tag.objects.create(**tag_data)
return tag_data
def to_representation(self, data):
data = super().to_representation(data)
return data
class TaggingSerializer(serializers.ModelSerializer):
tag = TagSerializer(required=False, write_only=False)
resource_id = serializers.PrimaryKeyRelatedField(queryset=Resource.objects.all(),
required=True,
source='resource',
write_only=False)
gameround_id = serializers.PrimaryKeyRelatedField(queryset=Gameround.objects.all(),
required=False,
source='gameround',
write_only=False)
user_id = serializers.PrimaryKeyRelatedField(queryset=CustomUser.objects.all(),
required=False,
source='user',
write_only=False)
class Meta:
model = Tagging
fields = ('id', 'user_id', 'gameround_id', 'resource_id', 'tag', 'created', 'score', 'origin')
depth = 1
def create(self, validated_data):
"""Create and return a new tagging"""
tag_data = validated_data.pop('tag', None)
if tag_data:
tag = Tag.objects.get_or_create(**tag_data)[0]
validated_data['tag'] = tag
tagging = Tagging(
user=validated_data.get("user"),
gameround=validated_data.get("gameround"),
resource=validated_data.get("resource"),
tag=validated_data.get("tag"),
created=datetime.now(),
score=validated_data.get("score"),
origin=validated_data.get("origin")
)
tagging.save()
return tagging
def to_representation(self, instance):
rep = super().to_representation(instance)
rep['tag'] = TagSerializer(instance.tag).data
return rep
CodePudding user response:
In your post method, you made
tag_serializer = TagSerializer(data=request.data)
if tag_serializer.is_valid(raise_exception=True):
# the rest of the code
this means that your data will pass to TagSerializer so DRF will check if the fields for this serializer are in the request body or not, and it's not provided, cause this data does not belong to this serializer, it belongs to TaggingSerializer so this will give you an error This field is required
So, you need to send your request data only to TaggingSerializer, try that and let's see what will happen, and I will suggest using serializers.Serializer instead of using serializers.ModelSerializer for better performance
