최근 udemy에서 DRF 강의를 듣고 있는데, 인턴을 하면서 정확하지 않은 지식으로 사용하던 것들에 대해 정리하는 시간을 가질 수 있었다.
다시 복습할 겸 정리를 해보고자 한다.
1. instance와 valideated_data
serializer 내부에서 create와 update method를 생성하는 경우가 정말 많았는데, 그중에서도 update method를 만들 때 instance와 validated_data라고 하는 parameter가 꼭 들어갔다.
data를 update 하기 위해 put 또는 patch 요청을 보내면 instance에는 old value가 담기게 되고, validated_date에는 new value가 담기게 된다.
class MovieSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
description = serializers.CharField()
active = serializers.BooleanField()
def create(self, validated_data):
return Movie.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.description = validated_data.get('description', instance.description)
instance.active = validated_data.get('active', instance.active)
instance.save()
return instance
위와 같은 MovieSerializer가 있을 때, old value를 담고 있는 instance의 내용을 new value를 가진 validated_data를 이용해 정보를 update 할 수 있다. 만약 validated_data에 담긴 내용이 없다면 기존 old value를 유지하게 된다.
2. @api_view
function-based view를 작성하면 api_view decorator를 사용하게 된다. @api_view() 내부에는 request mehtod 명이 들어가게 되는데, 아무런 설정을 하지 않으면 default로 GET 요청을 지원한다.
# views.py
@api_view()
def movie_details(request, pk):
movie = Movie.objects.get(pk=pk)
serializer = MovieSerializer(movie)
return Response(serializer.data)
위 view는 movie의 detail 페이지에 해당한다. @api_view() 안에 아무런 request method가 쓰여있지 않으므로 현재 이 view는 GET 요청만 처리할 수 있다.
하지만 GET 외의 POST, PUT, DELETE 등의 요청을 처리하기 위해서는 @api_view() 내부에 적어줘야 한다.
@api_view(['GET', 'POST'])
def movie_list(request):
if request.method == 'GET':
movies = Movie.objects.all()
serializer = MovieSerializer(movies, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
if request.method == 'POST':
serializer = MovieSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
request method는 list의 형태로 적어준다. 또한 각각의 요청에 맞춰 if request.method = 'POST'와 같이 분기를 나눠줘야 한다.
3. required
기본적으로 serializer에는 모든 필수 field의 값을 전달해야 하고, 그렇지 않으면 deserializer 과정에서 유효성 검사 오류가 발생한다. 그러나 required=False를 설정하면 일부 칼럼에 None이 포함되더라도 error가 발생하지 않는다. default는 True이다.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
4. ModelSerializer
일반 serializer와 비슷한 역할을 수행하지만 몇가지 다른 점이 있다.
- model을 기반으로 field 자동 생성
- validator 자동 생성
- create, updaate method 포함, 따라서 따로 두 method를 정의하지 않아도 된다.
또한 ModelSerializer 내부에 Meta class를 이용해 어떤 model를 사용하고 어떤 field를 사용할 것인지 정의하면 된다.
field 정의 시, model에 있는 모든 field를 사용하는 경우 '__all__'이라고 적으면 된다.
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = "__all__"
모든 field를 사용하지 않고 몇 가지를 제외하고 싶으면 아래와 같이 사용할 field만 써주면 된다.
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = ['id', 'name', 'description']
만약 field가 20개이고 그 중 19개의 field를 사용하고 싶을 때, 19개의 field를 일일이 적는 것은 비효율적이다. 이때에는 exclude를 사용하면 된다.
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
exclude = ['active']
5. Nested Serializer
# models.py
class StreamPlatform(models.Model):
name = models.CharField(max_length=30)
about = models.CharField(max_length=150)
website = models.URLField(max_length=100)
def __str__(self):
return self.name
class WatchList(models.Model):
title = models.CharField(max_length=50)
storyline = models.CharField(max_length=200)
platform = models.ForeignKey(StreamPlatform, on_delete=models.CASCADE, related_name='watchlist')
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
현재 작성한 model은 위와 같다.
한 platform이 여러 watchlist를 갖는 one to many 관계이기 때문에 model의 경우 WatchList가 StreamPlatform을 Foreignkey로 물고 있다. Serializer의 경우는 반대로, StreamPlatform 안에서 Watchlist의 내용을 새로운 field로 가져와야 어떤 list와 연결되어 있는지 확인할 수 있다.
따라서 StreamPlatformSerializer 안에서 WatchList에 관련한 field를 생성해야 한다. 이때 이름은 WatchList Model에서 설정한 related_name인 ‘watchlist’를 사용하면 된다. 만약 다른 이름을 사용하게 된다면 작동하지 않고 해당 field 내용을 보이지 않는다.
from rest_framework import serializers
from watchlist_app.models import WatchList, StreamPlatform
class WatchListSerializer(serializers.ModelSerializer):
class Meta:
model = WatchList
fields = "__all__"
class StreamPlatformSerializer(serializers.ModelSerializer):
watchlist = WatchListSerializer(many=True, read_only=True)
class Meta:
model = StreamPlatform
fields = "__all__"
'Django' 카테고리의 다른 글
[Django] DRF - 1차 프로젝트 리팩토링 (1) Product detail & list GET (0) | 2022.06.07 |
---|---|
[Django] DRF - API Testing (0) | 2022.06.01 |
[Django] DRF - Token Authentication (0) | 2022.05.29 |
[Django] DRF - Permission (0) | 2022.05.28 |
[Django] Django ORM - N+1 problem (0) | 2022.04.09 |