Код-ревью Middle Django: устройство обновлено

Код-ревью Django-вьюхи

Вопрос

Найдите проблемы во вьюхе, которая показывает заказ и отмечает его просмотренным.

from django.http import JsonResponse
from .models import Order

_cache = {}


def order_view(request, pk):
    order = Order.objects.get(pk=pk)
    if request.GET.get('mark_seen'):
        order.seen = True
        order.save()
    items = []
    for item in order.items.all():
        items.append({
            'name': item.product.name,
            'price': str(item.price),
        })
    _cache[pk] = items
    return JsonResponse({
        'id': order.id,
        'owner_email': order.owner.email,
        'items': items,
    })

Коротко

Исправленная версия: Отметку о просмотре выносим в отдельный POST-эндпоинт. Глобальный словарь убираем: в процессе gunicorn он живёт до перезапуска, растёт и не виден другим воркерам.

Что должен найти ревьюер

  1. Ошибкастрока 8
    Order.objects.get при отсутствии заказа даёт 500 DoesNotExist; нужен get_object_or_404
  2. Безопасностьстрока 8
    Нет проверки владельца: любой пользователь откроет чужой заказ по id (IDOR). Фильтровать по owner=request.user и требовать вход
  3. Ошибкастрока 9
    Изменение данных по GET-запросу: GET должен быть безопасным, его повторяют браузеры, кеши и краулеры; нужен POST
  4. Производительностьстрока 15
    N+1: для каждой позиции отдельный запрос за product; нужен select_related("product") или prefetch_related
  5. Ошибкастрока 18
    Глобальный словарь как кеш растёт без ограничений и не делится между процессами; использовать кеш Django с TTL или убрать

Разбор

Исправленная версия:

@login_required
@require_GET
def order_view(request, pk):
    order = get_object_or_404(
        Order.objects.select_related('owner'),
        pk=pk, owner=request.user,
    )
    items = [
        {'name': i.product.name, 'price': str(i.price)}
        for i in order.items.select_related('product')
    ]
    return JsonResponse({'id': order.id, 'items': items})

Отметку о просмотре выносим в отдельный POST-эндпоинт. Глобальный словарь убираем: в процессе gunicorn он живёт до перезапуска, растёт и не виден другим воркерам. Если email владельца не нужен клиенту, его тоже не стоит отдавать.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.