How to loop through object fields in django - python

I want to loop through the object fields to update their values but im not quite sure how to do this.
class FinancePending(models.Model):
invoiceNumber = models.IntegerField
amountPaid = models.CharField(max_length=20)
AmountPending = models.IntegerField( blank=True, null=True)
TotalAmount = models.CharField(max_length=50, default=0)
Now i want to calculate amountpending in a function. But its not working
amount_paid = FinancePending.objects.values_list('amountPaid', flat=True)
amount_paid = list(amount_paid)
total_amount = FinancePending.objects.values_list('TotalAmount', flat=True)
total_amount = list(total_amount)
# total - paid
TotalFee = [float(s.replace(',', '')) for s in total_amount]
AmountPaid = [float(s.replace(',', '')) for s in amount_paid]
finance_pending = FinancePending.objects.all()
i = 0
while i < len(TotalFee):
amount_pending = TotalFee[i] - AmountPaid[i]
FinancePending.objects.filter(invoiceNumber=i).values(AmountPending=amount_pending)
setattr(finance_pending, 'AmountPending', str(amount_pending))
i = 1 + i

Would suggest using Query Expression, more powerful and robust

Related

Refactor Business Logic From View To Model

I've got a django view with a small amount of business logic in it. However it would be useful to use this information elsewhere so it feels like something that I could put into a model however I'm not entirely sure how to go about doing this. Mainly because I need specific user data. This is what I have so far:
views.py
def create_budget(self, context):
starting_point = BudgetStartingPoint.objects.filter(owner=self.request.user)
running_tally = starting_point[0].starting_amount
budget_dict = {}
for transaction in self.get_queryset():
if transaction.income_type == "IN":
running_tally += transaction.transaction_amount
else:
running_tally -= transaction.transaction_amount
budget_dict[transaction] = [running_tally, transaction.income_type]
context['budget'] = budget_dict
models.py
class BudgetTransaction(models.Model):
"""
Individual transaction for Budget
"""
transaction_types = [
('fixed', 'Fixed'),
('extra', 'Extra'),
]
income_types = [
("IN", "Income"),
("OUT", "Expense"),
]
frequencies = [
('weeks', 'Weekly'),
('fort', 'Fortnightly'),
('4week', 'Four Weeks'),
('months', 'Monthly'),
('years', 'Yearly'),
]
today = datetime.today().date()
id = HashidAutoField(
primary_key=True, salt=f"transaction{settings.HASHID_FIELD_SALT}"
)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
help_text="Owner of the item"
)
category = models.ForeignKey(BudgetCategory, on_delete=models.CASCADE, default=1, null=True)
transaction_type = models.CharField(max_length=40, choices=transaction_types, default=1)
transaction_name = models.CharField(max_length=100, null=False)
transaction_amount = models.FloatField(null=False, default=100)
income_type = models.CharField(max_length=20, choices=income_types, default="IN")
next_date = models.DateField(null=False, default=today)
frequency = models.CharField(max_length=20, choices=frequencies, default=1)
complete = models.BooleanField(default=False)
def __str__(self):
return self.transaction_name
class Meta:
ordering = ['next_date']
I feel like I have a lot of things that would be nice to have as a model method but they need to get the current user which I'm not sure how to do.
Not sure if this is what you mean but you can do:
def create_budget(self, context):
starting_point = BudgetStartingPoint.objects.filter(owner=self.request.user)
running_tally = starting_point[0].starting_amount
budget_dict = {}
for transaction in self.get_queryset():
running_tally = transaction.get_change(running_tally)
budget_dict[transaction] = [running_tally, transaction.income_type]
context['budget'] = budget_dict
class BudgetTransaction(models.Model):
....
def get_change(self,value):
return value + (
self.transaction_amount
if self.income_type == "IN" else
-self.transaction_amount
)

How to get total quantity of each kind of task in Django?

