Django - default field value depends on other field value - python

I have a problem to set a default field value. What I want to do?I want that price in class Packages be a default value of priceNoTax in class Bill. As you can see, all three classes are
logically connected.Example: Account 1 has a package with id 1. Price of this package is 100. Default value of priceNoTax for Account 1 is 100.How to do that? I am relative new at this, so I need help.
models.py
class Packages(models.Model):
#other fields
price = models.IntegerField(validators=[MinValueValidator(1)], verbose_name="Price of package")
class Account(models.Model):
startDate = models.DateField(verbose_name="Start date")
finishDate = models.DateField(verbose_name="Finish date")
idPackage = models.ForeignKey(Packages, on_delete=models.CASCADE, verbose_name="Package")
class Bill(models.Model):
date = models.DateField(default=datetime.now())
tax = models.FloatField(default=0.20)
priceNoTax = models.IntegerField()
priceTax = models.FloatField(default=priceNoTax+(priceNoTax*tax))
idAccount = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="Account")
def __str__(self):
return self.date
Thanks a lot!!!

perhaps add this to your Bill class?
def save(self, *args, **kwargs):
if self.priceNoTax is None:
self.priceNoTax = self.idAccount.idPackage.price
super(Bill, self).save(*args, **kwargs)

Why do you need it to be a field? Do you see a reason where someone would want to change the total price without changing the price and tax to the corresponding values? If it doesn't really need to be a field, you can just make it a method.
class Bill(models.Model):
date = models.DateField(default=datetime.now())
tax = models.FloatField(default=0.20)
priceNoTax = models.IntegerField()
idAccount = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="Account")
def priceTax(self):
return self.priceNoTax + (self.priceNoTax*self.tax)
def __str__(self):
return self.date
You can still use it the same way in templates with {{ bill.priceTax }}. In code you would need to use bill.priceTax().
This way, the price with tax should stay up-to-date no matter how the tax or price without tax changes.
You can also use #property decorator to avoid having to call it as a function in code.
#property
def priceTax(self):
return self.priceNoTax + (self.priceNoTax*self.tax)
For more see https://docs.djangoproject.com/en/2.0/topics/db/models/#model-methods

#kichik answered how to display the default value using template tag, and I recommend to implement auto-calculation by javascript.
Of course, you will have to validate user input or implement save() method as #Chris Curvey said.

Related

Django Model Occurrence Count

I'm fairly new to Django and I'm in need of assistance with my models.
class Region(models.Model):
region_name = models.CharField(max_length=10)
def __str__(self):
return self.region_name
class Property(models.Model):
prop_name = models.CharField(max_length=200)
region_name = models.ForeignKey(Region, on_delete=models.CASCADE, verbose_name="Region")
prop_code = models.IntegerField(default=0, verbose_name="Property")
def __str__(self):
return self.prop_name
class Sale(models.Model):
prop_name = models.ForeignKey(Property, on_delete=models.CASCADE)
employee = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Person")
prop_state = models.CharField(null=True, max_length=5, choices=[('new','New'),('used','Used')])
date = models.DateField('Sale Date')
def __str__(self):
return '%s : %s %s - %s' % (self.prop_name.prop_name, self.employee, self.date, self.prop_state)
Here are my models. Property inherits from Region and Sale inherits from property. What I want to do is count the number of sales in a region and the number of sales on a specific property. However I do not know which would be the best way to approach this. I've tried using a lambda as a model field that uses the count() function but I wasn't able to see much success with that. Please let me know if you have any suggestions.
If you already have your Property/Region objects, something like this should work
sales_per_property = Sale.objects.filter(prop_name=property).count()
sales_per_region = Sale.objects.filter(prop_name__region_name=region).count()
Edit:
Seeing that you tried to add a lambda function to the model field, this may be more what you are looking for.
class Region(models.Model):
...
#property
def sales(self):
return Sale.objects.filter(prop_name__region_name=self).count()
and similarly for Property. Simply access the property using region.sales
You can annotate your querysets for Region and Property. For example:
from django.db.models import Count
regions = Region.objects.annotate(sales=Count('property__sale'))
properties = Property.objects.annotate(sales=Count('sale'))
The Regions/Propertys that arise from these querysets will have an extra attribute .sales that contains the number of related Sale objects.

Refer related model field in model layer

