Refactor Business Logic From View To Model - python

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
)

Related

FileField in nested serializer is holding a path, when updated throws error "The submitted data was not a file. Check the encoding type on the form."

I have created a nested serialiser and custome create and update methods in it. While POST(Create) methos i am not getting any error, but while Updating i am getting error "The submitted data was not a file. Check the encoding type on the form.".
Model:
class SmsModelMaster(models.Model):
SRNumber = models.CharField('Request Number', max_length=50, blank=True, unique=True)
SRState = models.CharField(max_length=100, choices=STATUS_CHOICES, default='Requested',
blank=False, null=True, unique=False)
PreviousSRState = models.CharField(max_length=100, blank=True, null=False)
SRCategory_SMS = models.ForeignKey(ServiceBucket, related_name='SRCategory_SMS',
blank=True, on_delete=models.CASCADE)
SRName_SMS = models.ForeignKey(ServiceBucket, related_name='SRName_SMS', blank=True,
on_delete=models.CASCADE)
RequestType = models.CharField(max_length=100, blank=True, null=False)
RequesterName = models.CharField(max_length=100, blank=True, null=False)
RequestedOnBehalfOf = models.CharField(max_length=100, null=True)
SMETSVersion = models.CharField(max_length=100, blank=True, null=True)
TPName = models.CharField(max_length=150, null=True)
Expected_Completion_Time = models.DateTimeField(default=timezone.now, blank=True,
null=True)
Request_Time = models.DateTimeField(default=timezone.now, null=True, blank=True)
CompletionDate = models.DateField(default=timezone.now, null=True, blank=True)
MetersetUseEndDate = models.DateField(default=timezone.now, blank=True, null=True)
ActivitySummary = models.CharField(max_length=3000, blank=True, null=True)
DocumentUpload = models.FileField(upload_to='Support_Request/SMS_Model/', default=None,
null=True, blank=True)
Assigned_To = models.CharField(max_length=100,blank=True, null=True)
def __str__(self):
return self.SRNumber
class Meta:
verbose_name_plural = "SMS Administrator"
Serializer:
class sms_adminserializer(serializers.ModelSerializer):
commentdata = sms_commentserializer(many=True, required=False)
metersetdata = sms_metersetserializer(many=True, required=False)
devicedata = sms_deviceserializer(many=True, required=False)
Expected_Completion_Time = serializers.DateTimeField(required=False)
class Meta:
model = SmsModelMaster
fields = ('SRNumber', 'SRState', 'PreviousSRState', 'SRCategory_SMS', 'SRName_SMS',
'RequestType', 'RequesterName', 'RequestedOnBehalfOf','SMETSVersion', 'TPName',
'Expected_Completion_Time, 'Request_Time','CompletionDate', 'MetersetUseEndDate',
'ActivitySummary', 'DocumentUpload', 'commentdata', 'metersetdata',
'devicedata','Assigned_To')
read_only_fields = ('Request_Time', )
def create(self, validated_data):
commentdata = validated_data.pop('commentdata')
metersetdata = validated_data.pop('metersetdata')
devicedata = validated_data.pop('devicedata')
smsdata = SmsModelMaster.objects.create(**validated_data)
count = 0
endcount = 0
for evcomments in commentdata:
evcomments.pop('SRNum', None)
evcomments.pop('CommentState', None)
CommentModel.objects.create(**evcomments, SRNum=smsdata, CommentState=smsdata)
for evmetersetdata in metersetdata:
evmetersetdata.pop('SRName', None)
meterda =MeterSetModelSMSAdmin.objects.create(**evmetersetdata, SRName=smsdata)
tobeuseddevicedata = devicedata
endcount = endcount + meterda.Device_length
deviceslicedata = tobeuseddevicedata[count:endcount]
for evdevicedata in deviceslicedata:
evdevicedata.pop('SR_Name', None)
evdevicedata.pop('MetsersetName', None)
DeviceModelSMSAdmin.objects.create(**evdevicedata, SR_Name=smsdata,
MetsersetName=meterda)
count = count + 1
endcount = endcount
return smsdata
def update(self, instance, validated_data):
comments = validated_data.pop('commentdata')
metersetdata = validated_data.pop('metersetdata')
devicedata = validated_data.pop('devicedata')
instance.SRState = validated_data.get("SRState", instance.SRState)
instance.PreviousSRState = validated_data.get("PreviousSRState",
instance.PreviousSRState)
instance.Expected_Completion_Time = validated_data.get("Expected_Completion_Time",
instance.Expected_Completion_Time)
instance.save()
id_list = []
comm = CommentModel.objects.filter(SRNum=instance)
test = sms_commentserializer(comm, many=True)
for ev in test.data:
id_list.append(ev['id'])
# take all id for the meterset table
met_list = []
metset = MeterSetModelSMSAdmin.objects.filter(SRName=instance)
qmetset = sms_metersetserializer(metset, many=True)
for mev in qmetset.data:
met_list.append(mev['id'])
# take all id for the device table
dev_list = []
devset = DeviceModelSMSAdmin.objects.filter(SR_Name=instance)
qdevset = sms_deviceserializer(devset, many=True)
for dev in qdevset.data:
dev_list.append(dev['id'])
# comments operations
comm_choices = []
for comment in comments:
if "id" in comment.keys():
if CommentModel.objects.filter(id=comment["id"]).exists():
c = CommentModel.objects.get(id=comment["id"])
c.Comments = comment.get('Comments', c.Comments)
c.CommentType = comment.get('CommentType', c.CommentType)
c.CommentedBy = comment.get('CommentedBy', c.CommentedBy)
c.save()
comm_choices.append(c.id)
else:
continue
else:
comment.pop('SRNum', None)
comment.pop('CommentState', None)
c = CommentModel.objects.create(**comment, SRNum=instance,
CommentState=instance)
comm_choices.append(c.id)
for ev in id_list:
if ev not in comm_choices:
com = CommentModel.objects.get(id=ev)
com.delete()
# Meterset operations
meterset_choices = []
for metsett in metersetdata:
if "id" in metsett.keys():
if MeterSetModelSMSAdmin.objects.filter(id=metsett["id"]).exists():
m = MeterSetModelSMSAdmin.objects.get(id=metsett["id"])
m.TestPhase = metsett.get('TestPhase', m.TestPhase)
m.Test_Phase_info_correct = metsett.get('Test_Phase_info_correct',
m.Test_Phase_info_correct)
m.Updated_Test_Phase = metsett.get('Updated_Test_Phase',
m.Updated_Test_Phase)
m.ReallocationToTP = metsett.get('ReallocationToTP', m.ReallocationToTP)
m.ReallocatedTPName = metsett.get('ReallocatedTPName',
m.ReallocatedTPName)
m.Set_Migration = metsett.get('Set_Migration', m.Set_Migration)
m.SMETS1_Reference_Number = metsett.get('SMETS1_Reference_Number',
m.SMETS1_Reference_Number)
m.Alternative_Meterset = metsett.get('Alternative_Meterset',
m.Alternative_Meterset)
m.Alternative_Meterset_Name = metsett.get('Alternative_Meterset_Name',
m.Alternative_Meterset_Name)
m.DCC_Approval = metsett.get('DCC_Approval', m.DCC_Approval)
m.Actioned = metsett.get('Actioned', m.Actioned)
m.Action_Reason = metsett.get('Action_Reason', m.Action_Reason)
m.Installer = metsett.get('Installer', m.Installer)
m.Install_Verification_Doneby = metsett.get('Install_Verification_Doneby',
m.Install_Verification_Doneby)
m.Inventory_Team = metsett.get('Inventory_Team', m.Inventory_Team)
m.New_Lab_Room = metsett.get('New_Lab_Room', m.New_Lab_Room)
m.Updated_MeterSet_Name = metsett.get('Updated_MeterSet_Name',
m.Updated_MeterSet_Name)
m.New_Lab_Position = metsett.get('New_Lab_Position', m.New_Lab_Position)
m.Inprogress_Remarks = metsett.get('Inprogress_Remarks',
m.Inprogress_Remarks)
m.Registered_SMSO = metsett.get('Registered_SMSO', m.Registered_SMSO)
m.Devices_Decommissioned = metsett.get('Devices_Decommissioned',
m.Devices_Decommissioned)
m.Attach_Evidence = metsett.get('Attach_Evidence', m.Attach_Evidence)
m.GPF_replaced_ACB = metsett.get('GPF_replaced_ACB', m.GPF_replaced_ACB)
m.Actual_Efforts = metsett.get('Actual_Efforts', m.Actual_Efforts)
m.Activity_Completed = metsett.get('Activity_Completed',
m.Activity_Completed)
m.UnInstaller = metsett.get('UnInstaller', m.UnInstaller)
m.Actioned = metsett.get('Actioned', m.Actioned)
m.save()
meterset_choices.append(m.id)
else:
continue
else:
metsett.pop('SRName', None)
m = MeterSetModelSMSAdmin.objects.create(**metsett, SRName=instance)
meterset_choices.append(m.id)
for re in met_list:
if re not in meterset_choices:
met = MeterSetModelSMSAdmin.objects.get(id=re)
met.delete()
return instance
API JSON Data Sample:
{
"SRNumber": "SR-129",
"SRState": "Pending Approval",
"PreviousSRState": "New Request",
"SRCategory_SMS": 19,
"SRName_SMS": 19,
"RequestType": "Mothball Out",
"RequesterName": "admin",
"RequestedOnBehalfOf": "admin",
"SMETSVersion": "SSSSSS",
"TPName": "XXXXXXX",
"Expected_Completion_Time": "2021-12-21T10:23:07.643427",
"Request_Time": "2021-12-21T10:23:07.643436",
"CompletionDate": "2021-12-22",
"MetersetUseEndDate": "2021-12-27",
"ActivitySummary": "test",
"DocumentUpload": "http://127.0.0.1:8000/media/Support_Request/SMS_Model/SR-
129_Mothball_Out_211221_102307.zip",
}
While i send update, i am not updating "DocumentUpload" this filed but , i am getting below error.
{
"DocumentUpload": [
"The submitted data was not a file. Check the encoding type on the form."
]
}
Please help me with this issue.

