Свяжите детали транзакции с продуктом

Я пытаюсь связать несколько моделей вместе, чтобы показать, какой продукт держит пользователь, а также цену, которую он заплатил, и сумму, которую он держит. Которые будут отображаться в таблице.

У меня есть модели Profile Product, Holding, и я хочу добавить модель Transaction, которая связана с моделью Profile, и Holding, чтобы можно было добавить количество купленного и цену.

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

Модели:

class Product(models.Model):

    product_name = models.CharField(max_length=255, blank=False, unique=True)
    product_slug = models.CharField(max_length=50, blank=True,null=True)
    product_symbol = models.CharField(max_length=50, blank=True,null=True)
    product_price = models.FloatField(blank=True,null=True)
    product_capture_date = models.DateField(blank=True,null=True)
    product_logo_address = models.URLField(max_length=255, blank=True)

    def __str__(self):
        return str(self.product_name)

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)
    products = models.ManyToManyField(Product, blank=True, through='Holding')
    website = models.URLField(max_length=50, blank=True)
    twitter = models.CharField(max_length=50, blank=True)
    meta = models.CharField(max_length=50, blank=True)
    github = models.CharField(max_length=50, blank=True)

    def __str__(self):
        return str(self.user)

class Holding(models.Model):

    product_name = models.ForeignKey(Product, on_delete=models.CASCADE)
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
    product_holding = models.FloatField(max_length=50, blank=True, null=True)

    def __str__(self):
        return str(self.product_name)

class Transaction(models.Model):

    product = models.ForeignKey(Holding, on_delete=models.CASCADE)
    amount = models.FloatField(max_length=50, blank=True, null=True)
    price = models.FloatField(max_length=50, blank=True, null=True)
    transaction_date = models.DateField(null=True, blank=True)
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE)

Я создал модель транзакций и связал ее с FK с холдингом и профилем - похоже, это работает для распределения данных в модели и позволяет мне добавить несколько транзакций к продукту для профиля.

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

profile = Profile.objects.get(user = request.user)
transact = Transaction.objects.filter(profile = profile)

Итак, в моей таблице я хочу показать product_name, transaction.price и product_holding.

Выглядит хорошо, за исключением того, что вам не нужно поле profile на Transaction, потому что оно уже есть на product/Holding товаре. Также не понятно, зачем нужно ManyToMany на Profile с product.

Так что я бы рефакторизовал это следующим образом (см. комментарии в строке):

class Product(models.Model):
    # no need to say 'product_name' because it is on model Product
    name = models.CharField(max_length=255, blank=False, unique=True)
    slug = models.CharField(max_length=50, blank=True,null=True)
    symbol = models.CharField(max_length=50, blank=True,null=True)
    
    # Float does not suite price due to its low precision. Use Decimal instrad
    price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    capture_date = models.DateField(blank=True,null=True)
    logo_address = models.URLField(max_length=255, blank=True)

    def __str__(self):
        return self.name

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

    # no need of products here - can be find via Holding
    # products = models.ManyToManyField(Product, blank=True, through='Holding')
    website = models.URLField(max_length=50, blank=True)
    twitter = models.CharField(max_length=50, blank=True)
    meta = models.CharField(max_length=50, blank=True)
    github = models.CharField(max_length=50, blank=True)

    def __str__(self):
        return str(self.user)

class Holding(models.Model):
    # the field is not product_name - it is actually `product` itself
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
    product_holding = models.FloatField(max_length=50, blank=True, null=True)

    def __str__(self):
        return str(self.product_name)

class Transaction(models.Model):

    product = models.ForeignKey(Holding, on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    price = models.FloatField(max_length=50, blank=True, null=True)
    transaction_date = models.DateField(null=True, blank=True)
    profile = models.ForeignKey(Profile, on_delete=models.CASCADE)

И фильтрация:

profile = Profile.objects.get(user = request.user)

transactions = Transaction.objects.filter(
    holding__in=Holding.objects.filter(profile=profile)
)

for t in transactions:
    product_name = t.holding.product.product_name
    price = t.price
    product_holding = t.holding.product_holding

Если вы хотите узнать продукты конкретного пользователя (с тех пор как мы убрали ManyToMany):

products = [x.product for x in Holding.objects.filter(profile=profile)]

Или если конкретный пользователь держит конкретный продукт:

product = Product.objects.get(name='specific_product')
exists = Holding.objects.filter(profile=profile, product=product).exists()
Вернуться на верх