updating account balance with django - django

absolute n00b here, just fiddling around. Trying to make a very simple app to track my personal expenses. I have a class for entering the expenses, a class for the categories and a class for my account balance. Plan is to create en entry in the account balance everytime I create an expense.
How do I update my account balance? I'll have to get fields from the latest entry in expenses to do the math with in my balance class, right?
This is what I have. Any help appreciated.
from django.db import models
from django.utils import timezone
class Category(models.Model):
category = models.CharField(max_length=200,blank=False)
def __str__(self):
return self.category
class Balance(models.Model):
date = models.DateTimeField(default=timezone.now)
previous_balance = ????
transaction = ????
current_balance = previous_balance - transaction
class Expense(models.Model):
date = models.DateTimeField(default=timezone.now)
spender = models.ForeignKey('auth.User')
description = models.CharField(max_length=200)
category = models.ForeignKey(Category,default=1)
ABN = 'ABN'
ING = 'ING'
ACCOUNT_CHOICES = (
(ABN, 'ABN'),
(ING, 'ING'),
)
account = models.CharField(
max_length=30,
choices=ACCOUNT_CHOICES,
default=ABN,
)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def commit(self):
self.commit_date = timezone.now()
self.save()
def __str__(self):
return u"%s. Kosten: %s" % (self.description, self.amount)

If I'm understanding your question correctly, you want to be able to get your current balance after creating Expenses. If so, you can use Django's aggregation:
from django.db.models import Sum
class Balance(models.Model):
date = models.DateTimeField(default=timezone.now)
# Keep the amount you start with
starting_balance = models.IntegerField()
# Get the Sum of all expenses and do some simple subtraction
def get_current_balance(self):
total_expenses = Expense.objects.all().aggregate(Sum('amount'))
return self.starting_balance - total_expenses['amount__sum']
Then in your views, you can do something like:
current_balance = some_balance_instance.get_current_balance()

considered balance change will be trigger by expense record change, you can overwrite save on Expense model. then balance table can be maintain in auto.
import datetime
class Expense(models.Model):
date = models.DateTimeField(default=timezone.now)
spender = models.ForeignKey('auth.User')
description = models.CharField(max_length=200)
category = models.ForeignKey(Category,default=1)
ABN = 'ABN'
ING = 'ING'
ACCOUNT_CHOICES = (
(ABN, 'ABN'),
(ING, 'ING'),
)
account = models.CharField(
max_length=30,
choices=ACCOUNT_CHOICES,
default=ABN,
)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def save(self, *args, **kwargs):
super(Expense, self).save(*args, **kwargs)
last_bal = Balance.objects.order_by('id').last()
Balance.objects.create(date=datetime.datetime.now(), previouse_balance=last_bal.current_balance,
transaction=self, current_balance=last_bal.current_balance + self.amount)

Related

Import-Export with "multilevel" models

