Django - Add online columns in "select" - django

I dont know if this is the best way to resolve my problem, if isn't , tell me plz :)
I have this model :
class userTrophy(models.Model):
user = models.ForeignKey(userInfo)
platinum = models.IntegerField()
gold = models.IntegerField()
silver = models.IntegerField()
bronze = models.IntegerField()
level = models.IntegerField()
perc_level = models.IntegerField()
date_update = models.DateField(default=datetime.now, blank=True)
Now i want to retrieve one user info, but i want add 3 new "columns" online :
total = platinum + gold + silver + bronze
point = platinum * 100 + gold * 50 + silver * 25 + bronze * 10
and sort by "point", after sort, put a new column, with a sequencial number: rank (1-n).
Can i do this ( or part of this ) working only with the model ?

I am sure there are many ways to achieve this behavior. The one I am thinking of right now is a Custom Model Manager and transient model fields.
Your class could look like so:
from django.db import models
from datetime import datetime
class UserTrophyManager(models.Manager):
def get_query_set(self):
query_set = super(UserTrophyManager, self).get_query_set()
for ut in query_set:
ut.total = ut.platinum + ut.gold + ut.silver + ut.bronze
ut.points = ut.platinum * 100 + ut.gold * 50 + ut.silver * 25 + ut.bronze * 10
return query_set
class UserTrophy(models.Model):
user = models.CharField(max_length=30)
platinum = models.IntegerField()
gold = models.IntegerField()
silver = models.IntegerField()
bronze = models.IntegerField()
level = models.IntegerField()
perc_level = models.IntegerField()
date_update = models.DateField(default=datetime.now, blank=True)
total = 0
point = 0
objects = UserTrophyManager()
class Meta:
ordering = ['points']
So you can use the following and get total and point calculated:
user_trophies = userTrophy.objects.all()
for user_trophy in user_trophies:
print user_trophy.total

Here's the way I would do it. Add the columns 'total' and 'points' to your model, like this:
class UserTrophy(models.Model):
...
total = models.IntegerField()
points = models.IntegerField()
...
Override the save method for your model:
def save(self, *args, **kwargs):
# Compute the total and points before saving
self.total = self.platinum + self.gold + self.silver + self.bronze
self.points = self.platinum * 100 + self.gold * 50 + \
self.silver * 25 + self.bronze * 10
# Now save the object by calling the super class
super(UserTrophy, self).save(*args, **kwargs)
With total and points as first class citizens on your model, your concept of "rank" becomes just a matter of ordering and slicing the UserTrophy objects.
top_ten = UserTrophy.objects.order_by('-points')[:10]
You'll also want to make sure you have your fields indexed, so your queries are efficient.
If you don't like the idea of putting these fields in your model, you might be able to use the extra feature of Django query set objects to compute your total and points on the fly. I don't use this very often, so maybe someone else can put together an example.
Also, I recommend for you to read PEP 8 for Python coding conventions.

This is more of a followup question than an answer, but is it possible to do something like:
class userTrophy(models.Model):
... stuff...
def points(self):
self.gold + self.silver + self.bronze
then call something like object.points in a template. Im just curious if that is a possibility

Related

How to calculate numbers of days between two dates and subtract weekends DJANGO MODELS

hope you're all fine!
I have a model called Vacation and I'm struggling with one field: days_requested, this field is the number days from vacation_start and vacation_end, it works and gives me an integer as result. The problem I'm facing now is that I need to subtract the weekends (or not count them).
What I have:
vacation_start = '2022-05-20'
vacation_end = '2022-05-24'
days_requested = 5
What I'm trying to have:
vacation_start = '2022-05-20'
vacation_end = '2022-05-24'
days_requested = 3
#21/05 and 22/05 are weekends
Vacation Models:
class Vacation(models.Model):
department = models.ForeignKey(
'departments.Department', on_delete=models.SET_NULL, null=True)
responsible = models.ForeignKey(
User, on_delete=models.SET_NULL, null=True, related_name='responsible_vacation')
status = models.CharField(
max_length=20, choices=STATUS_CHOICES_VACATIONS, default='open')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(
User, on_delete=models.SET_NULL, null=True, related_name='created_by_vacation')
vacation_start = models.DateField(blank=False)
vacation_end = models.DateField(blank=False)
days_requested = property(
lambda self: (self.vacation_end - self.vacation_start).days + 1
)
def __str__(self):
return str(self.created_by.first_name + ' ' + self.created_by.last_name)
I have tried:
days_requested = property(
lambda self: [(self.vacation_start + datetime.timedelta(days=i)).date()
for i in range(0, (self.vacation_end - self.vacation_start)) if (self.vacation_start + datetime.timedelta(days=i)).weekday() not in [5, 6].days()])
But I get the following error:
'datetime.timedelta' object cannot be interpreted as an integer
And as I perform operations with the amount of days asked I need to be able to get an integer.
Thank you all in advance.
UPDATE
class Model:
days_requested = models.IntegerField(blank=True,null=True)
def save(self, *args, **kwargs):
excluded = (6, 7)
days = 0
start_date =self.vacation_start
while start_date < self.vacation_end:
if start_date.isoweekday() not in excluded: #if you want to get only weekdays
days += 1
start_date+= timedelta(days=1)
self.days_requested=days
super(YourModel, self).save(*args, **kwargs)
After Elvin's answer I moved the hole logic to my view and set the logic inside form_valid function:
start = form.instance.vacation_start
end = form.instance.vacation_end
delta = end - start
excluded = (6, 7)
days = 0
for i in range(delta.days + 1):
day = start + datetime.timedelta(days=i)
if day.isoweekday() not in excluded:
days += 1
form.instance.days_requested = days
Thank you all.

