DRF (Django REST Framwork)
Serializer → 데이터 구조화/검증View / ViewSet → 로직 처리Router → URL 자동 매핑from rest_framework.routers import DefaultRouter
from .views import MyModelViewSet
router = DefaultRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns = [
path('', include(router.urls)),
]
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class MyAPIView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
return Response({"message": "Authenticated!"})
IsAuthenticated: 인증된 사용자만 접근 가능IsAdminUser: 관리자만 접근 가능AllowAny: 누구나 접근 가능from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class MyAPIView(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
return Response({"message": "Token authenticated!"})
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
class MyAPIView(APIView):
throttle_classes = [UserRateThrottle]
def get(self, request):
return Response({"message": "Throttled request!"})
validationcreate/update 로직Nested) 까지 지원# ViewSet 하나에서 3개의 Serializer를 사용
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
def get_serializer_class(self):
if self.action == 'list':
return UserListSerializer
if self.action == 'retrieve':
return UserDetailSerializer
return UserCreateUpdateSerializer
# Serializer 두 개를 동시에 쓰는 구조.
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['age', 'bio']
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer() # 중첩된 Serializer 사용
class Meta:
model = User
fields = ['username', 'email', 'profile']
class UserCreateSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
class UserResponseSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username']
# Serializer 두 개 사용
class PostSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField()
def get_comments(self, obj):
return CommentSerializer(obj.comments.all(), many=True).data
from rest_framework import serializers
from .models import MyModel
⎷
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
# model
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# Serializer
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content']
# serialize된 결과(JSON)
{
"id": 1,
"title": "Hello",
"content": "World"
}
# 검증(Validation)도 가능
class PostSerializer(serializers.ModelSerializer):
title = serializers.CharField(max_length=10)
class Meta:
model = Post
fields = "__all__"
## 10자 넘으면 에러:
{
"title": ["Ensure this field has no more than 10 characters."]
}
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content', 'is_published']
def validate(self, attrs): ✅
title = attrs.get('title')
content = attrs.get('content')
if title == content:
raise serializers.ValidationError("제목과 내용은 같을 수 없습니다.")
return attrs
# 호출 흐름
serializer = PostSerializer(data={
"title": "Hello",
"content": "Hello"
})
serializer.is_valid()
# => {"non_field_errors": ["제목과 내용은 같을 수 없습니다."]}
# 기본 예제 모델
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
is_published = models.BooleanField(default=False)
# serializer
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'is_published']
def to_representation(self, instance):
rep = super().to_representation(instance) ✅
# 커스텀 출력
rep['title_length'] = len(instance.title) # 필드 추가
rep['status'] = '공개' if instance.is_published else '비공개'
return rep
# 출력
"id": 1,
"title": "Hello",
"content": "World",
"is_published": true,
"title_length": 5,
"status": "공개"
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content', 'is_published']
def create(self, validated_data):
print("create 호출됨:", validated_data)
return Post.objects.create(**validated_data) ✅
# 호출 흐름
serializer = PostSerializer(data={
"title": "New",
"content": "Hello",
"is_published": True
})
serializer.is_valid(raise_exception=True)
post = serializer.save() # <===== create() 실행됨
# 출력
create 호출됨: {'title': 'New', 'content': 'Hello', 'is_published': True}
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content', 'is_published']
def update(self, instance, validated_data): ✅
print("update 호출됨:", validated_data)
instance.title = validated_data.get('title', instance.title)
instance.content = validated_data.get('content', instance.content)
instance.is_published = validated_data.get('is_published', instance.is_published)
instance.save()
return instance
# 호출 흐름
serializer = PostSerializer(post_instance, data={
"title": "Updated Title"
}, partial=True)
serializer.is_valid(raise_exception=True)
post = serializer.save() # <===== update() 실행됨
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = "__all__"
# 입력 검증
def validate(self, attrs):
if len(attrs.get('title', '')) < 3:
raise serializers.ValidationError("제목은 3글자 이상이어야 합니다.")
return attrs
# 생성
def create(self, validated_data):
validated_data['title'] = validated_data['title'].title()
# 예: 제목을 자동 capitalizing
return Post.objects.create(**validated_data)
# 수정
def update(self, instance, validated_data):
instance.content = validated_data.get('content', instance.content)
instance.save()
return instance
# 출력 변환
def to_representation(self, instance):
rep = super().to_representation(instance)
rep['summary'] = instance.content[:20] # 요약 필드 추가
return rep
APIView GenericAPIView Mixins ViewSet ModelViewSet GET, POST, PUT, DELETE 등)를 오버라이드하여 사용할 수 있음# 예시 1
class PostListAPIView(APIView):
def get(self, request):
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
return Response(serializer.data)
# 예시 2
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class MyAPIView(APIView):
def get(self, request):
data = {"message": "Hello, World!"}
return Response(data, status=status.HTTP_200_OK)
def post(self, request):
data = request.data
return Response(data, status=status.HTTP_201_CREATED)
ViewSets
ViewSet 은 Django REST Framework에서 제공하는 강력한 기능ModelViewSet, ReadOnlyModelViewSet 등등 이용시 더욱 간결하게 API를 정의 가능ViewSet: 모든 CRUD 작업을 직접 정의할 수 있는 기본 ViewSet.ModelViewSet: ViewSet을 상속받아 모델의 CRUD 작업을 자동으로 처리ReadOnlyModelViewSet: 읽기 전용 작업(리스트, 상세 조회)만 처리from rest_framework import viewsets
from rest_framework.response import Response
class CustomViewSet(viewsets.ViewSet):
def list(self, request):
data = {"message": "This is a list view"}
return Response(data)
def retrieve(self, request, pk=None):
data = {"message": f"This is the detail view of {pk}"}
return Response(data)
ModelViewSet은 가장 많이 사용되는 ViewSet, 모델의 모든 CRUD 작업을 처리queryset과 serializer_class만 정의하면, 나머지 작업은 자동으로 처리됨class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
GET /posts/ | POST /posts/ GET /posts/1/ | PUT /posts/1/ | DELETE /posts/1/ from rest_framework import viewsets
from .models import MyModel
from .serializers import MyModelSerializer
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
ReadOnlyModelViewSet은 읽기 전용 API를 제공하며, list와 retrieve 작업만 지원from rest_framework import viewsets
from .models import MyModel
from .serializers import MyModelSerializer
class MyModelReadOnlyViewSet(viewsets.ReadOnlyModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer

django-extionsion | ipython 설치후 shell_plus 사용 가능
Router
Router는 URL 라우팅을 자동으로 처리해주는 Django REST Framework의 기능
전체 흐름
[Client]
↓ HTTP 요청 (GET/POST/PUT/DELETE)
[Router]
↓ URL → 어떤 ViewSet 메서드를 호출할지 결정
[ViewSet]
↓
Serializer (JSON ↔ Model)
↓
Model (DB Access)
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import MyModelViewSet, custom_view
router = DefaultRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns = [
path('', include(router.urls)),
path('custom/', custom_view),
SimpleRouter는 가장 기본적인 Router로, ViewSet과 연결하여 기본적인 CRUD URL을 자동으로 생성from rest_framework.routers import SimpleRouter
from .views import MyModelViewSet
router = SimpleRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns = router.urls
GET /mymodels/ → list 뷰GET /mymodels/{pk}/ → retrieve 뷰POST /mymodels/ → create 뷰PUT /mymodels/{pk}/ → update 뷰DELETE /mymodels/{pk}/ → destroy 뷰DefaultRouter는 SimpleRouter의 기능에 더해, 기본 API 루트 엔드포인트를 추가로 생성 해줌rom rest_framework.routers import DefaultRouter
from .views import MyModelViewSet
router = DefaultRouter()
router.register(r'mymodels', MyModelViewSet)
urlpatterns = router.urls
# urls.py
router = DefaultRouter()
router.register('posts', PostViewSet)
urlpatterns = [
path('', include(router.urls)),
]
# Router이 자동으로 아래와 같은 URL을 만들어 줌
/posts/ = GET -> 전체 조회
/posts/ = POST -> 생성
/posts/1/ = GET -> 상세 조회
/posts/1/ = PUT -> 전체 수정
/posts/1/ = PATCH -> 부분 수정
/posts/1/ = DELETE -> 삭제
# model.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = "__all__"
# views.py
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
# urls.py
router = DefaultRouter()
router.register("posts", PostViewSet)
urlpatterns = [
path('', include(router.urls)),
]
poetry add djangorestframeworkINSTALLED_APPS 에 'rest_framework', 등록REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}

router.register(r'users', api_views.UserViewSet, basename='user')path('', include(router.urls)) 이걸로 /api/ 는 고정r'users' = include 에서 users로 시작하라는 것과 같은 의미 -> /api/users/