I am try to refer 'spot_price' of model 'Spot' in model 'Basis' in django model layer, How can I manage this?
I have designed view.py to automaticaly render the templates. so I am not able to modifty any view.py to choose data like 'models.B.objects.get().field'.
and more, str is set to indicate the date, so, in the django backstage admin, the 'spot' field display would be 'date' formate, how would be change to 'spot_price'?
model Spot
class Spot(models.Model):
date = models.DateField(primary_key=True)
spot_price = models.FloatField(blank=True, null=True)
def __str__(self):
return str(self.date) if self.date else ''
need to refer the model Spot'spot_price by date, cause date is unique but spot_price is not
class Basis(models.Model):
date = models.DateField(primary_key=True)
major_future_contract_close_price = models.FloatField(blank=True)
spot = models.OneToOneField(Spot, on_delete=models.CASCADE)
basis = models.FloatField(default=calculate_basis)
def __str__(self):
return str(self.date) if self.date else ''
def calculate_basis(self):
return abs(self.major_future_contract_close_price -
self.spot.spot_price)
I expect the Basis.query.data would to like 'date: 2019-04-25, major_future_contract_close_price: 100.0, spot: 96.5, basis: 3.5'
You can't use class method as default, because it requires self, which is not existing when you are still creating the object.
If you need to have it stored in field (database), override default save() method or use signals to modify the basis field once your object is created. Also note that you have to recalculate basis every time close_price or spot_price changes, as the value is just written in database.
Probably better solution would be to use #property so for anyone that will be using you model it will look like a field, but it will dynamically calculate value based on current data.
For example, I'm overriding save() to calculate the basis field. I set it as editable=False, which means it won't appear in forms by default (and you will not be able to see it in admin!). You can safely remove that part if you want to just look at the values.
Additionally I add basis_as_property property.
class Basis(models.Model):
date = models.DateField(primary_key=True)
major_future_contract_close_price = models.FloatField(blank=True)
spot = models.OneToOneField(Spot, on_delete=models.CASCADE)
basis = models.FloatField(editable=False, blank=True)
#property
def basis_as_property(self):
return '%s' % (self.calculate_basis())
def __str__(self):
return str(self.date) if self.date else ''
def save(self, *args, **kwargs):
if not self.basis:
self.basis = self.calculate_basis()
super(Basis, self).save(*args, **kwargs)
def calculate_basis(self):
return abs(self.major_future_contract_close_price - self.spot.spot_price)
As for Spot str repr, I don't think it's possible to change it based on where it is referenced. If you want to use spot_price, you can just use: return str(self.spot_price) if self.spot_price else str(self.date)

create value to a model from another model's method

I'm trying to create a new table (Payslip model) that will contain the computed salary on an employee in a cutoff.
I want Payslip.salary to get value from Employee.compute_pay()
Given the example in the url link above, what should my views.py look like?
Is this the best approach to this kind of process? or Is there any library that can help with what I want to do here?
https://imgur.com/a/wVG5qrd
model.py
class Employee(models.Model):
name = models.CharField(max_length=20)
rate = models.IntegerField()
absent = models.IntegerField()
def __str__(self):
return self.name
def compute_pay(self):
daily = rate / 20
return rate - (daily*absent)
class Payslip(models.Model):
name = models.CharField(max_length=20)
salary = models.IntegerField()
def __str__(self):
return self.name
views.py
def compute(request):
if request.method == "POST":
return render(request,'payroll/compute/compute.html')
I do not think there is a need for another model Payslip, also you have no ForeignKey connections between the two models for it to work.
Considering your requirement, property decorator should work. Read up on how #property works. Basically, it acts as a pseudo model field, keep in mind the value of this field is not stored anywhere in the database, but it is tailor-made for situations like this where the field changes based on other fields and is needed for read-only.
Try this
class Employee(models.Model):
name = models.CharField(max_length=20)
rate = models.IntegerField()
absent = models.IntegerField()
def __str__(self):
return self.name
#property
def compute_pay(self):
daily = self.rate / 20
return (self.rate - (daily*self.absent))
you can get the employee's salary by Employee.compute_pay just like any other model field

How to manipulate value of one Model Field from another Model?

