์ ๋๋ฆญ ๋ทฐ(Generic Views)๋ Django REST Framework์์ ์ ๊ณตํ๋ ๊ธฐ๋ณธ์ ์ธ CRUD(Create, Retrieve, Update, Delete) ์์ ์ ์ํํ๋ ๋ทฐ๋ฅผ ์์ฝ๊ฒ ๋ง๋ค ์ ์๋๋ก ๋์์ฃผ๋ ํด๋์ค์ ๋๋ค. ์ด๋ค์ APIView๋ฅผ ์์๋ฐ์ ํน์ ์์ ์ ์๋์ผ๋ก ์ฒ๋ฆฌํด์ฃผ๋ ๋ทฐ ํด๋์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
from django.db.models import Q
from rest_framework.generics import ListAPIView, RetrieveAPIView
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from blog.models import Blog
from blog.serializers import BlogSerializer
class BlogListAPIView(ListAPIView):
queryset = Blog.objects.all()
serializer_class = BlogSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def get_queryset(self):
queryset=super().get_queryset()
# ํจ์๋ฅผ ์ด๋ ๊ฒ ์ ์ธํด์ ๋ฃ์ด์ผ datetime.now๊ฐ ๊ทธ ๋ฐ์์๊ฐ์ ์ ์ธ๋๋ค. ํด๋์ค๋ด์์ ๋ง๋ค๋ฉด ๊ทธํด๋์ค๊ฐ ์ฒ์์ ์ ์ธ ๋ฌ์๋ ์์๊ฐ์ผ๋ก ์์ฑ๋จ
return queryset.filter(
Q(published_at__isnull=True) |
Q(published_at__gte=timezone.now())
).order_by('-created_at').select_related('author')
# blog/urls/generics_urls.py
from django.urls import path, include
from blog.views import generics_views
app_name ='generics_api'
# blog/urls/api_urls.py
urlpatterns = [
path('blog', generics_views.BlogListAPIView.as_view(), name='blog_list'),
]
class BlogRetrieveAPIView(BlogQuerySetMixin, RetrieveAPIView):
model = Blog
queryset = Blog.objects.all()
serializer_class = BlogSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(
Q(published_at__isnull=True) |
Q(published_at__gte=timezone.now())
).order_by('-created_at').select_related('author')
# blog/urls/generics_urls.py
from django.urls import path, include
from blog.views import generics_views
app_name ='generics_api'
# blog/urls/api_urls.py
urlpatterns = [
path('blog/<int:pk>', generics_views.BlogRetrieveAPIView.as_view(), name='blog_detail'),
]
class BlogQuerySetMixin:
serializer_class = BlogSerializer
queryset = Blog.objects.all()
permission_classes = [IsAuthenticatedOrReadOnly]
def get_queryset(self):
return self.queryset.filter(
Q(published_at__isnull=True) |
Q(published_at__gte=timezone.now())
).order_by('-created_at').select_related('author')
class BlogListAPIView(BlogQuerySetMixin, ListAPIView):
pass
class BlogRetrieveAPIView(BlogQuerySetMixin, RetrieveAPIView):
pass
class BlogQuerySetMixin:
serializer_class = BlogSerializer
queryset = Blog.objects.all()
permission_classes = [IsAuthenticatedOrReadOnly]
def get_queryset(self):
return self.queryset.filter(
Q(published_at__isnull=True) |
Q(published_at__gte=timezone.now())
).order_by('-created_at').select_related('author')
class BlogListAPIView(BlogQuerySetMixin, ListCreateAPIView):
pass
class BlogRetrieveAPIView(BlogQuerySetMixin, RetrieveUpdateDestroyAPIView):
pass
class BlogListAPIView(BlogQuerySetMixin, ListCreateAPIView):
def perform_create(self, serializer):
serializer.save(author=self.request.user)
from permissions import IsAuthorOrReadOnly
class BlogRetrieveAPIView(BlogQuerySetMixin, RetrieveUpdateDestroyAPIView):
permission_classes = [IsAuthorOrReadOnly]
# utils/permissions.py
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_pers(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
try:
obj = view.get_object(request, *view.args, **view.kwargs)
except TypeError:
# get_object ๊ฐ ๋ค๋ฆ
obj = view.get_object()
return obj.author == request.user
class BlogSerializer(serializers.ModelSerializer):
author = UserSerializer(many=False, read_only=True)
# read_only => ํด๋นํ๋๋ฅผ ํด๋ผ์ด์ธํธ๊ฐ ์์ ํ ์ ์๊ฒ ํด๋๋ ๊ฒ
# foreignkey ์ฐ๊ฒฐ๋๊ฒ ์ฐ๊ฒฐํ๋๊ฒ serialzer ๋ก ํด๋์๊ฒ ์ด๋ ๊ฒ ์ฌ์ฉ๊ฐ๋ฅ
# ์ด๋ ๊ฒ ์ฐ๋ฉด ๊ผญ ๊น๋จน์ง๋ง๊ณ views ์์ select_related ๋ฑ๋กํ๊ธฐ
# BlogSerializer๋ฅผ ์ฌ์ฉํ๋ฉด author๊ฐ ์๋์ผ๋ก ๋ถ๋ฌ์์ง
comment_count = serializers.SerializerMethodField() # ์ด๋ ๊ฒ ์ฐ๋ฉด ์ํ์ด์ง์์ ๋๊ธ ์ซ์๋ฅผ ํ์ธํ ์์์
# SerializerMethodField ๋ฉ์๋๋ ๋ณ์ ์์ get_ ์ ๋ถ์ฌ ํจ์๋ก ๋ง๋ค๋ฉด ๋ณ์๋ฅผ Meta์์ ์ฌ์ฉํ๊ฒ ํด์ฃผ๋ ๋ฉ์๋์ด๋ค
author_name = serializers.SerializerMethodField()
def get_comment_count(self, obj):
return obj.comment_set.count()
def get_author_name(self, obj):
return obj.author.username
# ๋ง๋ ๊ฒ ๊ผญ feild์ ๋ฃ์ด์ผ ๋ณด์
# author์ name ๋ง ๋ณด๋ฉด ๋์ด์ ์ด์ author ๋ฅผ ๋นผ๋ author๋ฅผ ํ์ธํ ์ ์๋ค.
class Meta:
model = Blog
fields = ['title', 'content', 'author', 'published_at', 'created_at',
'updated_at', 'comment_count','author_name']
# models.py
class Comment(TimestampModel) :
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField('๋ณธ๋ฌธ')
class Meta:
verbose_name = '๋๊ธ'
verbose_name_plural ='๋๊ธ ๋ชฉ๋ก'
ordering = ('-created_at', '-id')
# serializers.py
class CommentSerializer(serializers.ModelSerializer):
blog = BlogSerializer(many=False, read_only=True)
author = UserSerializer(many=False, read_only=True)
class Meta:
model = Comment
fields = ['id', 'blog', 'author', 'content', ]
# generics_views.py
class CommentListCreateAPIView(ListCreateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
blog=self.get_blog_object()
serializer.save(author=self.request.user, blog = blog)
def get_queryset(self):
queryset = super().get_queryset()
blog = self.get_blog_object()
return queryset.filter(blog=blog)
def get_blog_object(self):
return get_object_or_404(Blog, pk=self.kwargs.get('blog_pk'))
# generics_urls.py
path('comment/<int:blog_pk>', generics_views.CommentListCreateAPIView.as_view(), name='comment_list_create'),
# DELETE,UPDATE
# ์์ธํ์ด์ง๊ฐ ์๊ธฐ์ ์ด๋ ๊ฒ ์์๋ฐ๋๊ฒ ์ ์ ํจ
class CommentUpdateDestroyAPIView(UpdateAPIView, DestroyAPIView):
queryset = Comment.objects.all()
serializer_class = CommentBlogSerializer
permission_classes = [IsAuthorOrReadOnly]
# serializers.py
class CommentBlogSerializer(serializers.ModelSerializer):
author = UserSerializer(many=False, read_only=True)
blog = BlogSerializer(many=False, read_only=True)
class Meta:
model = Comment
fields = ['id', 'author', 'content','blog' ]
super().get_queryset()
์ ์ฌ์ฉํ๋ ์ด์ - ๋ ๊น์ด ์ดํดํ๊ธฐDjango REST Framework(DRF)์ ListAPIView
๋ ๊ธฐ๋ณธ์ ์ผ๋ก get_queryset()
์ ํธ์ถํด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
class ListAPIView(GenericAPIView, mixins.ListModelMixin):
def get_queryset(self):
assert self.queryset is not None, (
f"{self.__class__.__name__} should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
)
return self.queryset.all()
โ ์ฆ, ListAPIView
๋ ๊ธฐ๋ณธ์ ์ผ๋ก self.queryset
์ ์ฌ์ฉํด์ queryset.all()
์ ์คํํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํฉ๋๋ค.
โ ๋ง์ฝ ์ฐ๋ฆฌ๊ฐ queryset
์ ์ ์ํ์ง ์์ผ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
queryset
์ ๋ค์ ์ค์ ํ๋ ์ด์ ๐ ์๋ชป๋ ๋ฐฉ์ (๋ถ๋ชจ์ ๊ธฐ๋ฅ์ ๋ฌด์ํ๊ณ ์ง์ queryset
์ค์ )
class BlogListAPIView(ListAPIView):
serializer_class = BlogSerializer
def get_queryset(self):
return Blog.objects.filter(published_at__lte=timezone.now()).order_by("-created_at")
๐จ ๋ฌธ์ ์
ListAPIView
๋ ๊ธฐ๋ณธ์ ์ผ๋ก self.queryset
์ ํ์ฉํ๋ ๊ตฌ์กฐ์ธ๋ฐ, ์ด๋ ๊ฒ ํ๋ฉด ๋ถ๋ชจ ํด๋์ค(ListAPIView
)์ queryset
์ ์์ ๋ฌด์ํจ. BlogListAPIView
๋ฅผ ์์๋ฐ์์ ํ์ฅํ ๋ ๋ถ๋ชจ์ get_queryset()
์ ์ฌ์ฌ์ฉํ ์ ์์. ๐ ์ฌ๋ฐ๋ฅธ ๋ฐฉ์ (๋ถ๋ชจ์ ๊ธฐ๋ฅ์ ์ ์งํ๋ฉด์ ํํฐ๋ง ์ถ๊ฐ)
class BlogListAPIView(ListAPIView):
queryset = Blog.objects.all() # ๊ธฐ๋ณธ QuerySet ์ค์
serializer_class = BlogSerializer
def get_queryset(self):
queryset = super().get_queryset() # ๋ถ๋ชจ ํด๋์ค์ get_queryset() ์คํ
return queryset.filter(published_at__lte=timezone.now()).order_by("-created_at")
โ
์ด ๋ฐฉ์์ด ์ข์ ์ด์
1. super().get_queryset()
์ ํธ์ถํ๋ฉด ๋ถ๋ชจ ํด๋์ค(ListAPIView
)์์ ์ค์ ํ QuerySet์ ๋จผ์ ๊ฐ์ ธ์จ ํ, ์ถ๊ฐ์ ์ธ ํํฐ๋ง์ ์ ์ฉํ ์ ์์.
2. ์ด๋ ๊ฒ ํ๋ฉด ๋ง์ฝ ๋ค๋ฅธ ๊ณณ์์ queryset
์ ๋์ ์ผ๋ก ๋ณ๊ฒฝํด๋ get_queryset()
์ ์์ ํ์ง ์์๋ ๋จ.
3. super().get_queryset()
์ ์ฌ์ฉํ๋ฉด ์ถํ BlogListAPIView
๋ฅผ ์์๋ฐ์ ์ฌ์ฉํ ๊ฒฝ์ฐ, get_queryset()
์ ๋์์ ์ ์งํ๋ฉด์ ์ถ๊ฐ์ ์ธ ํํฐ๋ง์ ์ ์ฉํ ์ ์์.
ListAPIView
๋ฅผ ์์๋ฐ์ผ๋ฉด ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ(ListModelMixin
์ ๊ธฐ๋ฅ ํฌํจ)์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํ์ง๋ง ํ๋ก์ ํธ ์๊ตฌ์ฌํญ์ ๋ง์ถฐ ์ฟผ๋ฆฌ์
์ ์์ ํ๊ณ ์ถ์ ๋ get_queryset()
์ ์ค๋ฒ๋ผ์ด๋ฉํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ธ ํจํด์
๋๋ค.
๐ ์ดํด๋ฅผ ๋๊ธฐ ์ํ ๋น์
โ ListAPIView
๋ ์๋์ฐจ์ ๊ธฐ๋ณธ ๋ชจ๋ธ
โ BlogListAPIView
๋ ๊ธฐ๋ณธ ๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ์ต์
(์ฟผ๋ฆฌ ํํฐ๋ง ๋ฑ)์ ์ถ๊ฐํ ์คํฌ์ธ ์นด
๐ ์๋์ฐจ ๊ธฐ๋ณธ ๋ชจ๋ธ (ListAPIView)
class Car:
def drive(self):
print("Driving a car")
๐ ์์์ ํ์ฉํ ํ์ฅ (BlogListAPIView)
class SportsCar(Car):
def drive(self):
print("Super speed mode activated!")
super().drive() # โ
๋ถ๋ชจ ํด๋์ค์ ๊ธฐ๋ณธ ๋์์ ์ ์งํ๋ฉด์ ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐ
๐ ๊ฒฐ๊ณผ
my_car = SportsCar()
my_car.drive()
๐ ์ถ๋ ฅ:
Super speed mode activated!
Driving a car
โ ์ฆ, ๋ถ๋ชจ์ ๊ธฐ๋ฅ์ ์ ์งํ๋ฉด์๋ ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒ์ด ํต์ฌ!
์ง๋ฌธ | ์ค๋ช |
---|---|
๋ถ๋ชจ ํด๋์ค์ queryset ์ด ์๋๋ฐ ์ get_queryset() ์์ ๋ ์ค์ ํ๋๊ฐ? | ๋ถ๋ชจ ํด๋์ค์ ๊ธฐ๋ณธ ๋์์ ์ ์งํ๋ฉด์ ํน์ ํํฐ๋ฅผ ์ถ๊ฐํ๊ธฐ ์ํด |
๋ถ๋ชจ์ queryset ์ ์ง์ ์ฌ์ฉํ๋ฉด ์ ๋๋๊ฐ? | ๊ฐ๋ฅํ์ง๋ง, super().get_queryset() ์ ์ฌ์ฉํ๋ฉด ์ ์ง๋ณด์์ ํ์ฅ์ฑ์ด ๋ ์ข์์ง |
๊ทธ๋ผ queryset ์ ์ค์ ํ๊ณ get_queryset() ์ ์ค๋ฒ๋ผ์ด๋ฉํ๋ฉด ์ super() ๋ฅผ ํธ์ถํ๋๊ฐ? | ๋ค๋ฅธ ๊ณณ์์ queryset ์ด ๋ณ๊ฒฝ๋ ๊ฐ๋ฅ์ฑ์ด ์๊ธฐ ๋๋ฌธ. super().get_queryset() ์ ์ฌ์ฉํ๋ฉด ๋์ ์ผ๋ก ๋ณ๊ฒฝ๋ ๋ถ๋ชจ์ queryset ์ ํ์ฉํ ์ ์์ |
๐ฅ ์ฆ, super().get_queryset()
์ ์ฌ์ฉํ๋ฉด ๋ถ๋ชจ์ queryset
์ ๊ธฐ๋ฐ์ผ๋ก ํํฐ๋ง์ ์ถ๊ฐํ ์ ์์ด์ ๋ ์ ์ฐํ๊ณ ํ์ฅ์ฑ์ด ๋์์ง๋ค! ๐