Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
232 views
in Technique[技术] by (71.8m points)

django - Python OOP Design questions

I am currently building a Django application to track and analyse my stock porfolio. I currently have two [model] classes: Stock and Transaction. A stock holds information about a company and the transaction records all bought/sold shares from a stock.

In my Stock class I have a 'stock' attribute. This is simply a holding attribute. All methods for updating this are done via the Transaction model (e.g. when I sell or buy shares). My reasoning is that everytime I view the object page, or call it, I don't have to perform a calculation to determin the amount of shares a stock own; this happens when we are selling or buying.

The question I have is: is this a good way of writing software?

Personally, I think this is a pros and cons case. But it would be nice to hear others thoughts and experiance. Perhaps I should just move the methods to the Stock model? Many things to consider here...

Stock:

class Stock(models.Model):
"""
Stock model refrences an organisation on the stock market.
"""

# DESC: Name of the company.
name = models.CharField(
    unique=True, max_length=50, validators=[utils.alphabetical_chars_only]
)
# DESC: Uniquely identifies a stock globally.
isin = models.CharField(
    unique=True,
    verbose_name="ISIN",
    max_length=ISIN_LEN,
    validators=[MinLengthValidator(ISIN_LEN)],
)
# DESC: It's name in the market.
symbol = models.CharField(
    unique=True, max_length=12, validators=[utils.alphabetical_chars_only]
)
# DESC: The number of shares we own.
# NOTE:
# - We don't directly access with this field. All operations are done through
#   the transaction models.
shares = models.PositiveIntegerField(default=0)
# DESC: Store our profit or loss.
profits = models.FloatField(default=DEFAULT_FLOAT_VAL)
# DESC: Which exchange our stock belongs on.
exchange = models.ForeignKey("Exchange", on_delete=models.DO_NOTHING)
slug = models.SlugField(unique=True, max_length=12)

def __str__(self):
    return "%s: %s" % (self.symbol, self.name)

def get_absolute_url(self):
    return reverse("stocks:stock_view", kwargs={"slug": self.slug})

def total_amount_recieved_from_dividends(self):
    dividends = Dividend.objects.filter(stock__id=self.id).values_list('amount', flat=True)
    # If we have recieved none then return 0
    if not dividends:
        return 0 

    return sum(dividends)

Transaction:

class Transaction(models.Model):
"""
Transaction model refrences all recorded share transactions.
"""

BUY = "Buy"
SELL = "Sell"
TRANSACTION_TYPE_CHOICES = [(BUY, BUY), (SELL, SELL)]
objects = TransactionQuerySet.as_manager()
# DESC: What shares we buying from a stock we own.
stock = models.ForeignKey("Stock", on_delete=models.CASCADE)
# DESC: What type of transaction is this? (buy or sell)
tt = models.CharField(
    max_length=4,
    choices=TRANSACTION_TYPE_CHOICES,
    default=BUY,
    verbose_name="Transaction type",
)
# DESC: How many shares we have bought.
quantity = models.PositiveIntegerField(default=0)
# DESC: Price per share.
pps = models.FloatField(
    default=0.0,
    validators=[MinValueValidator(DEFAULT_FLOAT_VAL)],
    verbose_name="Price per share",
)
# DESC: Cost of commission.
commission = models.FloatField(
    default=DEFAULT_FLOAT_VAL, validators=[MinValueValidator(DEFAULT_FLOAT_VAL)]
)
# DESC: Stamp duty
sd = models.FloatField(
    default=DEFAULT_FLOAT_VAL,
    validators=[MinValueValidator(DEFAULT_FLOAT_VAL)],
    verbose_name="Stamp duty",
)
# DESC: Datetime.
dt = CustomDateTimeField(verbose_name="Datetime")
# DESC: Unique ID of the order.
order_id = models.CharField(
    unique=True,
    max_length=ORDER_ID_LEN,
    validators=[MinLengthValidator(ORDER_ID_LEN)],
)

def __str__(self):
    return "%s: %i - %s" % (self.stock, self.quantity, self.dt)

def get_absolute_url(self):
    return reverse("stocks:stock_view", kwargs={"slug": self.stock.slug})

def check_if_share_quantity_is_zero(self):
    # DESC: Prevent the user from selling nothing.
    if self.quantity is 0:
        raise ValidationError("This value cannot be 0.")

def add_or_sell_shares(self):
    if self.tt == Transaction.BUY:
        self.stock.shares += self.quantity
    else:
        if self.stock.shares - self.quantity < 0:
            raise ValidationError(
                "You are trying to sell more shares than you own!"
            )
        self.stock.shares -= self.quantity

    self.stock.save()

def clean(self):
    self.check_if_share_quantity_is_zero()
    self.add_or_sell_shares()

def delete(self, *args, **kwargs):
    if self.tt == Transaction.BUY:
        self.stock.shares -= self.quantity
    else:
        if self.stock.shares - self.quantity < 0:
            raise ValidationError(
                "You are trying to sell more shares than you own!"
            )
        self.stock.shares += self.quantity

    self.stock.save()
    super(Transaction, self).delete(*args, **kwargs)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
等待大神答复

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...