Will Roberts
Will Roberts

Reputation: 37

Cannot save Django model with foreign key

I'm attempting to send a POST request from my frontend to add a new "Achievement" record. The achievement record has a foreign key field referring to a set, which contains many achievements. However, when I attempt to save, I get the error: "{"set": ["Incorrect type. Expected resource identifier object, received Set."]}" I don't know what a "resource identifier type" is and cannot find any docs on it by searching online. Here is my POST request data: {"achievement":{"title":"Rails is Omakase","description":"Lorem ipsum","experience":100,"set":"52"}}

I've tried setting "set" to the id of the set and the "Set" serializer instance before saving.

Serializers.py

class SetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Set
        fields = ('id', 'title', 'description')

class AchievementSerializer(serializers.ModelSerializer):
    class Meta:
        model = Achievement
        fields = ('id', 'title', 'description', 'experience', 'set')

models.py

class Set(models.Model):
    title = models.CharField(max_length=100, blank=False)
    description = models.TextField()

    class JSONAPIMeta:
        resource_name = "sets"

class Achievement(models.Model):
    title = models.CharField(max_length=100, blank=False)
    description = models.TextField()
    experience = models.IntegerField()
    set = models.ForeignKey(Set, on_delete=models.CASCADE, related_name="achievements")

views.py

class AchievementList(APIView):
    def post(self, request):
        data = request.data["achievement"]
        setId = data["set"]
        set = Set.objects.get(pk=setId)
        data["set"] = set
        serializer = AchievementSerializer(data=data)

        if serializer.is_valid():
            serializer.save()
            return JsonResponse({"achievement":serializer.data}, safe=False)
        return JsonResponse(serializer.errors, status=400)

Upvotes: 0

Views: 465

Answers (2)

Will Roberts
Will Roberts

Reputation: 37

I found a solution by reading some JSON docs: https://jsonapi.org/format/#document-resource-identifier-objects. The error stated that it wanted a resource identifier object passed in for the "set" key. A resource identifier object is simply an object that contains type and id members. Thus, I ensure that set had type and id elements and it solved the issue. Thus, my new post request looks like this: {"achievement":{"title":"Rails is Omakase","description":"Lorem ipsum","experience":100,"set":{"type":"sets","id":"4"}}}

Upvotes: 0

Shane
Shane

Reputation: 653

If you keep your post to just this:

def post(self, request):
    data = request.data["achievement"]

    serializer = AchievementSerializer(data=data)

    if serializer.is_valid():
        serializer.save()
        return JsonResponse({"achievement":serializer.data}, safe=False)
    return JsonResponse(serializer.errors, status=400)

Then I had no issues running it. Generally, a primary key is expected for an object. I used the following test:

class TestAchievementList(TestCase):
    def test_add_achievement(self):
        title = 'title'
        description = 'desc'
        experience = 1
        set_object = Set.objects.create(title='new set')

        json = {
            'achievement': {
                'title': title,
                'description': description,
                'experience': experience,
                'set': set_object.id
            }
        }

        url = reverse('achievements')

        response = self.client.post(url, data=json, content_type='application/json')

        self.assertEqual(response.status_code, 200)
        self.assertIn('achievement', response.json())

        achievement_json = response.json()['achievement']

        self.assertEqual(achievement_json['title'], title)
        self.assertEqual(achievement_json['description'], description)
        self.assertEqual(achievement_json['experience'], experience)
        self.assertEqual(achievement_json['set'], set_object.id)

Upvotes: 1

Related Questions