Home > Software design >  Link transaction details with Product
Link transaction details with Product

Time:02-07

I'm trying to link some models together to show what product a user is holding as well as the price they paid and the amount they hold. Which will be displayed within a table.

I have Profile Product, Holding models, and want to add a Transaction model that is linked to the Profile, and Holding model to be able to add purchased amount and price.

I'm not sure what the best way to link these models is to be able to easily present the data within a table.

Models:

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)

I've created the transaction model and linked it with a FK to Holding and Profile - this seems to work for allocating the data to the model and allows me to add multiple transactions to a product for a profile.

But I think there is a better way to work within the view, bearing in mind that a product could have multiple transactions assigned and wouldn't all the transactions listed just the total amount and average price.

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

So within my table, I want to show the product_name, transaction.price, and product_holding.

CodePudding user response:

Looks good except that you don't need profile field on Transaction because it's already on product/Holding item. As well as not clear why need to have ManyToMany on Profile with product.

So I would refactor that as follows (see comments inline):

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)

And filtering:

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

If you want to know the products of a specific user (since we removed ManyToMany):

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

Or if a specific user hold specific product:

product = Product.objects.get(name='specific_product')
exists = Holding.objects.filter(profile=profile, product=product).exists()
  •  Tags:  
  • Related