I have three models:
class Ticket(models.Model):
date = models.DateTimeField(default=datetime.datetime.now, blank=True)
subject = models.CharField(max_length=256)
description = models.TextField()
class Comments(models.Model):
date = models.DateTimeField(default=datetime.datetime.now, blank=True)
comment = models.TextField()
action = models.ForeignKey(Label, on_delete=models.CASCADE, limit_choices_to={'group__name': 'action'}, related_name='action_label')
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
class Label(models.Model):
name = models.CharField(max_length=50)
group = models.ForeignKey(LabelGroup, on_delete=models.CASCADE)
My goal is to get an average timespan between Ticket.date (opened) and the latest Comment.date of type Label.name = containment. Lastly, I want to pass in a date range (start_date, end_date) to limit to only tickets within that time period.
Ultimately, I want to return:
{
avg_time_to_contain: 37,
avg_time_to_recover: 157
}
from a DRF view.
The query set I have so far is:
queryset = Comments.objects.filter(ticket__date__gte=start_date, ticket__date__lte=end_date).filter(action__name__icontains="containment").distinct(ticket).aggregate(avg_containment=Avg(F(date)- F(ticket__date)))
I am getting an error, saying incident is not defined. I believe that is due to my distinct function but I am having a hard time getting my brain to translate into what I am after.
Pseudo code for the query (in my thinking),
Grab comments that have tickets (FK) with dates between start / end date
Filter by the action (FK) name having the work containment
Just grab one distinct ticket at a time (feel like I am not doing this right, basically I want to just have the last entry for each ticket to be able to pull the diff between open & last containment comment, and then average the difference in time)
Get the average of the containment date comment - the open date.
ERROR MSG:
NameError at /api/timetocontain/
name 'ticket' is not defined
Request Method: GET Request URL: http://127.0.0.1/api/timetocontain/
Django Version: 4.1.3 Exception Type: NameError Exception Value:
name 'ticket' is not defined
Related
I've been scouting & testing for quite some time now and I'm unable to get anything working with MariaDB.
My models:
class SequenceCompletion(SequenceMixin, models.Model):
pass
class Play(SequenceMixin, models.Model):
puzzle = models.ForeignKey(
'puzzles.Puzzle', on_delete=models.CASCADE, related_name='plays')
rating = models.IntegerField(default=-1)
completion = models.ForeignKey(SequenceCompletion, blank=True,
null=True, on_delete=models.SET_NULL, related_name='plays')
I have a subquery:
rating_average_by_puzzle_from_completion_plays = Play.objects.filter(completion_id=OuterRef('id')).values('puzzle_id').order_by('puzzle_id').annotate(puzzle_rating_average=Avg('rating')).values('puzzle_rating_average')
where the ratings of all plays corresponding to a given completion are averaged per puzzle_id
Afterwards, I'm trying, for each completion, to average the puzzle_rating_average corresponding to each puzzle_id:
if I do annotate, I end up with :
SequenceCompletion.objects.annotate(rating_avg_from_completion_plays=Subquery(rating_average_by_puzzle_from_completion_plays.annotate(result=Avg('puzzle_rating_average')).order_by().values('result')))
django.core.exceptions.FieldError: Cannot compute Avg('puzzle_rating_average'): 'puzzle_rating_average' is an aggregate
if I do aggregate, I end up with :
SequenceCompletion.objects.annotate(rating_avg_from_completion_plays=Subquery(rating_average_by_puzzle_from_completion_plays.aggregate(result=Avg('puzzle_rating_average')).order_by().values('result')))
ValueError: This queryset contains a reference to an outer query and may only be used in a subquery.
the same if I add completion_id to the subquery:
rating_average_by_puzzle_from_completion_plays = Play.objects.filter(completion_id=OuterRef('id')).values('puzzle_id').order_by('puzzle_id').annotate(puzzle_rating_average=Avg('rating')).values('puzzle_rating_average','completion_id')
I tried as well:
SequenceCompletion.objects.annotate(rating_avg_from_completion_plays=Avg(Subquery(rating_average_by_puzzle_from_completion_plays)))
and I end up with:
MySQLdb._exceptions.OperationalError: (1242, 'Subquery returns more than 1 row'
Nothing I could find on the documentation or in any post would work, so I'll appreciate any help
Please see EDIT1 below, as well.
Using Django 3.0.6 and python3.8, given following models
class Plants(models.Model):
plantid = models.TextField(primary_key=True, unique=True)
class Pollutions(models.Model):
pollutionsid = models.IntegerField(unique=True, primary_key=True)
year = models.IntegerField()
plantid = models.ForeignKey(Plants, models.DO_NOTHING, db_column='plantid')
pollutant = models.TextField()
releasesto = models.TextField(blank=True, null=True)
amount = models.FloatField(db_column="amount", blank=True, null=True)
class Meta:
managed = False
db_table = 'pollutions'
unique_together = (('plantid', 'releasesto', 'pollutant', 'year'))
class Monthp(models.Model):
monthpid = models.IntegerField(unique=True, primary_key=True)
year = models.IntegerField()
month = models.IntegerField()
plantid = models.ForeignKey(Plants, models.DO_NOTHING, db_column='plantid')
power = models.IntegerField(null=False)
class Meta:
managed = False
db_table = 'monthp'
unique_together = ('plantid', 'year', 'month')
I'd like to annotate - based on a foreign key relationship and a fiter a value, particulary - to each plant the amount of co2 and the Sum of its power for a given year. For sake of debugging having replaced Sum by Count using the following query:
annotated = tmp.all().annotate(
energy=Count('monthp__power', filter=Q(monthp__year=YEAR)),
co2=Count('pollutions__amount', filter=Q(pollutions__year=YEAR, pollutions__pollutant="CO2", pollutions__releasesto="Air")))
However this returns too many items (a wrong number using Sum, respectively)
annotated.first().co2 # 60, but it should be 1
annotated.first().energy # 252, but it should be 1
although my database guarantees - as denoted, that (plantid, year, month) and (plantid, releasesto, pollutant, year) are unique together, which can easily be demonstrated:
pl = annotated.first().plantid
testplant = Plants.objects.get(pk=pl) # plant object
pco2 = Pollutions.objects.filter(plantid=testplant, year=YEAR, pollutant="CO2", releasesto="Air")
len(pco2) # 1, as expected
Why does django return to many results and how can I tell django to limit the elements to annotate to the 'current primary key' in other words to only annotate the elements where the foreign key matches the primary key?
I can achieve what I intend to do by using distinct and Max:
energy=Sum('yearly__power', distinct=True, filter=Q(yearly__year=YEAR)),
co2=Max('pollutions__amount', ...
However the performance is inacceptable.
I have tested to use model_to_dict and appending the wanted values "by hand" to the dict, which works for the values itself, but not for sorting the resulted dict (e.g. by energy) and it is acutally faster than the workaround directly above.
It conceptually strikes to me that the manual approach is faster than letting the database do, what it is intended to do.
Is this a feature limitation of django's orm or am I missing something?
EDIT1:
The behaviour is known as bug since 11 years.
Even others "spent a whole day on this".
I am now trying it with subqueries. However the forein key I am using is not a primary key of its table. So the kind of "usual" approach to use "pk=''" does not work. More clearly, trying:
tmp = Plants.objects.filter(somefilter)
subq1 = Subquery(Yearly.objects.filter(pk=OuterRef('plantid'), year=YEAR)) tmp1 = tmp.all().annotate(
energy=Count(Subquery(subq1))
)
returns
OperationalError at /xyz
no such column: U0.yid
Which definitely makes sense because Plants has no clue what a yid is, it only knows plantids. How do I adjust the subquery to that?
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'))
How to get only recently added Order?
class Order(models.Model):
STATUSS = (
(u'E', u'Expected'),
(u'S', u'Sent'),
(u'F', u'Finished'),
)
who = models.ForeignKey(User, related_name='Owner')
products = models.TextField()
date = models.DateTimeField(auto_now_add=True)
send = models.ForeignKey(Send)
status = models.CharField(max_length=2, null=True, choices=STATUSS, default='O')
I prefer auto-increment pk, then
Order.objects.latest('pk')
It's simpler, indexed and is ready as long as the default surrogate primary key is used.
If by recently you mean the most recent order regarding the date it was added then you can use:
Order.objects.order_by('-date')[0]
If your definition of "recently" is "the last added", you can use latest()
Order.objects.latest('date')
or just
Order.objects.latest()
if you have
class Meta:
get_latest_by = 'date'
in your model. This is from the django docs, https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.latest
If you want more than one of the most recent, say everything from the last 5 days:
import datetime
Order.objects.filter(date__gte=datetime.date.today() - datetime.timedelta(days=5))
or if you want the last 10 records regardless of how recent, then:
Order.objects.order_by('-date')[10]
In django, given the following models (slightly simplified), I'm struggling to work out how I would get a view including sums of groups
class Client(models.Model):
api_key = models.CharField(unique=True, max_length=250, primary_key=True)
name = models.CharField(unique=True, max_length=250)
class Purchase(models.Model):
purchase_date = models.DateTimeField()
client = models.ForeignKey(SavedClient, to_field='api_key')
amount_to_invoice = models.FloatField(null=True)
For a given month, I'd like to see e.g.
April 2010
For Each Client that purchased this month:
* CLient: Name
* Total amount of Purchases for this month
* Total cost of purchases for this month
For each Purchase made by client:
* Date
* Amount
* Etc
I've been looking into django annotation, but can't get my head around how to sum values of a field for a particular group over a particular month and send the information to a template as a variable/tag.
Any info would be appreciated
You could use aggregate instead of annotate: http://docs.djangoproject.com/en/dev/topics/db/aggregation/#topics-db-aggregation
It could be handy to add a Client method:
class Client(models.Model):
api_key = models.CharField(unique=True, max_length=250, primary_key=True)
name = models.CharField(unique=True, max_length=250)
def get_purchases_month(self, y, m):
return self.purchase_set.filter(purchase_date__year=y, purchase_date__month=m).aggregate(purchase_sum=Sum("amount_to_invoice"))
You can't use it directly from the template as there's no way to pass parameters from there. At least it would be easy to use in some view (for example www.domain.com/yourview/2010/03/)
y, m = 2010, 10
clients = []
for c in Client.objects.all():
clients.append({'client':c, 'purchases':c.get_purchases_month(y, m)})
And you pass clients list to your template and loop trough it there. It's not quite effective way but simple ;-)
{% for c in clients %}
{{c.client.name}}
{{c.purchases_month.purchase_sum}}
{{c.purchases_month.all|length}}
{% endfor %}
purchases_month.all because it's a queryset and you have to use all() to get items, and `|length' to count these items
(had to put as another answer because code in comment apparently can't be formatted)