I am figuring how can I manage this situation with django-import-export using the same excel and differents models with differents djangoapps. I have the following models:
# employees/models.py
class Employee(models.Model):
name = models.CharField(max_length=100)
job = models.ForeignKey(Job, on_delete=models.SET_NULL, null=True, blank=True)
# jobs/models.py
class Job(models.Model):
value = models.CharField(max_length=100)
department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True, blank=True)
place = models.ForeignKey(Place, on_delete=models.SET_NULL, null=True, blank=True)
class Department(models.Model):
value = models.CharField(max_length=100)
business = models.ForeignKey(Business, on_delete=models.SET_NULL, null=True, blank=True)
class Place(models.Model):
value = models.CharField(max_length=100)
business = models.ForeignKey(Business, on_delete=models.SET_NULL, null=True, blank=True)
class Business(models.Model):
name = models.CharField(max_length=100)
On excel I have following values:
xls_employee_name, xls_employee_job, xls_business_name
Jon Doe, Web Developer, Company 1
I know how to import employee name and his job because Job is directly related by ForeignKey. But how can I import business name?
Below is the code for employee name and his job:
# employees/resource.py
class EmpleadoResource(resources.ModelResource):
name = fields.Field(attribute='nombre', column_name='Nombre')
job = fields.Field(
column_name='xls_employee_job',
attribute='job',
widget=ForeignKeyWidget(
Job,
field='value'
))
class Meta:
model = Employee
fields = ('name','job',)
skip_unchanged = True
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
Is it possible to import business name using one excel? I appreciate if someone could guide me. I'm also pretty new with django.
thank you for all your answers. I finally manage it and this is the solution(it was a little trickier for me, but very simple. I tried to translate all spanish names to english, sorry if I misslooked some):
# employees/resource.py
class EmployeeResource(resources.ModelResource):
name = fields.Field(attribute='name', column_name='Name')
job = fields.Field(
column_name='xls_employee_job',
attribute='job',
widget=ForeignKeyWidget(
Job,
field='value'
))
place = fields.Field(attribute='place', column_name='xls_place_column')
department = fields.Field(attribute='department', column_name='xls_department_column')
business = fields.Field(attribute='business', column_name='xls_business_name')
class Meta:
model = Employee
fields = ('name','job','place','department','business')
skip_unchanged = True
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
self.place = row["xls_place_column"]
self.department = row["xls_department_column"]
self.business = row["xls_business_name"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
Centro.objects.get_or_create(name=self.centro)
Departamento.objects.get_or_create(name=self.departamento)
Empresa.objects.get_or_create(nombre=self.empresa)
I was stucked using widgets for business, department and place, but it was not necessary
Option 1
You can assign the column value for each row to a temporary attribute on the model (it doesn't matter that this attribute is not in the Employee model):
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
# assign row value to a temporary attr
self.business = row["xls_business_name"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
# create instance using temp attr value
Business.objects.get_or_create(name=self.business)
Option 2
You can create all Business instances by processing the dataset in a batch before processing the Employee resources:
def before_import(self, dataset, using_transactions, dry_run, **kwargs):
for row in dataset.dict:
business = row["xls_business_name"]
Business.objects.create(name=business)
Note that you could use bulk_create() here to make this process more efficient.
Add transaction protection as you see fit, and bear in mind that the Business and Job entities will be created even if your import fails.

How to use foreign key field's attribute for another model field

I have two models in different apps like so:
class Account(models.Model):
"""
Class to store fiat account information of a companies bank account
"""
number = models.CharField(max_length=100)
currency = models.ForeignKey(FiatCurrency, on_delete=models.CASCADE)
owner = models.ForeignKey(Company, on_delete=models.CASCADE)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.number
class FiatTransaction(models.Model):
"""
Class to store Transactions made between escrow and operative white-listed fiat accounts
"""
debit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='debit_account')
credit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='credit_account')
executed_on = models.DateTimeField(auto_now_add=True)
amount = models.FloatField()
currency = debit_account.currency
is_processed = models.BooleanField(default=False)
fee = models.FloatField()
memo = models.CharField(max_length=250)
def __str__(self):
return F"Transferred {self.amount} from {self.debit_account} to {self.credit_account} at {self.executed_on}"
Now the field currency of model FiatTransaction doesn't seem to work the way I intend it to do. It raises
AttributeError: 'ForeignKey' object has no attribute 'currency'
# Source model
class FiatCurrency(models.Model):
"""
A model to store Fiat Currencies offered by Finchin to
include into cash-pools.
"""
ISO_Code = models.CharField(max_length=3)
name = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
Why's that and how to make this work?
You can make a #property that will determine the currency of that object with:
class FiatTransaction(models.Model):
debit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='debit_account')
credit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='credit_account')
executed_on = models.DateTimeField(auto_now_add=True)
amount = models.FloatField()
is_processed = models.BooleanField(default=False)
fee = models.FloatField()
memo = models.CharField(max_length=250)
#property
def currency(self):
return self.debit_account.currency
This can however be inefficient if you have to do this for a lot of FiatTransactions.
In that case it might be better to remove the currency property, and annotate the QuerySet with:
from django.db.models import F
FiatTransaction.objects.annotate(currency=F('debit_account__currency'))
The FiatTransactions that arise from this will have an extra attribute named .currency that will contain the .currency of the .debit_account.
If you need this often, you can make use of a Manager that will automatically annotate when you access FiatTransaction.objects:
from django.db.models import F
class FiatTransactionManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).annotate(
currency=F('debit_account__currency')
)
class FiatTransaction(models.Model):
# …
objects = FiatTransactionManager()

how to fetch data from many to many field objects?