I have 1 table for tasks, those tasks can be in 3 status"todo","in-progress" and "done", I
want to calculate total number of each status' task, and put it into an array like ('todo
total','progress total','done total'), any idea how can I achieve that? my final goal is to
display the 3 subtotal in Chartjs, Thanks in advance.
models.py
'''
class Todo(models.Model):
status_option = (
('to_do', 'to_do'),
('in_progress', 'in_progress'),
('done', 'done'),
)
status = models.CharField(max_length=20, choices=status_option, default='to_do')
# todo_list's content
team = models.ForeignKey('Team', on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
create_date = models.DateTimeField(auto_now_add=True)
start_date = models.DateTimeField(default=datetime.datetime.now)
due_date = models.DateTimeField(default=datetime.datetime.now)
project_code = models.CharField(max_length=20)
details = models.TextField()
def __str__(self):
return self.status
# return self.team['team'].queryset
def update_status(self):
if self.status == 'to_do':
self.status = 'in_progress'
elif self.status == 'in_progress':
self.status = 'done'
self.save()
'''
If you want to count one status by itself you can do this:
to_do_count = Todo.objects.filter(status='to_do').count()
If you want a dictionary counting each status you can do this:
from django.db.models import Case, When
counts_data = Todo.objects.aggregate(
to_do_count=Count(Case(When(status='to_do', then=1))),
in_progress_count=Count(Case(When(status='in_progress', then=1))),
done_count=Count(Case(When(status='done', then=1))),
)
or this:
from django.db.models import Q
counts_data = Todo.objects.aggregate(
to_do_count = Count('pk', filter=Q(status='to_do')),
in_progress_count = Count('pk', filter=Q(status='in_progress')),
done_count = Count('pk', filter=Q(status='done'))
)

How to add additional model to list and filter on custom condition

