1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[DRF]結合テーブルの更新(part3)【Update】

Posted at

Django-rest-frameworkでAPIを作成します

前回までの記事一覧です。

基本編
[DRF]apiの作成(patch)
[DRF]apiの作成(post)
[DRF]apiの作成(get)part2
[DRF]apiの作成(get)part1
応用編
[DRF]結合テーブルの更新(part2)
[DRF]結合テーブルの更新(part1)
[DRF]結合テーブルの取得

前回の記事では、drf-writable-nestedを使用して、結合テーブルを新規作成しました。今回はupdate()メソッドのオーバーライドにより更新をしてみようと思います。

model

modelは以下のとおりです。

models.py
from django.db import models

DISTRICT_CATEGORIES = [
    ("1", "地区1"),
    ("2", "地区2"),
    ("3", "地区3"),
    ("4", "地区4"),
]


class Student(models.Model):
    """生徒情報"""

    # 生徒ID
    student_id = models.CharField(max_length=4, primary_key=True)

    # クラス
    class_no = models.CharField(max_length=1)

    # 出席番号
    attendance_no = models.IntegerField()

    # 名前
    name = models.CharField(max_length=20)

    # 地区番号
    district_no = models.CharField(max_length=1, choices=DISTRICT_CATEGORIES)

    # フリーコメント
    comment = models.CharField(max_length=200,blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["class_no", "attendance_no"],
                name="class_attendance_unique"
            ),
        ]


class Exam(models.Model):
    """試験情報"""

    # 国語
    japanese_score = models.IntegerField(null=True, blank=True)

    # 数学
    math_score = models.IntegerField(null=True, blank=True)

    # 英語
    english_score = models.IntegerField(null=True, blank=True)

    # 生徒ID
    student = models.OneToOneField(Student, on_delete=models.CASCADE)

StudentとExamが1対1対応しています。

update

今回は既存レコードを更新します。

view

viewは前回(create)と変わっていません。
とりあえず何も考えず、既存の普通のレコードの更新のように書いています。
partial=Trueもついてます。

views.py
from django.shortcuts import get_object_or_404

from rest_framework.views import APIView
from rest_framework.response import Response
from ..models import Student
from ..serializers import StudentExamUpdateSerializer


class StudentExamUpdateAPIView(APIView):
    """生徒情報、試験情報を登録"""

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

        instance = get_object_or_404(Student, pk=pk)

        # serializer作成
        serializer = StudentExamUpdateSerializer(instance=instance, data=request.data, partial=True)

        # validate
        try:
            serializer.is_valid(raise_exception=True)
        except Exception:
            raise Exception

        serializer.save()

        # Response
        return Response({'result':True})

serializer(未完成)

クラスの名前だけ変えました。

serizlizers.py
from rest_framework import serializers
from ..models import Student, Exam



class ExamOfStudentExamUpdateSerializer(serializers.ModelSerializer):
    """Examクラスのシリアライザ"""

    class Meta:
        model = Exam
        fields=[
            'japanese_score',
            'math_score',
            'english_score'
        ]


class StudentExamUpdateSerializer(serializers.ModelSerializer):
    """Studentクラスのシリアライザ"""

    exam = ExamOfStudentExamUpdateSerializer()

    class Meta:
        model = Student
        fields = [
            'student_id',
            'class_no',
            'attendance_no',
            'name',
            'district_no',
            'comment',
            'exam'
        ]

実行

実行ファイルは以下のとおりです。
urls.pyは貼りませんが、パスパラメータでpkをもらってそのpkに対して更新しにいきます。

名前と数学の点数を修正しにいきます。

import requests
import json

student_id='0001'
name="更木剣八"

math_score = 30
exam = {
    'math_score': math_score,
}
body = {
    'name':name,
}

headers = {"Content-Type" : "application/json;charset=UTF-8"}
response = requests.post('http://127.0.0.1:8000/student/{}/student_exam_update/'.format(student_id), data=json.dumps(body), headers=headers)

print(response.text)

AssertionError: The .update() method does not support writable nested fields by default.
Write an explicit .update() method for serializer students.serializers.student_exam_update.StudentExamUpdateSerializer, or set read_only=True on nested serializer fields.

やっぱり怒られました。しょうがない、updateをオーバーライドしてみましょうか。

serializer(完成)

updateを追加しています。

serizlizers.py
from rest_framework import serializers
from ..models import Student, Exam



class ExamOfStudentExamUpdateSerializer(serializers.ModelSerializer):
    """Examクラスのシリアライザ"""

    class Meta:
        model = Exam
        fields=[
            'japanese_score',
            'math_score',
            'english_score'
        ]


class StudentExamUpdateSerializer(serializers.ModelSerializer):
    """Studentクラスのシリアライザ"""

    exam = ExamOfStudentExamUpdateSerializer()

    class Meta:
        model = Student
        fields = [
            'student_id',
            'class_no',
            'attendance_no',
            'name',
            'district_no',
            'comment',
            'exam'
        ]

+   def update(self, instance, validated_data):
+   
+       exam_data = validated_data.pop('exam')
+       instance = super().update(instance, validated_data)
+   
+       exam_serializer = ExamOfStudentExamUpdateSerializer(instance=instance.exam, data=exam_data)
+       exam_serializer.is_valid()
+       exam_serializer.save()
+   
+       return instance

再実行

{"result":true}

出来ました。

serializerがあれば、これを使用してupdateとすることができそうです。
こちらを参考にしました。
個人的にとっても迷う実装だったのですが、参考リンクのおかげでなんとなく腹落ちしました。

次回はWritableNestedModelSerializerでupdateします。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?