I have the following models in django
class Job(models.Model):
cost = models.FloatField()
class Account(models.Model):
job = models.ManyToManyField(Job, through='HasJob')
class HasJob(models.Model):
account = models.ForeignKey(Account, related_name='hasjobs')
job = models.ForeignKey(Job, related_name='hasjobs')
quantity = models.IntegerField()
So an Account can have many jobs in different quantities. I want to be able to sum up the total cost of an account. Is that possible in database level or should I do python for it? Like
account = Account.objects.get(pk=1)
sum = 0
for hasjob in account.hasjobs.all():
sum += hasjob.quantity*hasjob.job.cost
I know its a very "starters" way to do that, and I am guessing it includes many hits on the database. So is there a better way?
IFAIK aggregation can't sum by F() expressions so you have to calculate the sum in python code.
But you can reduce the number of db hits to one - just add the select_related() call to the queryset:
total_sum = sum(hasjob.quantity * hasjob.job.cost
for hasjob in account.hasjobs.all().select_related('job'))
Related
I have 2 columns named Serial and Bag I need them to be auto incremented but based on each other and also based on the user that will update the record, so every bag should have 100 serial and reset the number automatically after reaching 100, then start again with Bag number 2 and put 100 serial in it and reset.
For example:
when user update the first record the Bag will start with number 1 and Serial will be also number 1 the second record Bag will still number 1 and the serial will be changed to number 2 till reach 100 Serial in one Bag, then we will start again with bag number 2 and serial number 1 etc ...
Thanks
The way you explain your example is a bit confusing but I'll try to give you an answer.
I assume the "2 columns named Serial and Bag" are fields of the same model and as you replied in the comments "the record is already existing but it has empty serial and bag", which means the auto-increment begins when the record is updated. Lastly, you mentioned first and second records implying that there are multiple records in this model. Based on these criteria, what you can do is add a save method in your model:
# Sample model
class Record(models.Model):
bag = models.IntegerField(default=0, null=True)
serial = models.IntegerField(default=0, null=True)
created_at = models.DateTimeField(auto_now=True, null=True)
def save(self, *args, **kwargs):
# Ensures the record will only auto-increment during update
if self.created_at:
# Retrieves the object with the highest bag & serial value
latest_record = Record.objects.all().order_by('bag', 'serial').last()
# Incrementing logic
if latest_record.serial_no + 1 <= 100:
self.bag = latest_record.bag if latest_record.bag > 0 else 1
self.serial = latest_record.serial + 1
else:
self.bag = latest_record.bag + 1
self.serial = 1
super(Record, self).save(*args, **kwargs)
Now, each time you write save like:
record = Record()
record.save()
The model save method executes.
Rather than do the incrementing logic in python, where it is subject to race conditions if multiple updates can happen concurrently, it should be possible to push it down into the database.
Something like:
update foop set
bag=vala,
ser=valb
from (
select
case when ser >= 5 then bag+1 else bag end as vala,
case when ser >= 5 then 1 else ser+1 end as valb
from foop
order by bag desc nulls last,
ser desc nulls last
limit 1) as tt
where some_primarykey = %;
It might be possible to translate that into django ORM, but it might also be easier and more readable to just drop into raw SQL or sneak it in via .extra() on a queryset than attempt to shoehorn it in.
I am trying to iterate through each instance of a model I have defined.
Say I have the following in models.py, under the people django app:
class foo(models.Model):
name = models.CharField(max_length=20, default="No Name")
age = models.PositiveSmallIntegerField(default=0)
And I have populated the database to have the following data in foo:
name="Charley", age=17
name="Matthew", age=63
name="John", age=34
Now I want to work out the average age of my dataset. In another part of the program (outside the people app, inside the project folder), in a file called bar.py that will be set up to run automatically every day at a specific time, I calculate this average.
from people.models import foo
def findAverageAge():
total = 0
for instance in //What goes here?// :
total += instance.age
length = //What goes here, to find the number of instances?//
average = total / length
return average
print(findAverageAge)
In the code above, the //What goes here?// signifies I don't know what goes there and need help.
You can retrieve all elements with .all() [Django-doc]:
from people.models import foo
def findAverageAge():
total = 0
qs = foo.objects.all()
for instance in :
total += instance.age
length = len(qs)
average = total / length
return average
print(findAverageAge())
But you should not calculate the average at the Django/Python level. This requires retrieving all records, and this can be quite large. A database itself can calculate the average, and this is (normally) done in a more efficient way. You can .aggregate(…) [jango-doc] on the queryset with:
from people.models import foo
def findAverageAge():
return foo.objects.aggregate(
avg_age=Avg('age')
)['avg_age'] or 0
print(findAverageAge())
You should make a management command however: this will load the Django apps, and also makes it more convenient to run command with parameters, etc.
I would like to display the percentage mark instead of the total sum of the mark. Right now i have a table that display the student name and their mark attendance. I would like to convert the mark attendance into a percentage. the current implementation is:
Student Name Attendance
Annie 200
Anny 150
But i would like to show the attendance in percentange. for example:
Student Name Attendance
Annie 100%
Anny 85%
i am not sure how to implement the method. But i have tried this:
# models.py:
class MarkAtt(models.Model):
studName = models.ForeignKey(
Namelist, on_delete=models.SET_NULL, blank=True, null=True, default=None,
)
classGrp = models.ForeignKey(GroupInfo, on_delete=models.SET_NULL, null=True)
currentDate = models.DateField(default=now())
week = models.IntegerField(default=1)
attendance = models.IntegerField(default=100) #1 is present
def get_percentage(self):
ttlCount = MarkAtt.objects.filter(studName).count()
perc = ttlCount / 1100 *100
return perc
# views.py:
def attStudName(request):
students = MarkAtt.objects.values('studName__VMSAcc').annotate(mark=Sum('attendance'))
context = {'students' : students}
return render(request,'show-name.html', context)
So you have your numerator but you need your denomenator. I'm not exactly sure what your denomenator should be with your current setup but creating a new field that uses "Count" rather than "Sum" might do the trick for you. Then you would divid the sum field by the count field. I would probably just do this in the view and not mess with the model.
You can use the formatting mini-language to express a percentage in a string
>>> attendence = 20
>>> total = 100
>>> '{:%}'.format(attendence/total)
'20%'
Bear in mind this will return the answer as string instead of an int or float
To use this in your get_percentage, there are a few issues that will need to be addressed:
studName is not defined or passed into the method but is used in the filter query. This will cause a NameError.
Your filter query looks like it is just counting the students in the filter query instead of summing the attendance. You should use a sum annotation like you have done in the view.
You will need to be able to get the value for 100% attendance to be able to divide by it.
To modify your implimentatiom of this on the model you could do something like this.
def get_percentage(self):
student = MarkAtt.objects.filter(studName=self.studName).annotate(mark=Sum('attendance'))
return '{:%}'.format(student.mark / 1100)
However, I don't think the MarkAtt model is the right place to do this as many MarkAtt objects could relate to one NameList object resulting in possibly running the same query several times for each student. I think it would be better to do this on NameList or the view itself.
class NameList(models.Model):
...
def get_percentage(self):
attendance = self.markatt_set().annotate(total=Sum('attendance'))
return '{:%}'.format(attendance.total / 1100)
I have created a custom field in the model mrp.BOM structure as below to compute the total cost of BOM for a product:-
Field Name:- x_bom_total
Field Label:- Total BOM
Field Type:- Float
ReadOnly:- True
Dependency:- bom_line_ids
Dependent Field name 'bom_line_ids' is the field which display all the materials used in the product. It refernces to the model 'mrp.bom.line' in a one2many relationship model. So now in the computation part how to calculate the Total BOM for the product something like this:-
for record in self:
for each_object in record.bom_line_ids:
record['x_bom_total'] += record.bom_line_ids.qty * record.bom_line_ids.list_price
I am using odoo v11. Does anyone have an idea?
You were on the right way, but you should consider child BOMs, too.
First your nearly correct approach without child BOMs:
for record in self:
total = 0.0
for line in record.bom_line_ids:
total += line.product_qty * line.product_id.list_price
record['x_bom_total'] = total
And now with consideration of child BOMs. You're obviously using Odoo Studio App, but i don't know if you can define methods on computed fields, but you can give it a try. A recursive function would be really nice here:
def get_bom_total(lines)
total = 0.0
for line in lines:
if line.child_bom_id:
total += get_bom_total(line.child_bom_ids) # recursive call
else:
total += line.product_qty * line.product_id.list_price
return total
for record in self:
record['x_bom_total'] = get_bom_total(record.bom_line_ids)
I'm really stuck on a Django query and am hoping you've got a couple minutes to help me figure it out.
I have a very simple model:
class Task(models.Model):
# a tuple representing a specific item to be searched for a specific URL
instructions = models.TextField()
ASSIGNMENT_STATUS_CHOICES = (
( 'a', 'assigned' ),
( 'c', 'submitted for review' ),
( 'f', 'finished' ),
( 'r', 'rejected' ),
)
class Assignment(models.Model):
# the overall container representing a collection of terms for a page found
# by a user
user = models.ForeignKey(User)
task = models.ForeignKey(Task, related_name='assignments')
status = models.CharField(max_length=1, choices=ASSIGNMENT_STATUS_CHOICES, default='a')
What I want to do is randomly select a task that has fewer than N assignments that are status != 'r'. In other words, I want to make sure each task gets successfully completed N times, so if a worker requests a task it needs one that has fewer than N tasks in a state that could lead to finishing.
I'm just totally lost trying to figure out the query that would return such tasks. For a given task, I can test:
task.assignments.exclude(status='r').count() < N
and if that is true it's a candidate. But how do I query Task.objects in such a way that it returns all candidates in a single database query such that I can randomly choose one:
Task.objects.<some magic filter>.order_by('?')[0]
Any help would be appreciated!
from django.db.models import Count
Task.objects.exclude(assignments__status='r').annotate(assignments_count=Count('assignments').filter(assignments_count__gt=N)