I have 4 models from 4 different tables :
class Profile(models.Model):
player_name = models.CharField(max_length=150)
player_surname=models.CharField(max_length=200)
sport_type=models.CharField(max_length=200)
class Results_2019(models.Model):
player_name = models.CharField(max_length=150)
first_score=models.DecimalField(decimal_places=2)
second_score=models.DecimalField(decimal_places=2)
average_score=models.DecimalField(decimal_places=2)
class Results_2018(models.Model):
player_name = models.CharField(max_length=150)
first_score=models.DecimalField(decimal_places=2)
second_score=models.DecimalField(decimal_places=2)
average_score=models.DecimalField(decimal_places=2)
class Weight(models.Model):
player_name = models.CharField(max_length=150)
year2019=models.DecimalField(decimal_places=2)
year2018=models.DecimalField(decimal_places=2)
I use these models to filter based on meeting certain condition. Foreign key does not work for me ( i tried i do not what is wrong).
ABC = []
for MyResults in [Results_2019, Results_2018 ]:
results_list = MyResults.objects.annotate(increase=F('second_score') /
F('first_score'),total=F('second_score') +
F('first_score',).filter(increase__gt=0.2,total__gt=average_score,)
ABC.extend(results_list.values_list('player_name', flat=True))
DoubleSNames = list(set([x for x in ABC if ABC.count(x) == 2]))
finallist=list(Profile.objects.filter(player_name__in=DoubleSNames).
values_list('player_name', 'player_surname'))
This code returns list of players and their surname who meet the criteria.
However i can't embed Weight class in list and filter based on
score_weight=first_score/Weight.year19
and then filter if score_weight > 30
I tried to embed
weight_list=[Weight.year19,Weight.year18]
How can i use weight_list with MyResults to calculate score_weight=first_score/Weight.year19
How to to do it?
Is that possible at all to do that ?
Additional:
When i asked you this question i minimised formula i put in filter so after i understand code answered i learn and i can solve independently. Which i did the majority, however i get lost as i haven't done that before but want to learn.
However there are 2 formula I can't insert in the code and make it work. I am keep learning and some are confusing.
Questions:
So the code answered to the question in annotate:
total=F('second_score') + F('first_score')
This is from the code.
Formula I want to embed and filter is:
total_growth=total2019/total2018
So total of 2019 divided by total of 2018). In Python there is the list I tried to apply like:
total_growth=list(map(lambda i,w: (i/w) if w else 0, total[0],total[1]))
And check on condition whether total_growth > 0.05
However it does not work and i do not exactly where to put it in the code to make it work?
How to filter by sport_type (class Profile) so sport_type is not in football.
Would appreciate help to my additional queries to close finally my concern over this question.
I wrote my own code, you are right the result is always empty because of this conditions:
total=second+first
score_weight=total/weight19
if increase > 0.2 and total > average and average > 50 and total > 70 and
score_weight > 30 : ABC.append(result.player_name)
score_weight will never be > 30
you can find below my code, when I put score_weight > 1 I can get results:
model.py:
class Profile(models.Model):
player_name = models.CharField(max_length=150)
player_surname = models.CharField(max_length=200)
sport_type = models.CharField(max_length=200)
class Results2019(models.Model):
player_name = models.CharField(max_length=150)
first_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
second_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
average_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
class Results2018(models.Model):
player_name = models.CharField(max_length=150)
first_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
second_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
average_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
class Weight(models.Model):
player_name = models.CharField(max_length=150)
year19 = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
year18 = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
views.py
def myPageView():
ABC = {}
players = Profile.objects.all().values()
for result in players:
for data in [[Results2019, 'year19'], [Results2018, 'year18']]:
Results = data[0].objects.get(player_name__exact=result['player_name'])
if Results:
wgt = Weight.objects.filter(player_name__exact=result['player_name']).values(data[1]).last()
if wgt:
if float(Results.first_score) > 0.0:
increase19 = float(Results.second_score) / float(Results.first_score)
else:
increase19 = 0
total = float(Results.second_score) + float(Results.first_score)
score_weight = total / float(wgt[data[1]])
if (increase19 > 0.2) and (total > float(Results.average_score)) and \
(float(Results.average_score) > 50) and (total > 70) and (score_weight > 1):
ABC[result['player_name']] = result['player_surname']
else:
pass
else:
pass
else:
pass
print('*************ABC***************', ABC)
finallist = []
for name in ABC.keys():
finallist.append([name, ABC[name]])
print('*************finallist***************', finallist)
A) Possible bugs:
Maybe you forgot to initialize some variables in the loop for Weight. (your intention was not clear to me)
Other possible problem could be combined tabs and spaces. The code is incorrectly indented after you have pasted it to Stackoverflow. Maybe your editor has a different tab size, maybe Python2 interpreted it as valid, but not how you see it in the editor. Try to run python with -t option, to see warnings if the interpreted logic depends on the tab size. The recommended code style for Python is to not use TABs and to fix all indents to multiples of 4 spaces. The best editor setting for Python is to automatically expand all tabs to spaces.
B) optimizations:
very important
finallist simplified from many database requests to one request:
finallist = list(
Profile.objects.filter(player_name__in=DoubleSNames)
.values_list('player_name', 'player_surname')
)
less important optimization
simplify MyResults.objects.all() to a request with less data rows
results_list = MyResults.objects.filter(
averrage__gt=50,
second_score__gt=70 - F(first_score),
average__lt=F(first_score) + F(second_score),
first_score__gt=0,
second_score__gt=0.2 * F(first_score),
)
ABC.extend(results_list.values_list('player_name', flat=True))
It shouldn't be done before the view will be slow, because readability counts and maybe this will be not the bottle neck.
Disadvantages:
The expression for increase with Case() When() functions would be bad readable. Everything is reformulated, but it is still less readable than a filter on plain Python list.
EDIT:
I accept that you forgot to set the primary key and foreign keys that suggested #Houda, how is normal in Django and that it can be too late to allow Django to add indexes to these tables.
If all data from Profile and Weight can fit in the memory then you can easily map it by a dictionary, otherwise you can filter it by individual names as you did originally or to load them to the memory by a subset of players.
from collections import Counter
profile_map = {x.player_name: x for x in Player.objects.all()}
weight_map = {x.player_name: x for x in Weight.objects.all()}
totals_map = {} # like rows: player, columns: year
ABC = []
for MyResults in [[Results_2019, Results_2018 ]:
results_list = (
MyResults.objects
.annotate(
increase=F('second_score') / F('first_score'),
total=F('second_score') + F('first_score')
)
.filter(
increase__gt=0.2,
total__gt=average_score,
# ... possible more filters
)
)
year_str = MyResults._meta.model_name[-4:]
for result in results_list:
player_name = result.player_name
player = profile_map[player_name]
weight = weight_map[player_name]
weight_year = vars(weight)['year' + year_str[-2:]]
score_weight = result.first_score / weight_year
totals_map.setdefault(player_name, {})[year_str] = result.total
if score_weight > 30 and player.sport_type not in ['football']:
ABC.append(player_name)
counter = Counter(ABC)
DoubleSNames = {name for name, count in counter.items() if count == 2}
finallist = []
for player_name in DoubleSNames:
totals = totals_map['player_name']
total2018 = totals.get('2018', 0)
total2019 = totals.get('2019', 0)
if totals2018 > 0 and total2019 / total2018 > 0.05:
player = player_map[player_name]
finallist.append([player.player_name, player.player_surname])
(It is not an advanced question about django-queryset now, but how to simulate a database relation by Python.)
model.py
from django.db import models
class Profile(models.Model):
player_name = models.CharField(max_length=150)
player_surname = models.CharField(max_length=200)
sport_type = models.CharField(max_length=200)
class Results2019(models.Model):
player_name = models.CharField(max_length=150)
first_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
second_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
average_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
increase = models.BooleanField(null=False, blank=False, default=False, editable=False)
total = models.BooleanField(null=False, blank=False, default=False, editable=False)
def save(self, *args, **kwargs):
if self.second_score / self.first_score > 0.2:
self.increase = 1
else:
self.increase = 0
if self.second_score + self.first_score > self.average_score:
self.total = 1
else:
self.total = 0
super().save(*args, **kwargs)
class Results2018(models.Model):
player_name = models.CharField(max_length=150)
first_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
second_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
average_score = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
increase = models.BooleanField(null=False, blank=False, default=False, editable=False)
total = models.BooleanField(null=False, blank=False, default=False, editable=False)
def save(self, *args, **kwargs):
if self.second_score / self.first_score > 0.2:
self.increase = 1
else:
self.increase = 0
if self.second_score + self.first_score > self.average_score:
self.total = 1
else:
self.total = 0
super().save(*args, **kwargs)
class Weight(models.Model):
player_name = models.CharField(max_length=150)
year19 = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
year18 = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True,)
views.py
ABC = {}
weight_dict = defaultdict(dict)
player_dict = {}
for player in Profile.objects.values('player_name', 'player_surname'):
player_dict[player['player_name']] = player['player_surname']
for wt in Weight.objects.values():
weight_dict[wt['player_name']] = {'year19': wt['year19'], 'year18': wt['year18']}
for MyResults in [[Results2019, 'year19'], [Results2018, 'year18']]:
results_list = MyResults[0].objects.\
filter(increase__exact=True, total__exact=True).values('player_name', 'first_score')
for t in results_list:
if t['player_name'] in ABC.keys():
pass
elif float(t['first_score']) / float(weight_dict[t['player_name']][MyResults[1]]) > 30:
ABC[t['player_name']] = player_dict[t['player_name']]
finallist = ABC.items()

