Django; обновлять товары, но сохранять историю покупок
TLDR: Как редактировать продукты без изменения истории заказов/покупок, в которых этот продукт был куплен?
Я хочу включить возможность совершать покупки на своем сайте. У меня есть бэкенд с django-drf и фронтенд react. Во фронтенде я хочу иметь возможность добавлять, редактировать и удалять товары. Для создания согласованной базы данных я вижу две вещи, которые делаются на стеке:
- copy all relevant fields from the product table over to the purchase table
- 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.?