Как смоделировать базу данных типа "многие ко многим" с 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
, которое должно быть суммой всех Position
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)
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
есть.