filtering in django with foriegn key references

I have a view that is taking in query params and searching for venues which meet that query param.
cuisine = self.request.query_params.getlist('cuisine', 0)
if cuisine != 0:
cuisinelist =[]
for cuisine in cuisinelist:
cuisinepk = get_object_or_404(Cuisine, name=cuisine)
cuisinepk = cuisinepk.pk
cuisinelist.append(cuisinepk)
print('this is the cuisine pk')
print(cuisinepk)
venuelist = venuelist.filter(Q(cuisine1__in=cuisinelist) | Q(cuisine2__in=cuisinelist))
But it is not working in fact I do not get any returned venues.
the cuisine1 and cuisine2 in a venue object look like this.
cuisine1 = models.ForeignKey(Cuisine, on_delete=models.DO_NOTHING, related_name='cuisine1', blank=True, null=True)
cuisine2 = models.ForeignKey(Cuisine, on_delete=models.DO_NOTHING, related_name='cuisine2', blank=True, null=True)
I use this method when I wanted to check relations within a room. I am wondering if I need to do the same? What is the correct method?
seatedcapacity = self.request.query_params.get('seatedcapacity',0)
if seatedcapacity != 0:
try:
seatedcapacity = int(seatedcapacity)
except ValueError:
return Response(status=status.HTTP_400_BAD_REQUEST)
rooms = Room.objects.filter(venue=OuterRef('pk'), seatedcapacity__gte=seatedcapacity)
venuelist = venuelist.annotate(hasseatedcapacity=Exists(rooms))
venuelist = venuelist.filter(Q(hasseatedcapacity=True) | Q(fullbuyoutseatedcapacity__gte=seatedcapacity))

