DRF의 클래스 기반 뷰는 강력합니다.
여기에서는 REST API를 빌드할 때 참조할 수 있는 GenericViews 및 ViewSets에 대한 몇 가지 팁을 편집했습니다.
목차
Using GenericViews
get_object(self)
get_queryset(self)
perform_create(self, serializer)
The Power of ViewSets
전제 조건
This article assumes that you have a basic understanding on Django and Django Rest Framework (DRF). A decent understanding of serializers and views is also needed.
소개
You might be asking yourself, why should I even use class-based views when I can have more control of what is happening in function-based views? This section will answer that and highlight class-based views.
A view is a place where you can put the logic of your application. In general, we have 2 types of views in Django:
- Function-based views
- Class-based views
The major problem with function-based views is the lack of an easy way to extend them. You may see yourself using the same kind of code over and over again throughout your project which is not a good design principle.
In addition, function-based views use conditional branching inside a single view function to handle different HTTP methods which might make your code unreadable.
Class-based views aren’t a replacement for function-based views. However, they provide you with a way of writing views in an object-oriented fashion. This means that they can be really powerful and highly extensible by using concepts from OOP such as inheritance and Mixin (multiple inheritance).
Anyways, we have the following class-based views in DRF:
-
APIView
-
GenericView
-
ViewSets
APIView
is similar to Django’s View
class (It is actually an extension of it). You may have been using this approach to dispatch your requests to an appropriate handler like get()
or post()
.
That being said, let's get started.
GenericView 사용
Can you think of some tasks that you repeat very often while working on a project? Tasks like form handling, list view, pagination, and many other common tasks might make your development experience boring because you repeat the same pattern over and over again. GenericViews come to the rescue by taking certain common patterns and abstracting them so that you can quickly write common views of data and save yourself time for a cup of tea🍵
Here are some of the methods you might often want to override when using GenericViews.
get_object(자신)
Assume you want to have a view that will handle a user’s request to retrieve and update their profile.
class ProfileAPIView(RetrieveUpdateAPIView):
"""
Get, Update user profile
"""
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = (IsUserProfileOwner,)
- Simple right? you override the
queryset
attribute to determine the object you want the view to return, as well as your serializer class and permission class. Then, you define the path inurls.py
file like this.
path('profile/<int:pk>/', ProfileAPIView.as_view(), name='profile_detail'),
- This is the kind of pattern you may have used to achieve the goal. However, another way of doing the same thing is by overriding the
get_object(self)
method to return a profile instance without having to provide a lookup field (<int:pk>
) in your path.
class ProfileAPIView(RetrieveUpdateAPIView):
"""
Get, Update user profile
"""
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = (IsUserProfileOwner,)
def get_object(self):
return self.request.user.profile
This way, you can modify urls.py
file to remove <int:pk>
from the path:
path('profile/', ProfileAPIView.as_view(), name='profile_detail'),
.
get_queryset(자신)
Want to return a queryset
that is specific to the user making a request? You can do so using get_queryset(self)
method
class OrderListCreate(ListCreateAPIView):
"""
List, Create orders of a user
"""
queryset = Order.objects.all()
permission_classes = (IsOrderByBuyerOrAdmin, )
def get_queryset(self):
res = super().get_queryset()
user = self.request.user
return res.filter(buyer=user)
- The
get_queryset(self)
method filters the response to include a list of orders of the currently authenticated user.
perform_create(자체, 직렬 변환기)
Assume you have a Recipe
class. When users want to create a recipe, you need to hide the author
field in your serializer:
author = serializers.PrimaryKeyRelatedField(read_only=True)
and then in your views, you can automatically set author
to the currently authenticated user by overriding the perform_create(self, serializer)
method.
class RecipeCreateAPIView(CreateAPIView):
"""
Create: a recipe
"""
queryset = Recipe.objects.all()
serializer_class = RecipeSerializer
permission_classes = (IsAuthenticated,)
def perform_create(self, serializer):
serializer.save(author=self.request.user)
Similar to perform_create(self, serializer)
, there are also perform_update(self, serializer)
and perform_destroy(self, serializer)
methods.
ViewSet의 힘
ViewSet
is a type of class-based view that combines the logic for a set of related views into a single class. The 2 most common types of ViewSets that you are most likely to use are Modelviewset
and ReadOnlyModelViewSet
Say you want to perform CRUD operations on a user's order. Using ModelViewSet
, doing so is as simple as:
class OrderViewSet(ModelViewSet):
"""
CRUD orders of a user
"""
queryset = Order.objects.all()
serializer_class = (SomeSerializer, )
permission_classes = (SomePermission, )
- The above class provides you with everything you need for CRUD operations on the
Order
model. In addition, one of the main advantages of usingModelViewSet
, or any other type ofViewSet
, is to have URL endpoints automatically defined for you throughRouters
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from orders.views import OrderViewSet
app_name = 'orders'
router = DefaultRouter()
router.register(r'', OrderViewSet)
urlpatterns = [
path('', include(router.urls)),
]
- With just that, you have URL endpoints for
list
,create
,retrieve
,update
, anddestroy
actions!
읽기 및 쓰기를 위한 다양한 직렬 변환기
Ever needed to separate your serializer for read and write operation? Perhaps because you have a lot of nested fields, but you only need a few of them for write operation? You can easily use a single view for both serializers by overriding the get_serializer_class(self)
method.
class ProductViewSet(ModelViewSet):
"""
CRUD products
"""
queryset = Product.objects.all()
def get_serializer_class(self):
if self.action in ('create', 'update', 'partial_update', 'destroy'):
return ProductWriteSerializer
return ProductReadSerializer
다른 작업에 대한 다른 권한
get_permissions(self)
method helps you separate permissions for different actions inside the same view.
def get_permissions(self):
if self.action in ("create", ):
self.permission_classes = (permissions.IsAuthenticated, )
elif self.action in ('update', 'partial_update', 'destroy'):
self.permission_classes = (IsSellerOrAdmin, )
else:
self.permission_classes = (permissions.AllowAny, )
return super().get_permissions()
Note: You can use methods like get_queryset(self)
, perform_create(self, serializer)
et cetera inside Vewsets as well.
ReadOnlyModelViewSet
If you plan to make your view read-only, you can use ReadOnlyModelViewSet
class ProductCategoryViewSet(ReadOnlyModelViewSet):
"""
List and Retrieve product categories
"""
queryset = ProductCategory.objects.all()
serializer_class = ProductCategoryReadSerializer
permission_classes = (permissions.AllowAny, )
결론
In general, you can see that ViewSets have the highest level of abstraction and you can use them to avoid writing all the code for basic and repetitive stuff. They are a huge time-saver! However, if you need to have more control or do some custom work, using APIView
or GenericAPIView
makes sense.
For instance, in the following code, I’m using APIView
to create a Stripe
checkout session. I think this is a good candidate for using APIView
class StripeCheckoutSessionCreateAPIView(APIView):
"""
Create and return checkout session ID for order payment of type 'Stripe'
"""
permission_classes = (IsPaymentForOrderNotCompleted,
DoesOrderHaveAddress, )
def post(self, request, *args, **kwargs):
order = get_object_or_404(Order, id=self.kwargs.get('order_id'))
order_items = []
for order_item in order.order_items.all():
product = order_item.product
quantity = order_item.quantity
data = {
'price_data': {
'currency': 'usd',
'unit_amount_decimal': product.price,
'product_data': {
'name': product.name,
'description': product.desc,
'images': [f'{settings.BACKEND_DOMAIN}{product.image.url}']
}
},
'quantity': quantity
}
order_items.append(data)
checkout_session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=order_items,
metadata={
"order_id": order.id
},
mode='payment',
success_url=settings.PAYMENT_SUCCESS_URL,
cancel_url=settings.PAYMENT_CANCEL_URL
)
return Response({'sessionId': checkout_session['id']}, status=status.HTTP_201_CREATED)
스 니펫은 GitHub 의 내 프로젝트에서 가져옵니다. 당신은 그들을 확인할 수 있습니다.
즐거운 코딩하세요! 🖤
Reference
이 문제에 관하여(DRF의 클래스 기반 뷰는 강력합니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/earthcomfy/class-based-views-in-drf-are-powerful-19dg텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)