How to return sum according to date range in django?

It'd be really nice if someone could help me with this. I've stuck here. I'm able to do this manually but how to do according to user input.
Payment.objects.filter(created_by=42, mode='cash', created_at__range=["2021-11-01", "2021-11-04"]).aggregate(Sum('amount'))
Here created_by and date_range I'm sending in url like this : http://127.0.0.1:8000/api/v1/registration/?created_by=42&start_date=2021-06-06&end_date=2021-11-18 so the id created by and date_range will always change. And according to change the sum will return.
My Model :
class Payment(TimestampedModel):
customer_visit = models.ForeignKey(
CustomerVisit, on_delete=models.CASCADE, null=True, related_name="customer_payments"
)
mode = models.CharField(choices=PAYMENTCHOICES, max_length=25)
amount = models.FloatField()
ref_no = models.TextField(null=True)
bank = models.ForeignKey(
"BankDetails", on_delete=models.CASCADE, null=True, related_name="payment_bank"
)
is_settlement = models.BooleanField(default=False)
created_by = models.ForeignKey("Employee", on_delete=models.DO_NOTHING, null=True,related_name='payment_created_by')
updated_by = models.ForeignKey("Employee", on_delete=models.DO_NOTHING, null=True,related_name='payment_updated_by')
My View :
class UserWiseCollectionView(ListAPIView):
permission_classes = [
IsAuthenticated,
]
pagination_class = CustomPagination
model = CustomerVisit
serializer_class = UserWiseCollectionSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['created_by']
def get_queryset(self):
start_date = self.request.query_params.get("start_date")
end_date = self.request.query_params.get("end_date")
emp_id = self.request.query_params.get("emp_id")
items = self.model.objects.all()
if start_date and end_date:
items = items.filter(
created_at__range=[start_date, end_date]
)
if emp_id is not None:
items = items.filter(phelebotomist_id = emp_id)
return items
If requirement is to get all the rows created by specific employee within the specific date range, below code might work.
Requirement
Payment.objects.filter(created_by=42, mode='cash', created_at__range=["2021-11-01", "2021-11-04"]).aggregate(Sum('amount'))
View code
def get_queryset(self):
start_date = self.request.query_params.get("start_date")
end_date = self.request.query_params.get("end_date")
emp_id = self.request.query_params.get("emp_id")
employee = Employee.objects.get(pk=emp_id)
payment_by_employee = employee.payment_created_by.all()
if start_date and end_date:
payment_by_employee = payment_by_employee.filter(
created_at__range=[start_date, end_date], mode='cash'
)
payment_by_employee.aggregate(Sum('amount'))
return payment_by_employee
If you want all the records related to specific employee within specific range in CustomerVisit table, you can make use of the related name that is provided to the field(foreign_key) pointing to an employee in customerVisit Table.
For example, if CustomerVisit model has "phelebotomist_id" foreign key that points to Employee Model, you can get all customerVisit records related to Employee Model by below queries
CustomerVisit(models.Model):
phelebotomist_id = models.ForeignKey(Employee, related_name="employee_customer_visit")
emp = Employee.objects.get(pk=<emp_id>)
customer_visits_related_to_emp = emp.employee_customer_visit.all()
You can filter out further depending on the date range and any other fields.

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 loop through object fields in django

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

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()

Categories