How to model many-to-many database with 3 tables

Question:

I’m working on a django backend and I’m trying to model a database and want to do it the best practice way.
I need a "User" table, a "Portfolios" table and a "Stocks" table. A user can have multiple portfolios which consist of multiple stocks.

This is my code so far:

class User(models.Model):
    user_id = models.AutoField(primary_key=True)
    username = models.CharField(max_length=25)
    in_cash = models.DecimalField(max_digits=15, decimal_places=2)

class Portfolios(models.Model):
    portfolio_id = models.AutoField(primary_key=True)
    user_id = models.ForeignKey("User", on_delete=models.CASCADE)
    stock_id = models.ForeignKey("Stocks", on_delete=models.CASCADE)
    buy_datetime = models.DateTimeField(default=datetime.now, blank=True)
    number_of_shares = models.IntegerField()

class Stocks(models.Model):
    stock_id = models.AutoField(primary_key=True)
    stock_symbol = models.CharField(max_length=12)

In my head I would have an entry in the "Portfolios" table for each stock of a portfolio.
So "Portfolios" would look like

  • portfolioid 1, userid: 1, stockid: 1, buydate: 12.01.2019, shares: 20
  • portfolioid 1, userid: 1, stockid: 2, buydate: 19.02.2020, shares: 41

So there is one portfolio, which contains two stocks. But overall that doesn’t seem right. If used like in my example above I can’t have the portfolioid as a primary key, how can I improve this?

Thanks for your time

Asked By: Twenny

||

Answers:

As in usual many-to-many cases, you will need to create an intermediary table which is also called junction table/association table. Associative Entity

Your users are going to have several portfolios:

class UserPortfolio(models.Model):
    user_id = models.ForeignKey("User")
    portfolio_id = models.ForeignKey("Portfolio")

The portfolios will have multiple stocks in them:

class PortfolioStock(models.Model):
    portfolio_id = models.ForeignKey("Portfolio")
    stock_id = models.ForeignKey("Stock")

Now a user can have several portfolios, and those portfolios will include several stocks. In order to get access to the corresponding stocks for a user, you will need to join the tables.

Answered By: BcK

What confused me is the name portfolio, which I would call position. Your initial code was correct, although I changed it a bit, removing AutoField which is probably not needed, using a OneToOneField to connect a Customer to a User, removing the s at the end of class names, which are templates, and therefore should be singular, nor plural, adding price to the Stock. And finally changing Portfolio, which should be the sum of all the Positions.

from django.conf import settings

class Customer(models.Model):
    customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
    in_cash = models.DecimalField(max_digits=15, decimal_places=2)

    def __str__(self):
        return self.customer.username

class Position(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    stock = models.ForeignKey('Stock', on_delete=models.CASCADE)
    number_of_shares = models.IntegerField()
    buy_datetime = models.DateTimeField(default=datetime.now, blank=True)

    def __str__(self):
        return self.customer.customer.username + ": " + str(self.number_of_shares) + " shares of " + self.stock.stock_symbol
    
class Stock(models.Model):
    stock_symbol = models.CharField(max_length=12)
    price = models.DecimalField(max_digits=10, decimal_places=2)

    def __str__(self):
        return self.stock_symbol

In my head I would have an entry in the "Portfolios" table for each
stock of a portfolio. So "Portfolios" would look like

portfolioid 1, userid: 1, stockid: 1, buydate: 12.01.2019, shares: 20
portfolioid 1, userid: 1, stockid: 2, buydate: 19.02.2020, shares: 41

So there is one portfolio, which contains two stocks. But overall that
doesn’t seem right. If used like in my example above I can’t have the
portfolioid as a primary key, how can I improve this?

You are correct, except that should be applied to a Position, each of which is unique, not the Portfolio, which is all the Positions the Customer has.

Answered By: raphael
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.