Django-rest-frameworkでAPIを作成します
前回までの記事一覧です。
基本編 |
---|
[DRF]apiの作成(patch) |
[DRF]apiの作成(post) |
[DRF]apiの作成(get)part2 |
[DRF]apiの作成(get)part1 |
応用編 |
---|
[DRF]結合テーブルの取得 |
この記事では、OneToOneフィールドで結合したテーブルを更新します。
model
modelは以下のとおりです。
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というOneToOneフィールドで結合しているテーブルに付いて考えましょう。
create
まずは新規レコード作成から考えてみます。
serializer(未完成)
前回と同じようなserializerです。これを出力でなく入力に使います。
from rest_framework import serializers
from ..models import Student, Exam
class ExamSerializer(serializers.ModelSerializer):
"""Examクラスのシリアライザ"""
class Meta:
model = Exam
fields=[
'japanese_score',
'math_score',
'english_score'
]
class StudentSerializer(serializers.ModelSerializer):
"""Studentクラスのシリアライザ"""
exam = ExamSerializer()
class Meta:
model = Student
fields = [
'student_id',
'class_no',
'attendance_no',
'name',
'district_no',
'comment',
'exam'
]
view
とりあえず新規作成のviewです。
あまり結合テーブルということは意識していませんね。
from rest_framework.views import APIView
from rest_framework.response import Response
from ..serializers import StudentSerializer
class StudentExamCreateAPIView(APIView):
"""生徒情報、試験情報を登録"""
def post(self, request, *args, **kwargs):
# serializer作成
serializer = StudentSerializer(data=request.data)
# validate
try:
serializer.is_valid(raise_exception=True)
except Exception:
raise Exception
serializer.save()
# Response
return Response({'result':True})
実行
import requests
import json
student_id='0011'
class_no='3'
attendance_no=10
name="茶渡泰虎"
district_no='2'
comment='チャドの霊圧が・・・消えた!?'
japanese_score = 90
math_score = 60
english_score = 50
exam = {
'japanese_score': japanese_score,
'math_score': math_score,
'english_score': english_score,
}
body = {
'student_id':student_id,
'class_no':class_no,
'attendance_no':attendance_no,
'name':name,
'district_no':district_no,
'comment':comment,
'exam': exam,
}
headers = {"Content-Type" : "application/json"}
response = requests.post('http://127.0.0.1:8000/student/student_exam_create/', data=json.dumps(body), headers=headers)
print(response.text)
怒られました。
AssertionError: The
.create()
method does not support writable nested fields by default.
Write an explicit.create()
method for serializerstudents.serializers.student_exam_create.StudentSerializer
, or setread_only=True
on nested serializer fields.
save()
を呼んだときに呼ばれるcreate()
でエラったようですね。ネストされた項目の更新はデフォルトで出来ないから、readonly=True
をつけるか、自分でcreate()
をオーバーライドしてね。と言われてます。
公式のドキュメント(writable-nested-serializers)を参照してみました。
同じように実装してみましょう。
serializer(修正版)
from rest_framework import serializers
from ..models import Student, Exam
class ExamSerializer(serializers.ModelSerializer):
"""Examクラスのシリアライザ"""
class Meta:
model = Exam
fields=[
'japanese_score',
'math_score',
'english_score'
]
class StudentSerializer(serializers.ModelSerializer):
"""Studentクラスのシリアライザ"""
exam = ExamSerializer()
class Meta:
model = Student
fields = [
'student_id',
'class_no',
'attendance_no',
'name',
'district_no',
'comment',
'exam'
]
+ def create(self, validated_data):
+ exam_data = validated_data.pop('exam')
+ student = Student.objects.create(**validated_data)
+ Exam.objects.create(student=student, **exam_data)
+
+ return student
はい。createメソッドを追加しました。
validated_dataからexamデータをpopして、ネストされた構造をネストされていない状態にすることが出来ました。そしてそれぞれのmodelについてcreateメソッドを実行することによりデータの作成ができています。
実行
{"result":true}
STUDENT ID | CLASS NO | ATTENDANCE NO | NAME | DISTRICT NO | COMMENT |
---|---|---|---|---|---|
0011 | 3 | 10 | 茶渡泰虎 | 地区2 | チャドの霊圧が・・・消えた!? |
STUDENT ID | JAPANESE SCORE | MATH SCORE | ENGLISH SCORE |
---|---|---|---|
0011 | 90 | 60 | 50 |
無事createが出来ました。
createをオーバーライドしてくれなくてもやってくれたらいいのになーちょっと思ったりもしますが、まぁ想定されていないようなやり方なのでしょうか。
今回は新規作成をやりましたが、更新だったり、あと他にもやり方があると思うのでpart2などで勉強していくこととしましょう。