Throwing ZeroDivisionError

I need to get a calculation of some data so, in annotate I put some maths logic with other field but whenever there is 0 it is throwing an error. I need to handle that error in annotate. My code looks like this:
total_amount = Invoice.objects.filter(client_account__account_UID=account_UID,
created_at__range=(from_date, to_date)
).aggregate(Sum('total_amount'))['total_amount__sum']
total_billable_leads = CampaignContact.objects.filter(campaign=campaigns, billable=True, billable_item__created_at__range=(from_date, to_date)).count()
cch = CampaignContactHistory.objects.annotate(
campaign_name=F('campaign__name')
).values('campaign_name'
).filter(id__in=cch_ids
).annotate(
total=Count('lead_status'),
scheduled=Count(Case(When(lead_status='10', then=1))),
total_billable=(int(total_amount) / total_billable_leads) * Count(Case(When(campaign_contact__billable=True, then=1))),
)
In total_billable, there is total_billable_leads variable which may have Zero(0) then on a division it will throw an error. So, please help me to handle this exception in annotate.
CampaignContactHistory Model
class CampaignContactHistory(DateAwareModel):
campaign_contact = models.ForeignKey(CampaignContact, on_delete=models.CASCADE)
lead_status = models.CharField(max_length=20, choices=leadstatus, default=FRESH)
campaigner = models.ForeignKey(Resource, on_delete=models.CASCADE)
response_date = models.DateTimeField(null=True, blank=True)
first_reponse = models.TextField(blank=True, null=True, default='')
second_reponse = models.TextField(blank=True, null=True, default='')
campaign = models.ForeignKey(Campaign, null=True, blank=True)
For result I want if it is an error or zero(0) it should return zero(0) otherwise the calculated value.
the total_amount and total_billable_leads are constants so you get the error on python level, so the solution is:
if total_billable_leads:
total_amount_avg = int(total_amount) / total_billable_leads
else:
total_amount_avg = 0
cch = CampaignContactHistory.objects.annotate(
campaign_name=F('campaign__name')
).values('campaign_name'
).filter(id__in=cch_ids
).annotate(
total=Count('lead_status'),
scheduled=Count(Case(When(lead_status='10', then=1))),
total_billable=total_amount_avg * Count(Case(When(campaign_contact__billable=True, then=1))),
# ^^^^^^^^^^^^^^^
)

Categories