Django; обновлять товары, но сохранять историю покупок

TLDR: Как редактировать продукты без изменения истории заказов/покупок, в которых этот продукт был куплен?

Я хочу включить возможность совершать покупки на своем сайте. У меня есть бэкенд с django-drf и фронтенд react. Во фронтенде я хочу иметь возможность добавлять, редактировать и удалять товары. Для создания согласованной базы данных я вижу две вещи, которые делаются на стеке:

  1. copy all relevant fields from the product table over to the purchase table
  2. upon edit of a product create a new product instead of actually updating, and upon removal of a product flag it as deleted instead of actually deleting

Я склоняюсь ко второму варианту, так как в этом случае базу данных легче понять и поддерживать. Однако это усложняет логику обновления и удаления.

Мои жесткие требования к реализации примерно таковы:

models.py
from django.db import models


class Product(models.Model) :
    name = models.CharField(max_length=120)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    has_been_removed = models.BooleanField(default=False)
    ...

class Purchase(models.Model) :
    product = models.ForeignKey(Product, related_name="purchases", on_delete=models.PROTECT)
    quantity = models.IntegerField(max_length=120)
    total = models.DecimalField(max_digits=10, decimal_places=2)
    ...
serializer.py
from django.db import models
from .models import Product, Purchase


class ProductSerializer(models.Serializer):
    ...

    def update(self, instance: Product, validated_data):
        has_associated_purchases = Purchases.objects.filter(product=instance).exists()
        if has_associated_purchases:
            new_product = Product(**validated_data)
            new_product.save()
            return new_product
        else:
            return super().update(instance, validated_data)
viewsets.py
from rest_framework import viewsets
from .models import Product, Purchase


class ProductViewset(viewsets.ModelViewSet):
    ...

    def get_queryset(self):
        return Product.objects.filter(has_been_removed=False)

    def destroy(self, request, *args, **kwargs):
        product = self.get_object()
        has_associated_purchases = Purchase.objects.filter(product=product).exists()
        if has_associated_purchases:
            product.has_been_removed = True
            product.save()
        else:
            self.perform_destroy(product)
        return Response(status=status.HTTP_204_NO_CONTENT)

Так ли нужно реализовывать вариант 2? Возникнут ли при этом проблемы? Есть ли лучшая стратегия, чем 1. или 2.?

Вернуться на верх