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'))
)
Related
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
)
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
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()
I met an issue when I want to pass a list of value to one attribute during initial the form.
The whole process of what I am doing is:
1. User defines a number N.
2. I display N text field.
3. User fills in data and I store in the database.
4. User wants to modify the data -> which is the issue I have when. I want to initial the form with current data.
Here is my model.py
class PageComponent(models.Model):
componentName=models.CharField(max_length=50,null=True, verbose_name="Name")
type = models.ForeignKey(Type, on_delete=models.CASCADE)
user = models.ForeignKey(AS_User, on_delete=models.CASCADE, editable=False)
page = models.ForeignKey(CommunityPage, on_delete=models.CASCADE, editable=False)
STATUS = (
('a', 'Activated'),
('d', 'Deactivated'),
)
componentStatus=models.CharField(
max_length=1,
choices=STATUS,
blank=False,
default='d',
help_text='the current status of the page component', editable=False
)
textContent=models.TextField(max_length=10000, help_text="Enter a description for your component", null=True, blank=True)
photoContent=models.ImageField(upload_to=component_directory_path, null=True, blank=True, verbose_name="Photo")
videoContent=models.FileField(upload_to=component_directory_path, null=True, blank=True, verbose_name="Video")
def __str__(self):
return self.componentName
class PageComponent_SeasonalText(models.Model):
pageStextComponent = models.ForeignKey(PageComponent, on_delete=models.CASCADE)
seasonalText = models.CharField(max_length=10001)
Here is my form.py
class SeasonalTextForm(forms.Form):
componentName = forms.CharField(label=_('Enter title'),max_length=40)
def __init__(self, *args, **kwargs):
seasonTexts = kwargs.pop('extra')
super(SeasonalTextForm, self).__init__(*args, **kwargs)
# self.cleaned_data = {}
for i in range(0, seasonTexts):
field_name = 'seasonText_%s' % (i,)
self.fields[field_name] = forms.CharField(widget=forms.Textarea(attrs={'rows':10, 'cols':51}))
#set field label as placeholder for every field
for field in self.fields.values():
field.widget.attrs["placeholder"] = field.label
def clean(self):
seasonTexts = set()
i = 0
field_name = 'seasonText_%s' % (i,)
while self.cleaned_data.get(field_name):
seasonText = self.cleaned_data[field_name]
if seasonText in seasonTexts:
self.add_error(field_name, 'Duplicate')
else:
seasonTexts.add(seasonText)
i += 1
field_name='seasonText_%s' % (i,)
self.cleaned_data["seasonTexts"] = seasonTexts
def save(self):
for seasonalText in self.cleaned_data["seasonTexts"]:
PageComponent_SeasonalText.objects.create(pageStextComponent = PageComponent.pageStextComponent,seasonalText = seasonalText,)
Here is my view.py
def edit_stext(request, page_id, id):
page = get_object_or_404(CommunityPage, pk=page_id)
component = PageComponent.objects.get(id=id)
stext = PageComponent_SeasonalText.objects.filter(pageStextComponent=component)
numOfSeasons = page.numSeasons
if request.method == "POST":
stextEditor = SeasonalTextForm(request.POST, instance=stext, extra=numOfSeasons)
if stextEditor.is_valid():
stextEditor.save()
return HttpResponseRedirect(reverse('page', kwargs={'page_id' : page_id}))
else:
# stext1 = PageComponent_SeasonalText.objects.filter(pageStextComponent=component)
initial = {}
initial['componentName'] = component.componentName
for i in range(0,numOfSeasons):
st = stext[i].seasonalText
print(st)
initial['seasonalText_{}'.format(i)] = st
stextEditor = SeasonalTextForm(initial=initial, extra=numOfSeasons)
return render(request, 'editComponent_stext.html', {'stextEditor': stextEditor})
NB:
at the view.py, I have a print function to print the actual value of the attribute "seasonalText" and it is success. But it seems cannot be passed to the initial when I want to initial the form.
Thanks for all spending time to help me with this issue. Many thanks.
Screenshot for understanding:
print function gets the correct value
initialed form doesn't get the seasonalText value
models.py
My models.py
class Custom_user_model(User):
daily_target = models.IntegerField()
monthly_target = models.IntegerField()
yearly_target = models.IntegerField()
weekly_target = models.IntegerField()
call_target = models.IntegerField()
email_target = models.IntegerField()
meeting_target = models.IntegerField()
added_under = models.IntegerField()
profile_pic = models.TextField()
doj = models.DateTimeField(default='')
location_id = models.IntegerField()
locked = models.BooleanField()
default_currency = models.IntegerField()
date_change_permission = models.BooleanField()
deal_back_log = models.BooleanField()
created_date=models.DateTimeField(auto_now_add=True)
role_id=models.IntegerField()
profile_pic = models.FileField(upload_to='.')
objects = UserManager()
//This custom_user model is the extension of django's default user model.
class Deal(models.Model):
a_choices = ((0,'yes'),(1,'no'))
approved = models.IntegerField(choices=a_choices,default=1)
user_id = models.IntegerField()
company_id = models.IntegerField()
contact_id = models.IntegerField()
deal_title=models.CharField(max_length=200)
deal_value = models.CharField(max_length=20)
currency_id = models.IntegerField()
process_id = models.IntegerField()
expected_close_date = models.DateField(default='')
closed_date = models.DateField()
deal_milestone=models.IntegerField()
created=models.DateTimeField(auto_now_add=True)
last_modified=models.DateTimeField(auto_now_add=True)
s_choices = ((0,'active'),(1,'won'),(2,'junk'),(3,'lost'))
status = models.IntegerField(choices=a_choices,default=0)
type = models.CharField(max_length=50, default='deal')
source = models.CharField(max_length=50,default='O')
class user_Roles(models.Model):
code = models.CharField(max_length=20)
description = models.CharField(max_length=30)
permitted_menus = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
Using user_roles model, I have assigned permission for accessing data to the newly created user based on his/her role. I want to get the created deals which are added by the users having role_id = 2 and deals created date between the specified dates .
### views.py
st_date, end_date = week_magic(date.today())
cur = connection.cursor()
cur.execute("select *, CONCAT(au.first_name,' ',au.last_name) as full_name from myapp_custom_user_model mu left join auth_user au on mu.user_ptr_id = au.id INNER JOIN myapp_user_roles ml on ml.id= 2 and ml.id = mu.role_id LEFT JOIN (SELECT user_id,SUM( deal_value ) AS cnt FROM myapp_deal where status = 1 and DATE_FORMAT(closed_date,'%Y-%m-%d') BETWEEN " '%s' " and " '%s' " GROUP BY user_id)d ON mu.user_ptr_id = d.user_id where mu.locked !=1 and mu.role_id = 2 order by COALESCE( d.cnt, 0 ) DESC",(st_date,end_date))
users = dictfetchall(cur)
cur.close()
While executing the query it shows unsupported format error. So I used one more % symbol in the same query as follows:
cur.execute("select *, CONCAT(au.first_name,' ',au.last_name) as full_name from myapp_custom_user_model mu left join auth_user au on mu.user_ptr_id = au.id INNER JOIN myapp_user_roles ml on ml.id= 2 and ml.id = mu.role_id LEFT JOIN (SELECT user_id,SUM( deal_value ) AS cnt FROM myapp_deal where status = 1 and DATE_FORMAT(closed_date,'%%Y-%%m-%%d') BETWEEN " '%s' " and " '%s' " GROUP BY user_id)d ON mu.user_ptr_id = d.user_id where mu.locked !=1 and mu.role_id = 2 order by COALESCE( d.cnt, 0 ) DESC" %(st_date,end_date))
It doesn't give any error but the result is empty even though there is data because of this syntax: DATE_FORMAT(closed_date,'%%Y-%%m-%%d'). How to solve this?
First of all you should use ForeignKey fields for role_id in Custom_user_model and user_id in Deal. The same is probably true for some of the other _id fields in your models.
class Custom_user_model(User):
...
role = models.ForeignKey('Role')
...
class Deal(models.Model):
...
user = models.ForeignKey('Custom_user_model')
...
After that you can do your query like this:
# get deals from users with role_id=2
query = Deal.objects.filter(user__role_id=2)
# add filter for deals created by that user created between
start_date, end_date = week_magic(date.today())
query = query.filter(created__between=(start_date, end_date))