Django - show sums of a table of records - django

I have a table which has a list of invoices and their details:
class Invoice(models.Model):
invoiceNum = models.CharField(etc...)
invoiceDate = models.DateField(etc...)
customerID = models.ForeignKey(etc...)
isPaid = models.CharField(etc...)
The Invoice records do not hold the actual invoice total. Instead, an invoice's total is made up of several Invoice_Line_Item records, held in another table:
class Invoice_Line_Item(models.Model):
invNum = models.ForeignKey(Invoice)
itemName = models.CharField(etc...)
itemPrice = models.DecimalField(etc...)
I have a webapp that shows all the invoices in a big HTML table, along with all the details of that invoice on the table's tr row. Details such as, Invoice Date, Invoice Number, Customer ID, all come from that Invoice table. There are hundreds of invoices to display in this HTML table.
What I would like to do is also show each invoice's total value - which is the sum of all the line items. However, I can't think of a simple way to acomplish this since the invoice details and the line items that make up the invoice's total are in two different tables.
One way I thought is to pass the entire Invoice_Line_Item querySet into the HTML template, then for each invoice displayed in a table tr, I could iterate over the entire Invoice_Line_Item querySet, adding up all the line items that match the current invoice. This, however, seems hugely inefficient.
Any better ideas on how to do this?
Thanks!

One word: Aggregation
Invoice_Line_Item.objects.filter(invNum=invoice).aggregate(Sum('itemPrice'))
https://docs.djangoproject.com/en/dev/topics/db/aggregation/
Another way is to store the total in Invoice and update it whenever you change a related Invoice_Line_Item

One more word: annotate.
from django.models import Sum
Invoice.objects.filter( .. ).annotate( InvTotal=Sum( 'invoice_line_number__itemPrice' ) )
InvTolal becomes a new attribute of Invoice object, you can use it in template the same way as invoiceNum or invoiceDate.
With this approach you do not have to pass any additional data structures to your template, only a QuerySet of Invoices.
Please note:
Argument of Sum is a string, which is a concatenation of the name of related model converted to lowercase, than double '_', and than the name of a field in related model.

Related

Join two records from same model in django queryset

Been searching the web for a couple hours now looking for a solution but nothing quite fits what I am looking for.
I have one model (simplified):
class SimpleModel(Model):
name = CharField('Name', unique=True)
date = DateField()
amount = FloatField()
I have two dates; date_one and date_two.
I would like a single queryset with a row for each name in the Model, with each row showing:
{'name': name, 'date_one': date_one, 'date_two': date_two, 'amount_one': amount_one, 'amount_two': amount_two, 'change': amount_two - amount_one}
Reason being I would like to be able to find the rank of amount_one, amount_two, and change, using sort or filters on that single queryset.
I know I could create a list of dictionaries from two separate querysets then sort on that and get the ranks from the index values ...
but perhaps nievely I feel like there should be a DB solution using one queryset that would be faster.
union seemed promising but you cannot perform some simple operations like filter after that
I think I could perhaps split name into its own Model and generate queryset with related fields, but I'd prefer not to change the schema at this stage. Also, I only have access to sqlite.
appreciate any help!
Your current model forces you to have ONE name associated with ONE date and ONE amount. Because name is unique=True, you literally cannot have two dates associated with the same name
So if you want to be able to have several dates/amounts associated with a name, there are several ways to proceed
Idea 1: If there will only be 2 dates and 2 amounts, simply add a second date field and a second amount field
Idea 2: If there can be an infinite number of days and amounts, you'll have to change your model to reflect it, by having :
A model for your names
A model for your days and amounts, with a foreign key to your names
Idea 3: You could keep the same model and simply remove the unique constraint, but that's a recipe for mistakes
Based on your choice, you'll then have several ways of querying what you need. It depends on your final model structure. The best way to go would be to create custom model methods that query the 2 dates/amount, format an array and return it

django setting filter field with a variable

I show a model of sales that can be aggregated by different fields through a form. Products, clients, categories, etc.
view_by_choice = filter_opts.cleaned_data["view_by_choice"]
sales = sales.values(view_by_choice).annotate(........).order_by(......)
In the same form I have a string input where the user can filter the results. By "product code" for example.
input_code = filter_opts.cleaned_data["filter_code"]
sales = sales.filter(prod_code__icontains=input_code)
What I want to do is filter the queryset "sales" by the input_code, defining the field dynamically from the view_by_choice variable.
Something like:
sales = sales.filter(VARIABLE__icontains=input_code)
Is it possible to do this? Thanks in advance.
You can make use of dictionary unpacking [PEP-448] here:
sales = sales.filter(
**{'{}__icontains'.format(view_by_choice): input_code}
)
Given that view_by_choice for example contains 'foo', we thus first make a dictionary { 'foo__icontains': input_code }, and then we unpack that as named parameter with the two consecutive asterisks (**).
That being said, I strongly advice you to do some validation on the view_by_choice: ensure that the number of valid options is limited. Otherwise a user might inject malicious field names, lookups, etc. to exploit data from your database that should remain hidden.
For example if you model has a ForeignKey named owner to the User model, he/she could use owner__email, and thus start trying to find out what emails are in the database by generating a large number of queries and each time looking what values that query returned.