I have two models
class Employee(models.Model):
name = models.CharField(max_length=20)
ID = models.IntegerField()
basic_salary = models.IntegerField()
total_leave = models.IntegerField(default = 14)
paid_leave = models.IntegerField(default = 0)
unpaid_leave = models.IntegerField(default = 0)
def __str__(self):
return self.name
class Leave_management(models.Model):
name = models.OnetoOneField(Employee,on_delete= models.CASCADE)
reason = models.CharField(max_length=50)
from = models.DateTimeField()
to = models.DateTimeField()
total_days = models.IntegerField()
def __str__(self):
return self.name
So,i want to minus 'total_days' of 'model-Leave_management' from 'total_leave' field of 'model-Employee'. And as per leaves taken i want to update 'paid_leave' and 'unpaid_leave' sections.
I can perform so if these two models would be one model(example below), But i dont know how to perform so in different models.
def save(self,*args,**kwargs):
if self.total_days<=self.total_leave:
self.total_leave -= self.total_days
self.unpaid_leave = 14 - self.total_leave
else:
self.total_days -= 14
self.paid_leaves = self.total_days
super(Model_name,self).save(*args,**kwargs)
`
Please be guiding me.
In fact your OneToOneField(..) to an Employee is not a name. At the database level it will store values that correspond to primary keys of an Employee, and in Django, name will be a lazy fetch to the corresponding Employee. Therefore I suggest to rename your function to (for example) employee.
Another problem is that you define it as a OneToOneField. That means that an Employee has one Leave_management. But based on the fields (reason, from, to, etc.), it looks like an Employee can have zero, one, or more Leave_managements. So that means it is a ForeignKey.
So our model looks like:
class Leave_management(models.Model):
employee = models.ForeignKey(Employee,on_delete= models.CASCADE)
reason = models.CharField(max_length=50)
from = models.DateTimeField()
to = models.DateTimeField()
total_days = models.IntegerField()
def __str__(self):
return self.employee.name
Like the __str__ function already suggests, we can obtain the name of the employee by querying self.employee, and we can then fetch its .name attribute.
But now the challenge is what to do when save a Leave_management object. In that case the number of total_leave and paid_leave should be updated.
We first have to figure out the total number of total_days that are stored in Leave_management objects related to an Employee, this is equal to:
(Leave_management.objects.filter(employee=some_employee)
.aggregate(totals=Sum('total_days'))['totals'] or 0
So we can then subtract this from 14, and store the (possibly) remaining days in paid_leave, like:
class Leave_management(models.Model):
# ...
def save(self, *args, **kwargs):
super(Leave_management, self).save(*args, **kwargs)
totals = (Leave_management.objects
.filter(employee=some_employee)
.aggregate(totals=Sum('total_days'))['totals'] or 0
employee = self.employee
unpaid = min(14, totals)
employee.total_leave = 14 - unpaid
employee.unpaid_leave = unpaid
employee.paid_leave = totals - unpaid
employee.save()
Note: typically we do not handle this by overriding the .save(..) function, but by using Django signals: triggers that can be implemented when certain objects are saved, etc. This especially should be used since the objects can be changed circumventing the .save(..) function, and sometimes such objects might get deleted as well. So the above is not a good design decision.
Even when we use signals, it is a good idea to frequently (for example once a day) recalculate the total leave, and update the corresponding Employee models.

What the right way to localize the price in the Django-shop?

I know easy way, make a few different fields for needed currencies, but that's not only ugly, but the currencies will be hardcoded. It seems to me be more elegant through django-parler, but I do not quite understand how to do it.
I think this is the right way:
class CurrencyModel(TranslatableModel):
translations = TranslatedFields(
title = models.CharField(_("Title"), max_length=120),
)
code = models.CharField(_('ISO 4217 code'), max_lenght=3)
def __str__(self):
return self.title
class ItemModel(BaseProduct, TranslatableModel):
slug = models.SlugField(_("Slug"), unique=True)
translations = TranslatedFields(
product_name = models.CharField(_("Item Name"), max_length=256),
item_price = models.FloatField(_("Item price")),
currency = models.ForeignKey(CurrencyModel, verbose_name=_("Currency ")),
)
def get_price(self, request):
money = MoneyMaker(self.currency.code)
return money(self.item_price)
The simplest way to localize prices in django-SHOP, is to use the MoneyInXXX class. This class can be generated for each currency using the MoneyMaker factory.
Whenever an amount of a Money class is formatted, it is localized properly.

Categories