Код-ревью Middle Django REST Framework спрашивают часто обновлено
Код-ревью DRF-вьюсета
Вопрос
Найдите проблемы в сериализаторе и вьюсете заказов.
from rest_framework import serializers, viewsets
from .models import Order
class OrderSerializer(serializers.ModelSerializer):
customer_name = serializers.CharField(source='customer.name')
items_count = serializers.SerializerMethodField()
class Meta:
model = Order
fields = '__all__'
def get_items_count(self, obj):
return obj.items.count()
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
pagination_class = None
def perform_create(self, serializer):
serializer.save(owner_id=self.request.data['owner_id'])
Коротко
Исправленный вариант: Пагинация берётся из настроек проекта (DEFAULTPAGINATIONCLASS).
Что должен найти ревьюер
- Безопасностьстрока 11
fields = "__all__"отдаёт и принимает на запись все поля модели, включая служебные (статус, скидка); нужен явный список иread_only_fields - Производительностьстрока 14
obj.items.count()делает запрос на каждый заказ (N+1); использоватьannotate(items_count=Count("items")) - Безопасностьстрока 18Нет фильтра по пользователю и прав: любой видит и меняет все заказы (IDOR);
get_querysetс фильтром иpermission_classes - Производительностьстрока 18Нет
select_related("customer")дляcustomer_name: N+1 на каждый заказ - Производительностьстрока 20Пагинация отключена: список отдаст все заказы разом
- Безопасностьстрока 23Владелец берётся из тела запроса: клиент создаст заказ от чужого имени; использовать
owner=self.request.user
Разбор
Исправленный вариант:
class OrderSerializer(serializers.ModelSerializer):
customer_name = serializers.CharField(source='customer.name', read_only=True)
items_count = serializers.IntegerField(read_only=True)
class Meta:
model = Order
fields = ['id', 'status', 'total', 'customer_name', 'items_count', 'created_at']
read_only_fields = ['status', 'created_at']
class OrderViewSet(viewsets.ModelViewSet):
serializer_class = OrderSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return (Order.objects.filter(owner=self.request.user)
.select_related('customer')
.annotate(items_count=Count('items')))
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Пагинация берётся из настроек проекта (DEFAULT_PAGINATION_CLASS).
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.