Update multiple results at once in Django

I want to update budget of Category model in Django.
class Category(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=128)
budget = models.DecimalField(default=0.0, decimal_places=2, max_digits=12, help_text="Amount in dollars ($)")
I would have a list of new budget like this which is equal to number of data in Category:
>>> new_category_budget
[Decimal('2700.000'), Decimal('750.000'), Decimal('1500.000')]
I am updating like this:
>>> for budget in new_category_budget:
... Category.objects.filter(user=2).update(budget=budget)
...
3L
3L
3L
But all of these return the same data. What's wrong with my update statement?
What you're doing is iterating over your budget values, and updating all records with user=2 to each one, overriding the previous value.
The return value of QuerySet.update is the number of updated records. Each time you call update(), you get the result that 3 records were updated.
I don't quite understand what you are actually trying to do, but it might be something like this (untested!):
for (budget, category) in zip(new_category_budget, list(Category.objects.filter(user=2)):
category.budget=budget
category.save()
Of course, this assumes that the number of filtered categories will exactly match the number of budgets in new_category_budget, and also the order of iteration over categories is not obvious. All in all, this seems weird :)
When you call update on a QuerySet, it will set all items in the QuerySet to that value. See the example here.
So through your for loop, you are updating all the Category objects with user=2 to each budget. At the end of the for loop, all the Category objects should be have budget == new_category_budget[-1] or the last budget item.
If you want different values for each Category object, you'll need to call save on them individually.

Grouping Django model entries by day using its datetime field

I'm working with an Article like model that has a DateTimeField(auto_now_add=True) to capture the publication date (pub_date). This looks something like the following:
class Article(models.Model):
text = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True)
I want to do a query that counts how many article posts or entries have been added per day. In other words, I want to query the entries and group them by day (and eventually month, hour, second, etc.). This would look something like the following in the SQLite shell:
select pub_date, count(id) from "myapp_article"
where id = 1
group by strftime("%d", pub_date)
;
Which returns something like:
2012-03-07 18:08:57.456761|5
2012-03-08 18:08:57.456761|9
2012-03-09 18:08:57.456761|1
I can't seem to figure out how to get that result from a Django QuerySet. I am aware of how to get a similar result using itertools.groupby, but that isn't possible in this situation (explanation to follow).
The end result of this query will be used in a graph showing the number of posts per day. I'm attempting to use the Django Chartit package to achieve this goal. Chartit puts a constraint on the data source (DataPool). The source must be a Model, Manager, or QuerySet, so using itertools.groupby is not an option as far as I can tell.
So the question is... How do I group or aggregate the entries by day and end up with a QuerySet object?
Create an extra field that only store date data(not time) and annotate with Count:
Article.objects.extra({'published':"date(pub_date)"}).values('published').annotate(count=Count('id'))
Result will be:
published,count
2012-03-07,5
2012-03-08,9
2012-03-09,1

Django both filtered and total number of a certain field

I have such model and query
class Employer(Models.model)
name = ...
class JobTitle(Models.model)
name = ...
employer = models.ForeignKey(Employer)
and query is
Employer.objects.select_related('jobtitle')
.filter(jtt__activatedate__range=[startdate,enddate])
.annotate(jtt_count=Count('jobtitle'))
.order_by('-jtt_count')[:5]
As you see it returns 5 employer list which has maximum number of jobtitles which are related to that employer and whose activation date is in some certain range.
However, I also want to get the total number of jobtitles of each employer in that query.
Of course I may loop over each employer and make such query JobTitle.objects.filter(employer = emp) and taking length of that query but it is bad solution.
How can I achive this in that query?
Although it may not be possible to get both total number and filtered number of job titles, I may get the jobttiles of each emplyoer such that len(emp.jobtitle) however it also didn't work.
Thanks
Try the extra lookup. So, in your case it may be like this:
.extra(
select={
'jobtitle_count': 'SELECT COUNT(*) FROM YOURAPP_jobtitle WHERE YOURAPP_jobtitle.employer_id = YOURAPP_employer.id'
},
)