Study/django

[DRF] drf-writable-nested 사용 방법에 대한 정리

bluebamus 2024. 3. 7.

https://github.com/beda-software/drf-writable-nested

 

GitHub - beda-software/drf-writable-nested: Writable nested model serializer for Django REST Framework

Writable nested model serializer for Django REST Framework - beda-software/drf-writable-nested

github.com

 

 1. drf-writable-nested란?

   1) 정리 : 

      - Django REST framework를 이용해 API를 개발할 때 중첩된(nested) 관계의 데이터를 쓰기 가능하게 만들어주는 라이브러리입니다. 즉, Serializer 내에서 관계된 객체를 생성, 업데이트, 삭제할 수 있는 기능을 제공하여 복잡한 데이터 구조를 편리하게 다룰 수 있도록 돕습니다. 이 라이브러리를 사용하면 중첩된 시리얼라이저를 통해 효율적으로 데이터를 다룰 수 있으며, 폼을 통한 다중 레벨 중첩 관계에서도 매끄러운 작업을 가능하게 해줍니다.

 

   2) drf-writable-nested의 장점과 특징 :

      - 읽기 및 쓰기가 가능한 중첩 작업 : 

         - 일반적으로 DRF에서 중첩 시리얼라이저는 읽기 전용이지만, drf-writable-nested는 중첩된 데이터 구조에서 생성 및 업데이트 작업을 할 수 있게 해줍니다.

      - 관계 유지 : 

         - 다양한 유형의 관계(ForeignKey, ManyToManyField 등)를 지원하며, 중첩된 관계에서 원본 객체와 연관 객체 사이의 관계를 유지하면서 데이터 조작이 가능합니다.

      - 복잡성 감소 :

         - 클라이언트 측에서 받은 데이터 구조를 필요에 맞게 변환하지 않고도 직접적으로 모델에 적용할 수 있어서, 서버 측에서의 복잡성을 줄여주고 코드를 간결하게 만들 수 있습니다.

      - 유효성 검사와 같은 기능 포함 : 

         - drf-writable-nested는 DRF의 기본 시리얼라이저처럼 유효성 검사 등을 포함하여 중첩된 데이터에 대한 오류 검출 및 처리를 용이하게 합니다.

      - 기존 API 구조와의 통합 용이성 : 

         - 이미 구축된 API 구조에 중첩된 데이터 작업을 추가하기 위한 간단한 방법을 제공하여 기존 시스템과의 호환성을 높입니다.

 

   3) 일반적인 제한 사항 :

      - 상세한 유효성 검사 :

         - drf-writable-nested는 기본적인 중첩 생성과 갱신을 지원하지만, 복잡한 유효성 검사나 커스텀 동작은 사용자가 직접 오버라이드해야 할 수 있습니다.

      - 큰 변경에 따른 대응 :

         - DRF나 다른 종속 패키지들의 큰 변경사항에 대해서는 drf-writable-nested가 신속하게 대응하지 못할 수 있으며, 이는 사용자에게 문제를 야기할 수 있습니다.

 

   4) 구체적인 제한 사항 :

      - validated_data와 initial_data :

         - drf-writable-nested는 주로 initial_data를 참조하여 객체를 생성 및 갱신하기 때문에, validated_data를 기반으로 추가적인 처리를 하려 할 때 어려움이 발생할 수 있습니다.

      - 트랜잭션 관리 :

         - 복잡한 트랜잭션을 관리하는 경우, DRF의 기본 지원 기능만으로는 부족할 수 있으며, drf-writable-nested가 이를 모두 해결해주지는 않습니다.

      - 많은 양의 자동화된 업데이트 지원 :

         - 대량의 자동 업데이트 시나리오에서 drf-writable-nested의 지원은 제한적일 수 있습니다.

 

   5) 오버라이딩 시 주의점 :

      - 기본 메소드 오버라이딩 :

         - 필요한 동작을 수행하기 위해서는 DRF 시리얼라이저에서 제공하는 create() 또는 update()와 같은 메소드를 사용자가 직접 오버라이드해야 하며, 이 과정에서 drf-writable-nested가 기대한 대로 작동하지 않을 수 있습니다.

 

 2. 설치 방법 :

