Как смоделировать базу данных типа "многие ко многим" с 3 таблицами

Я работаю над бэкендом django и пытаюсь смоделировать базу данных и хочу сделать это лучшим практическим способом. Мне нужна таблица "User", таблица "Portfolios" и таблица "Stocks". Пользователь может иметь несколько портфелей, которые состоят из нескольких акций.

Вот мой код на данный момент:

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)

В моей голове у меня была бы запись в таблице "Портфели" для каждой акции портфеля. Таким образом, "Портфели" выглядели бы как

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

Итак, есть один портфель, который содержит две акции. Но в целом это кажется неправильным. Если, как в моем примере выше, я не могу использовать portfolioid в качестве первичного ключа, как я могу это улучшить?

Спасибо за ваше время

Как и в обычных случаях "многие ко многим", вам потребуется создать intermediary table, который также называется junction table/association table. Ассоциативная сущность

У ваших пользователей будет несколько портфолио:

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

В портфелях будет несколько акций:

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

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

Краткий ответ

Вы можете сделать это, просто используя ForeignKey's. Как показано ниже, в каждом Portfolio будет МНОГО User и МНОГО Stock в каждом Portfolio.

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

class Portfolios(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    buy_datetime = models.DateTimeField(default=datetime.now, blank=True)
    number_of_shares = models.IntegerField()

class Stocks(models.Model):
    stock_symbol = models.CharField(max_length=12)
    portfolio = models.ForeignKey(Portfolios, on_delete=models.CASCADE)

Экстра

Не совсем понятно, зачем вы используете AutoField, поскольку Django сделает это автоматически. Я бы создал другой класс, скажем, Customer, и установил OneToOneField, расширив Django's User Model. Наконец, в английском языке s в конце обычно означает множественное число, многие. Поскольку класс - это шаблон, было бы менее запутанно писать их без s:

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)

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

class Stock(models.Model):
    stock_symbol = models.CharField(max_length=12)
    portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE)

Что меня смутило, так это название portfolio, которое я бы назвал position. Ваш первоначальный код был правильным, хотя я немного изменил его, удалив AutoField, который, вероятно, не нужен, используя OneToOneField для соединения Customer с User, удалив s в конце имен классов, которые являются шаблонами, и поэтому должны быть в единственном или множественном числе, добавив price к Stock. И, наконец, изменение Portfolio, которое должно быть суммой всех 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

В моей голове у меня была бы запись в таблице "Портфели" для каждой акции портфеля. Таким образом, "Портфели" выглядели бы как

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

Итак, есть один портфель, который содержит две акции. Но в целом это не кажется правильным. Если использовать, как в моем примере выше, я не могу иметь portfolioid в качестве первичного ключа, как я могу это улучшить?

Вы правы, только это следует применять к позиции, каждая из которых уникальна, а не к Portfolio, которая является всеми теми Position, которые у Customer есть.

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