How can I sum up and display a model method field and also display it on the dashboard without looping through the template in Django

How can I sum up and display a model method field and also display it on the dashboard without looping through the template in Django.
I aim to display two values on my dashboard. Total Invested and total Earned. I am able to display total Invested by using the code below.
Formula I used throughout is simple interest = (principal * duration * rate)/100
My view
investments = Investment.objects.all()
total_invested = Investment.objects.aggregate(
total_invested=Sum('amount_deposited '))
context = {
'investments': investments,
'total_invested': total_invested,
}
return render(request, 'list-investments.html', context)```
Then displaying it in my template ```{{total_invested.total_invested}}```
Note: I didnt use **for loop** in my template but I was able to get the total sum invested.
I tried repeating the same procedure to get the total amount earned but Its not working. Any idea how to go about it?
I also want to know how I can save these model methods values in the database.
Model Class
```class Investment(models.Model):
PLAN_CHOICES = (
("Basic - 4% - max 6", "Basic - 4% - max 6"),
("Premium - 5% - max 12", "Premium - 5% - max 12"),
)
plan = models.CharField(max_length=50, choices=PLAN_CHOICES, null=True)
duration = models.IntegerField(max_length=50, null=True)
start_date = models.DateField(null=True)
end_date = models.DateField(null=True)
active_investment = models.BooleanField(default=True)
amount_deposited = models.IntegerField(null=True)
def __str__(self):
return self.plan
def basic_profit(self):
self.basic_pro = (self.amount_deposited*self.duration*4)/100
return self.basic_pro
def premium_profit(self):
self.premium_pro = (self.amount_deposited*self.duration*8)/100
return self.premium_pro
def total_basic_net_profit(self):
self.total_basic_pro = (self.amount_deposited *
self.duration*4)/100 + self.amount_deposited
return self.total_basic_pro
def total_premium_profit(self):
self.total_premium_pro = (
self.amount_deposited *self.duration*8)/100 + self.amount_deposited
return self.total_premium_pro```

django - improve performance of __in queryset in M2M filtering

I have a models that has a M2M relationship to another model.
These are my models:
class Catalogue(models.Model):
city = models.CharField(db_index=True,max_length=100, null=True)
district = models.CharField(db_index=True,max_length=100, null=True)
type = models.ManyToManyField(Type, db_index=True)
datetime = models.CharField(db_index=True, max_length=100, null=True)
class Type(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
And this is views.py:
class all_ads(generic.ListView):
paginate_by = 12
template_name = 'new_list_view_grid-card.html'
def get_queryset(self):
city_district = self.request.GET.getlist('city_district')
usage = self.request.GET.get('usage')
status = self.request.GET.get('status')
last2week = datetime.datetime.now() - datetime.timedelta(days=14)
status = status.split(',')
if usage:
usage = usage.split(',')
else:
usage = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31']
intersections = list(set(status).intersection(usage))
type_q = (Q(type__in=intersections) & Q(type__isnull=False))
result = models.Catalogue.objects.filter(
Q(datetime__gte=last2week) &
type_q &
((reduce(operator.or_, (Q(city__contains=x) for x in city_district)) & Q(city__isnull=False)) |
(reduce(operator.or_, (Q(district__contains=x) for x in city_district)) & Q(district__isnull=False)))
).distinct().order_by('-datetime').prefetch_related('type')
return result
I want to filter MySQL db with some queries and return result in a listview.
It works good on a small database, but with large database it takes over 10 seconds to return results. If I delete type_q query, It takes 2 seconds (reduce 10 second!).
How can I improve performance of __in queryset?
It looks like type_q itself is not really the culprit, but acts as a multiplier, since now we make a LEFT OUTER JOIN, and thus the __contains runs over all combinations. This is thus more a peculiarity of two filters that work together
We can omit this with:
cat_ids = list(Catalogue.objects.filter(
Q(*[Q(city__contains=x) for x in city_district], _connector=Q.OR) |
Q(*[Q(district__contains=x) for x in city_district], _connector=Q.OR)
).values_list('pk', flat=True))
result = models.Catalogue.objects.filter(
Q(datetime__gte=last2week),
type_q,
pk__in=cat_ids
).distinct().order_by('-datetime').prefetch_related('type')
Some database (MySQL is known to not optimize a subquery very well), can even do that with a subquery with. So here we do not materialize the list, but let Django work with a subquery:
cat_ids = Catalogue.objects.filter(
Q(*[Q(city__contains=x) for x in city_district], _connector=Q.OR) |
Q(*[Q(district__contains=x) for x in city_district], _connector=Q.OR)
).values_list('pk', flat=True)
result = models.Catalogue.objects.filter(
Q(datetime__gte=last2week),
type_q,
pk__in=cat_ids
).distinct().order_by('-datetime').prefetch_related('type')

How do I use data stored in django model in a calculation and then store the result of a calculation in a django model field?

I'm working on my first Django app, and I need to take the data inputted by a user in my models fields, insert it into a function that makes a calculation using that data, and then returns the value of that calculation to my model where it is then stored.
It is not essential that the result be stored in my database, however I will need to use the resulting figure later on to allow the app to determine which data to present to the user.
I have my model class in models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.IntegerField(default=18)
gender = models.CharField(max_length=6, choices=gend, default='')
weight = models.IntegerField(default=0)
height = models.IntegerField(default=0)
and my function in a services.py file:
def caloriefunction():
weight = Profile.weight
height = Profile.height
age = Profile.age
isMale = Profile.gender
if isMale == "Male":
isMale = True
elif isMale == "Female":
isMale = False
else:
print("Error")
quit()
if isMale:
bmr = 66.5 + (13.75 * weight) + (5 * height) - (6.755 * age)
else:
bmr = 655.1 + (9.6 * weight) + (1.8 * height) - (4.7 * age)
bmr = round(bmr)
return bmr
How would I get the resulting value and then store it in my database or keep it to use in another piece of logic?
Would I be better off using the function in the class as a method?
Sorry if the question isn't being asked very well i'm quite a newbie.
Any help if appreciated!
Yes, you can add that as a method of Profile. And/or if you want to keep the result handy you could add another field to the profile model, and override the save method so it is recalculated whenever an instance is saved. Something like this:
def save(self, *args, **kwargs):
self.update_bmr()
super().save(*args, **kwargs)
def update_bmr(self):
if self.gender == "Male":
self.bmr = (
66.5
+ (13.75 * self.weight)
+ (5 * self.height)
- (6.755 * self.age)
)
elif self.gender == "Female":
self.bmr = (
655.1
+ (9.6 * self.weight)
+ (1.8 * self.height)
- (4.7 * self.age)
)
else:
self.bmr = None
You may need to guard against missing data.

Building up subqueries of derived django fields

I have a few transformations I need to perform on my table before I aggregate.
I need to multiply transaction_type (which is either 1 or -1) by amount to yield a signed_amount. Then I need to sum all signed_amounts by primary_category (which is a foreign key to secondary category which is a foreign key of my table).
DEBIT = -1
CREDIT = 1
TRANSACTION_TYPE_CHOICES = (
(DEBIT, 'debit'),
(CREDIT, 'credit'),
)
class Transaction(models.Model):
amount = models.DecimalField(max_digits=7, decimal_places=2)
transaction_type = models.IntegerField(choices=TRANSACTION_TYPE_CHOICES)
secondary_category = models.ForeignKey(Secondary_Category)
class Primary_Category(models.Model):
name = models.CharField("Category Name", max_length=30)
category = models.ForeignKey(Primary_Category_Bucket)
class Secondary_Category(models.Model):
name = models.CharField("Category Name", max_length=30)
primary_category = models.ForeignKey(Primary_Category)
I'm stuck on the first bit though.
from django.db.models import Sum, Count, F
original_transactions = Transaction.objects.all()
original_transactions.signed_amount = F('transaction_type') * F('amount')
for transaction in original_transactions:
print transaction.signed_amount
When I try to sanity check that signed_amount is being calculated, I get an error that 'Transaction' object has no attribute 'signed_amount'. I don't want to save signed_amount to the database. I just want to generate it as derived field so I can calculate my totals.
How do I calculate this derived field and subsequently aggregate by primary_category.name?
User python decorator property on a method for class Transaction:
class Transaction(models.Model):
amount = models.DecimalField(max_digits=7, decimal_places=2)
transaction_type = models.IntegerField(choices=TRANSACTION_TYPE_CHOICES)
secondary_category = models.ForeignKey(Secondary_Category)
#property
def signed_amount(self):
return self.amount * self.transaction_type
Then for each Transaction object you can do transaction.signed_amount.
I'm not sure if the aggregation part could be done using queries, but if you don't have that many PrimaryCategory, then python would be good enough to achieve it.
Or you can do this.
all_transactions = Transaction.objects.all().order_by('secondary_category__primary_category_id')
sum = 0
if all_transactions:
primary_category_id = all_transactions[0].secondary_category.primary_category_id
for transaction in all_transactions:
if primary_category_id == transaction.secondary_category.primary_category_id:
sum += (transaction.amount * transaction_type)
else:
sum = (transaction.amount * transaction_type)
print sum