pip install drf-writable-nested

 

 3. 기본 drf의 nested 구현

   - 학교(School) 모델이 교사(Teacher)와 학생(Student)의 데이터를 포함하는 경우 중첩된 교사와 학생에 대한 데이터를 학교의 create와 update를 통해 nested serializer의 관련 입력을 관리해줘야 한다.

 

   - 모델 설정 :

# models.py
from django.db import models

class School(models.Model):
    name = models.CharField(max_length=100)

class Teacher(models.Model):
    name = models.CharField(max_length=100)
    school = models.ForeignKey(School, related_name='teachers', on_delete=models.CASCADE)

class Student(models.Model):
    name = models.CharField(max_length=100)
    school = models.ForeignKey(School, related_name='students', on_delete=models.CASCADE)

 

   - 시리얼라이저 설정 :

# serializers.py
from rest_framework import serializers
from .models import School, Teacher, Student

class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = '__all__'

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = '__all__'

class SchoolSerializer(serializers.ModelSerializer):
    teachers = TeacherSerializer(many=True)
    students = StudentSerializer(many=True)

    class Meta:
        model = School
        fields = '__all__'
        
    def create(self, validated_data):
        teachers_data = validated_data.pop('teachers')
        students_data = validated_data.pop('students')
        school = School.objects.create(**validated_data)
        for teacher_data in teachers_data:
            Teacher.objects.create(school=school, **teacher_data)
        for student_data in students_data:
            Student.objects.create(school=school, **student_data)
        return school

    def update(self, instance, validated_data):
        # 이곳에 중첩된 관계의 인스턴스를 갱신하는 코드를 작성합니다.
        # ...
        return instance

 

   - 뷰 설정 :

# views.py
from rest_framework import viewsets
from .models import School
from .serializers import SchoolSerializer

class SchoolViewSet(viewsets.ModelViewSet):
    queryset = School.objects.all()
    serializer_class = SchoolSerializer

 

 4. drf-writable-nested를 이용한 nested 구현

   - 이전 학교에서 관리했던 create, update를 직접 구현하지 않아도 제대로 동작을 한다.

 

   - 모델 설정 :

# models.py
from django.db import models

class School(models.Model):
    name = models.CharField(max_length=100)

class Teacher(models.Model):
    name = models.CharField(max_length=100)
    school = models.ForeignKey(School, related_name='teachers', on_delete=models.CASCADE)
    classes_taught = models.ManyToManyField('Class', related_name='taught_by')

class Student(models.Model):
    name = models.CharField(max_length=100)
    school = models.ForeignKey(School, related_name='students', on_delete=models.CASCADE)
    classes_attended = models.ManyToManyField('Class', related_name='attended_by')

class Class(models.Model):
    subject = models.CharField(max_length=100)

 

   - 시리얼라이저 설정 :

# serializers.py
from rest_framework import serializers
from drf_writable_nested import WritableNestedModelSerializer
from .models import School, Teacher, Student, Class

class ClassSerializer(serializers.ModelSerializer):
    class Meta:
        model = Class
        fields = '__all__'

class TeacherSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
    classes_taught = ClassSerializer(many=True)

    class Meta:
        model = Teacher
        fields = '__all__'

class StudentSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
    classes_attended = ClassSerializer(many=True)

    class Meta:
        model = Student
        fields = '__all__'

class SchoolSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
    teachers = TeacherSerializer(many=True)
    students = StudentSerializer(many=True)

    class Meta:
        model = School
        fields = '__all__'

 

   - 뷰 설정 :

# views.py
from rest_framework import viewsets
from .models import School
from .serializers import SchoolSerializer

class SchoolViewSet(viewsets.ModelViewSet):
    queryset = School.objects.all()
    serializer_class = SchoolSerializer

 

   - 결과 

{
    "name": "우리학교",
    "teachers": [
        {
            "name": "김선생님",
            "classes_taught": [
                {"subject": "수학"},
                {"subject": "과학"}
            ]
        }
    ],
    "students": [
        {
            "name": "홍길동",
            "classes_attended": [
                {"subject": "수학"},
                {"subject": "영어"}
            ]
        },
        {
            "name": "임꺽정",
            "classes_attended": [
                {"subject": "과학"},
                {"subject": "영어"}
            ]
        }
    ]
}

댓글