۹. Viewها و API¶
پنل ادمین بدون کد از قانونها میپرسد. viewهای Django و DRF از طریق چند کلاس میپرسند، و serializer، فرم و قالب چیزی دربارهی دسترسی نمیدانند.
viewهای Django¶
"""The order pages, for a project on templates: the same rules as the API."""
from django.views.generic import ListView, UpdateView
from clearance.views import PolicyRequiredMixin, ProtectedFieldsViewMixin, ScopedQuerySetMixin
from shop.models import Order
from shop.policies import order_access
class OrderList(ProtectedFieldsViewMixin, PolicyRequiredMixin, ScopedQuerySetMixin, ListView):
"""Only the orders the person may view; the price only when they may read it."""
policy = order_access.policy
queryset = Order.objects.select_related('branch', 'customer')
template_name = 'shop/orders.html'
class OrderEdit(ProtectedFieldsViewMixin, PolicyRequiredMixin, ScopedQuerySetMixin, UpdateView):
"""The form offers only the fields, and the status values, the person may write."""
policy = order_access.policy
queryset = Order.objects.all()
fields = ('item', 'status', 'price')
template_name = 'shop/order_form.html'
success_url = '/orders/'
ScopedQuerySetMixinکوئری را به ردیفهایی که شخص به آنها میرسد محدود میکند.PolicyRequiredMixinاز policy میپرسد. اکشن از خود view خوانده میشود -ListViewیعنیlist،UpdateViewیعنیedit- یا باpolicy_actionنام برده میشود.ProtectedFieldsViewMixinفرم را برای شخص میسازد، وreadable_fieldsرا به قالب میدهد:
API¶
"""
The orders API. The serializer knows nothing of access: the viewset narrows
the rows, asks the policy, and leaves out the price for whoever may not read it.
"""
from rest_framework import serializers
from rest_framework.decorators import action
from rest_framework.response import Response
from clearance.drf.viewsets import ScopedModelViewSet
from shop.models import Order
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('id', 'customer', 'branch', 'item', 'status', 'price')
read_only_fields = ('customer',)
class OrderViewSet(ScopedModelViewSet):
queryset = Order.objects.select_related('branch')
serializer_class = OrderSerializer
def perform_create(self, serializer):
serializer.save(customer=self.request.user)
@action(detail=True, methods=['post'])
def print_invoice(self, request, pk=None):
"""Found in the code: shop.order_print_invoice is made by migrate."""
order = self.get_object()
return Response({'invoice': f'INV-{order.pk:05d}', 'item': order.item})
ScopedModelViewSet ردیفها را محدود میکند، برای هر اکشن از policy مدل میپرسد،
و فیلدهایی را که شخص اجازهی خواندنشان را ندارد کنار میگذارد. نوشتنی که اسم
فیلدی را بیاورد که نمیتواند بنویسد - یا مقدار وضعیتی که نمیتواند بگذارد - یک
400 است که اسم فیلد را میبرد.
print_invoice اکشنی است که در کد پیدا میشود: migrate مجوز آن،
shop.order_print_invoice، را میسازد، و فقط کسی که آن را دارد میتواند صدایش
بزند.
viewای که هیچ رکوردی ندارد¶
وقتی ModelPolicyPermission پیشفرض باشد، viewای که نه مدل دارد و نه policy،
موقع بالا آمدن پروژه رد میشود (E009) - از جمله صفحهی اصلی router خود DRF.
فروشگاه مجوزی را که لازم دارد به آن میدهد:
from django.urls import include, path
from rest_framework import routers
from rest_framework.permissions import IsAuthenticated
from shop import api, views
class ShopRouter(routers.DefaultRouter):
class APIRootView(routers.APIRootView):
"""
The list of endpoints holds no record, so it asks no policy - said here,
because every view asks one unless it says otherwise (E009).
"""
permission_classes = (IsAuthenticated,)
router = ShopRouter()
router.register('orders', api.OrderViewSet)
urlpatterns = [
path('orders/', views.OrderList.as_view(), name='orders'),
path('orders/<int:pk>/', views.OrderEdit.as_view(), name='order'),
path('api/', include(router.urls)),
]
امتحان کنید¶
با alice وارد شوید و به /api/orders/ بفرستید:
جواب یک 400 است که اسم price را میبرد: مشتری میتواند سفارش ثبت کند، و هیچوقت
قیمتش را تعیین نمیکند.