I am getting stuck in many to many field objects in my model i want to retrieve the price of product from the order model objects but while fetching it gives sometime many related manager error or query set error
models.py
from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
slug = models.SlugField(max_length=100, db_index=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
stock = models.PositiveIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Order(models.Model):
user = models.ForeignKey(User,on_delete= models.SET_NULL,null = True)
product = models.ManyToManyField('Order.Product')
is_ordered = models.BooleanField(default =False)
date_ordered = models.DateTimeField(auto_now = True,null = True)
views.py
def get_product_price(request):
if request.method=="GET":
user=User.objects.get(username = "hemant")
orders = user.order_set.all()
order = orders[0]
price = order.product.price
return HttpResponse(price)
When you are getting value from ManyToManyField, you need to do it like this:
products = order.product.all()
If you want to get the prices, you can do it like this:
products = order.product.all()
for product in products:
print(product.price)
If you want to return prices as a http response, then you can use values_list() to get list of prices from queryset. Like this
import json
views.py
def get_product_price(request):
if request.method=="GET":
user=User.objects.get(username = "hemant")
orders = user.order_set.all()
order = orders[0]
price = list(order.product.all().values_list('price', flat=True))
return HttpResponse(json.dumps(price))

Filtering Django models by user & object

I'm learning Django with a dummy example but having difficulty in understanding how to correctly filter my Django models by an authorised user in my views.
In my view I want to list the transactions associated with a users portfolio. The code below runs but when trying to access the result of 't' I get the error:
'ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing.'
Any help would be much appreciated, thanks.
if request.user.is_authenticated:
# Get model data
pf = Portfolio.objects.filter(user=request.user)
t = Transaction.objects.filter(pf=pf)
My model is as below:
from django.db import models
from django.contrib.auth.models import User
class Portfolio(models.Model):
# Portfolio has one user associated with it
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100, default='-')
def __str__(self):
return self.name
class Transaction(models.Model):
# Transaction has one equity associated with it
equity = models.ForeignKey('Equity', on_delete=models.CASCADE, null=True)
# Transaction has one portfolio associated with it
pf = models.ForeignKey('Portfolio', on_delete=models.CASCADE)
BUY = 'BUY'
SELL = 'SELL'
BUY_OR_SELL = (
(BUY, 'BUY'),
(SELL, 'SELL'),
)
action = models.CharField(choices=BUY_OR_SELL, default=BUY, max_length=5)
num = models.FloatField(default=1)
price = models.FloatField(default=0)
date = models.DateField('date')
fee = models.FloatField(default=0)
def __str__(self):
return f'{self.equity}, {self.num}x{self.price}, {self.date:%d %b %Y}'
class Equity(models.Model):
class Meta:
verbose_name_plural = "Equities"
CUR_EUR = 'EUR'
CUR_GBP = 'GBP'
CUR_USD = 'USD'
CURRENCY_CHOICES = (
(CUR_EUR, 'EUR'),
(CUR_GBP, 'GBP'),
(CUR_USD, 'USD'),
)
symbol = models.CharField(max_length=20, default='-')
exchange = models.CharField(max_length=100, default='-')
currency = models.CharField(max_length=15, choices=CURRENCY_CHOICES, default=CUR_USD)
def __str__(self):
return self.symbol
Many thanks!
pf is here a collection of Portfolio objects, so you can query it with the __in lookup [Django-doc]:
Transaction.objects.filter(pf__in=pf)
Or if you are not interested in the Porfolio objects itself, you can make a query like:
Transaction.objects.filter(pf__user=request.user)
The query below will result in a query like:
SELECT transaction.*
FROM transaction
JOIN portfolio ON transaction.pf_id = portfolio.id
WHERE porfolio.user_id = 123
(with 123 the id of the request.user)

What model object type do I use to store a value of time in Django Admin?

I'm trying to set up a product that corresponds to a length of time (a newspaper subscription).
I'm having the clients use the Django Admin to add product types (various subscription lengths) but I can't find anything out there on what model object type I should use to store a value of time (like 52 weeks, 26 weeks, 1 week, etc.). I would want to be able to choose a length as either days or weeks, because some papers are weekly, others daily.
Right now my Product Model is:
class Product(models.Model):
product_type = models.CharField(max_length=100)
product_description = models.CharField(max_length=255)
product_cost = models.DecimalField(decimal_places=2, max_digits=4)
product_active = models.BooleanField()
def get_absolute_url(self):
return "/signup/%i/" % self.id
def __unicode__(self):
return self.product_type
Is there any way to make the product_type an object type that would let a user define a value of time?
Thanks,
Anthony
I think it'd be easier to just store two values, one numeric value and a choice for what the number represents.
duration = models.IntegerField()
duration_type = models.CharField(max_length=32, choices=[
("day", "Days"),
("week", "Weeks")])
Assuming you have a separate subscription model, you could then calculate the expiration in that model as a method:
class Subscription(models.Model):
product = models.ForeignKey(Product)
starts = models.DateField()
def expires(self):
from datetime import timedelta
if self.product.duration_type == "day":
days = self.product.duration
elif self.product.duration_type == "week":
days = self.product.duration * 7
return self.starts + timedelta(days=days)
You could add some DateTime model fields describing the start and end of a subscription. you can create a method of your model that returns a timedelta by subtracting one datetime from the other
class Product(models.Model):
product_type = models.CharField(max_length=100)
product_description = models.CharField(max_length=255)
product_cost = models.DecimalField(decimal_places=2, max_digits=4)
product_active = models.BooleanField()
subscription_start = models.DateTimeField()
subscription_end = models.DateTimeField()
def get_duration(self):
# returns a timedelta
return self.subscription_end - self.subscription_start
def get_absolute_url(self):
return "/signup/%i/" % self.id
def __unicode__(self):
return self.product_type
eg:
>> import datetime
>> d1 = datetime.datetime(1986,14,05,0,0,0)
>> d2 = datetime.datetime.now()
>> print d2 - d1
9471 days, 17:24:31
you can use that timedelta to do calculations in your views