I got two models Article and Author implemented like this:
class Author(models.Model):
name = models.CharField(max_length=50)
class Article(models.Model):
name = models.CharField(max_length=50)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
pub_date = models.DateField()
On my template I want to plot a graph (using chart.js on the frontend side) showing the authors publications per month, for the last 12 months. For that I need the count of articles published each month.
This is the query to get the authors articles:
articles = Article.objects.filter(author=author)
What would be best practice to get the count per month?
I've thought about annotating each of the twelve month separately to the QuerySet, but haven't found a way that works for me.
Alternatively, I thought about dealing with this on the JS site in the users browser.
Any suggestions/recommendations?
If you need to group data in Django you'll need to use ORM aggregation features.
And to use that on dates you can leverage their ORM helper functions.
from django.db.models.functions import TruncMonth
from django.db.models import Count
Article.objects
.filter(author=author) # Filter by the author
.annotate(month=TruncMonth('created')) # Truncate by month and add 'month' to values
.values('month') # Group By month
.annotate(count_id=Count('id')) # Count the number of articles in the grouping
.order_by('-month')[:12] # Sort by months in descending order (latest to earliest) and get the 12 first results
Related
My django model:
class Movimentacao(models.Model):
product = models.CharField(max_length=300)
value = models.DecimalField(max_digits=19, decimal_places=2)
I'm trying to SUM all value by product using django annotate. I tried:
query = Movimentacao.objects.annotate(total=Sum('value')
).values('product', 'total'
).order_by('product')
There are a lot of repeated products (multiple lines) with different values and I'd like to group all same products and sum their respective values.
But it's showing all records without grouping and summing all of them.
I wouldn't like to use aggregate in this case.
Tks
You use .values(…) [Django-doc] first:
from django.db.models import Sum
query = (
Movimentacao.objects.values('product')
.annotate(total=Sum('value'))
.order_by('product')
)
That being said, if the same product occurs multiple times, it is normally best practice to make a Product model and work with a ForeignKey.
I have a puzzle on my hands. As an exercise, I am trying to write a queryset that helps me visualize which of my professional contacts I should prioritize corresponding with.
To this end I have a couple of models:
class Person(models.Model):
name = models.CharField(max_length=256)
email = models.EmailField(blank=True, null=True)
target_contact_interval = models.IntegerField(default=45)
class ContactInstance(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='contacts')
date = models.DateField()
notes = models.TextField(blank=True, null=True)
The column target_contact_interval on the Person model generally specifies the maximum amount of days that should pass before I reach out to this person again.
A ContactInstance reflects a single point of contact with a Person. A Person can have a reverse relationship with many ContactInstance objects.
So, the first Person in the queryset should own the greatest difference between the date of the most recent ContactInstance related to it and its own target_contact_interval
So my dream function would look something like:
Person.objects.order_by(contact__latest__date__day - timedelta(days=F(target_contact_interval))
but of course that won't work for a variety of reasons.
I'm sure someone could write up some raw PostgreSQL for this, but I am really curious to know if there is a way to accomplish it using only the Django ORM.
Here are the pieces I've found so far, but I'm having trouble putting them together.
I might be able to use a Subquery to annotate the date of the most recent datapoint:
from django.db.models import OuterRef, Subquery
latest = ContactInstance.objects.filter(person=OuterRef('pk')).order_by('-date')
Person.objects.annotate(latest_contact_date=Subquery(latest.values('date')[:1]))
And I like the idea of sorting the null values at the end:
from django.db.models import F
Person.objects.order_by(F('last_contacted').desc(nulls_last=True))
But I don't know where to go from here. I've been trying to put everything into order_by(), but I can't discern if it is possible to use F() with annotated values or with timedelta in my case.
UPDATE:
I have changed the target_contact_interval model to a DurationField as suggested. Here is the query I am attempting to use:
ci = ContactInstance.objects.filter(
person=OuterRef('pk')
).order_by('-date')
Person.objects.annotate(
latest_contact_date=Subquery(ci.values('date'[:1])
).order_by((
(datetime.today().date() - F('latest_contact_date')) -
F('target_contact_interval')
).desc(nulls_last=True))
It seems to me that this should work, however, the queryset is still not ordering correctly.
I'm not clear from the docs what an "=" filter condition means when used directly on a ManyToManyField.
For example, if I have:
class Publication(models.Model):
title = models.CharField(max_length=30)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
What does the .filter(publications=pub) do here:
pub = Publication.objects.get(id=1) # or any other Publication
Article.objects.filter(publications=pub)
Will it select articles that have exactly/only this publication?
Will it select articles that have at least this publication, among others?
And what does the .filter(publications=pubs) mean here:
pubs = Publication.objects.filter(id__in=[1,2]) # or any other subset query of Publication
Article.objects.filter(publications=pubs)
Will it select articles that have exactly/only this subset of publications?
Will it select articles that have at least all of the publications, among others?
Will it select articles that have at least one of these publications?
pub = Publication.objects.get(id=1) # or any other Publication
Article.objects.filter(publications=pub)
This first part means that it will get all articles that are at least related to the publication with id=1.
pubs = Publication.objects.filter(id__in=[1,2]) # or any other subset query of Publication
Article.objects.filter(publications=pubs)
This second part will get all articles with publications with either id=1 or id=2.
For more information, try the django docs.
following this question:
Count number of records by date in Django
class Review(models.Model):
venue = models.ForeignKey(Venue, db_index=True)
review = models.TextField()
datetime_visited = models.DateTimeField(default=datetime.now)
It is true that the following line solves the problem of count number of records by date:
Review.objects.filter
.extra({'date_visited' : "date(datetime_visisted)"})
.values('date_visited')
.annotate(visited_count=Count('id'))
However, say I would like to have a distinct count, that is, I would like to avoid Review objects from the same id on the same day, what can I do?
I tried:
Review.objects.filter.
.extra({'date_visited': "date(datetime_visited)"})
.values('date_visited', 'id')
.distinct()
.annotate(Count('id'))
but it seems not working
Your problem is that you're including id in your values(), which is making all records unique, defeating distinct(). Try this instead:
Review.objects.filter.
.extra({'date_visited': "date(datetime_visited)"})
.values('date_visited')
.distinct()
.annotate(Count('date_visited'))
I need to create a report in which I can get the value for each month of the year.
models.py
class Ask(models.Model):
team = models.CharField(max_length=255)
date = models.DateField()
In team are only three possible values: A, B, C.
How do I calculate how many times a month was added individual team?
I would like to generate a report for each year.
I suggest you to add month field to your model and pass there month on save. This is help you to run this:
from django.db.models import Count
Ask.objects.filter(date__year='2013').values('month', 'team').annotate(total=Count('team'))
Other method is to use extra parameter and extract month from date field:
from django.db.models import Count
Ask.objects.filter(date__year='2013').extra(select={'month': "EXTRACT(month FROM date)"}).values('month', 'team').annotate(Count('team'))
but EXTRACT method is database dependent and for example dont`t work in SQLite