How do I calculate the average difference between two dates in Django? - django

Using Django and Python 3.7. I'm tryhing to write a query to give me the average of the difference between two dates. I have two fields in my model, both "DateTimeField"s, and I try to calculate the average difference like so
everything_avg = Article.objects.aggregate(
avg_score=Avg(F('removed_date') - F('created_on'), output_field=models.DateTimeField())
).filter(removed_date__isnull=False)
return everything_avg
but I end up getting this error when running the above
AttributeError: 'dict' object has no attribute 'filter'
What's the right way to get my average?

As the documentation says:
aggregate() is a terminal clause for a QuerySet that, when invoked, returns a dictionary of name-value pairs. *
aggregate method returns a dictionary, thus you need to make your filtering before it. Thus if you alter your code as following you would get your result:
everything_avg = Article.objects.filter(removed_date__isnull=False)\
.aggregate(
avg_score=Avg(
F('removed_date') - F('created_on'),
output_field=models.DateTimeField()
)
)
return everything_avg

Related

Adding an annotation to django queryset to be used by django_tables2

I have a model that has (among other fields) one value for the current price of an item and one value for the usual price of an item. I'd like to include a field for the percentage saving. I've done this using the #property:
#property
def percSaving(self):
aval = self.stickerprice
bval = self.currentprice
if self.stickerprice > 0:
return "%0.2f" % ((aval-bval)/aval*100) + "%"
elif self.currentprice == 0:
return "Always Free"
else:
return "100% OFF"
This works, I can add this column to my django_table2 table with:
percSaving = tables.Column(verbose_name='% Saving')
Super easy and all good. However, I am unable to sort by this column. This is because it's not one of the columns of data from the query set. So I've been trying to annotate the query set to allow for this ordering I've based by annotation attempt on this queryset api reference page and have produced this:
annoed = products.objects.annotate(percSaving=((stickerprice)-(currentprice))/(stickerprice))
However this gives me an error of "name 'stickerprice' is not defined" which I thought might be because of not using inverted commas around the field names, but I tried that and got an error saying "unsupported operand type(s) for -: 'str' and 'str'" - basically using the inverted commas forces it to view the field names as strings.
What am I doing wrong? How can I annotate a query set to allow for ordering by a column I have defined as above!
Yes, you can. With providing some more code like Model etc. I can only make some general advise.
You should use F function to calculate in the query like:
annoed = products.objects.annotate(
percSaving=((F("stickerprice") - F("currentprice")) / F("stickerprice"))
)
Check also How to make sum query with type casting and calculation in django views? which provides also some type casting hints.

Is there any way I can avoid iterating over a query set with single value?

I get a queryset object every time i want some data from models.
So when i say,
"items = Items.object.get(value=value)"
I get --
"<QuerySet [<Item-name>]>"
I have to iterate through the queryset object to get the data and I do that with
"items[0]"
Is there any way I can avoid this?
Edit: I meant "items = Items.object.filter(value=value)"
first of all items = Items.objects.get(value=value) does not return a queryset,
rather it returns an object of <Items: Items object (1)>
To get the first(or just one result) or last date from the object, do this Items.objects.first() or Items.objects.last()
To get the desired data without using its index position, then you can filter it like this Items.objects.filter(value=value)
You are mistaken. items = Items.object.get(value=value) will not give you a queryset, but an object. items = Items.object.filter(value=value)
would give you a queryset.
Filter method will always give you a queryset, because; in order to minimize the need of database hits, django considers you might add additional filters through your code. So if you not execute that queryset, e.g. by using list(your_queryset) django never hits the database.
# when you are using 'get' in your query, you don't need to iterate, directly get an access to the field values
try:
items = Items.object.get(value=value)
except Items.DoesNotExist:
items = None
if items:
print(items.value)

Annotate and .get() in Django

In my database I have a table card linked to a table amount, 1 card can have many amounts.
So with django I try to get a specific card and aggregate the sum of all the amounts linked. But impossible to concatenate .get() and .annotate together.
I tried this:
last_year_amount = Card.objects.get(date__date=date(today.year - 1,
today.month, today.day)).annotate(total=Sum(Amount.amount))
But of course it raises an error ''Card' object has no attribute 'annotate''.
I understand that .get() return an object an not a QuerySet so .annotate doesn't exists as a method of this object.
But how to get this object with the annotate?
Thanks for your answers
Swap the order of annotate and get.
last_year_amount = Card.objects\
.annotate(total=Sum('amount'))\
.get(date__date=date(today.year - 1, today.month, today.day))
You could also use a filter().
last_year_amount = Card.objects\
.filter(date__date=date(today.year - 1, today.month, today.day))\
.annotate(total=Sum('amount'))\
.get()
The final get() with no arguments will raise an exception if the queryset doesn't contain exactly one item.
I tried, but had another error.
Finally I had an error in the Amount.amount and in the format of get.
The solution was:
last_year_amount = Card.objects.filter(date__date=date(today.year - 1, today.month, today.day)).annotate(total=Sum('amount__amount'))[:1].get()
Which works very fine
How about going in the reverse order:
Amount.objects.filter(card__date__date=date(today.year - 1, today.month, today.day)).values('card').annotate(total=Sum('amount'))
I assumed that Amount model has a foreign key to Card model with field name of card.

how to insert results of a queryset to a table in Django

Am trying to insert results of a queryset into another table in my views but I get the error:
TypeError at /trackdata/
int() argument must be a string or a number, not 'ValuesQuerySet'
The code is this:
def trackdata(request):
usbtrack=(Usb.history.values('evidence'))
recordscount=Usb.history.values('evidence').count()
update=Evidence_source(evidence=usbtrack,frequency=recordscount,datatype='tzworksusb')
update.save()
I'm not sure what history is and what you are trying to fetch because your QuerySet should be formed as Usb.objects.filter(evidence=request.POST['evidence']).values('evidence'), for example. See the docs.
Anyway, values('evidence') returns a list of dicts (e.g. [{'evidence': some_value,}]), so you'd need to change evidence=usbtrack to something like evidence=usbtrack[0]['evidence']. You should probably use try/except and other error checking code, as well as loops because QuerySets by definition return lists, even if there is only one row in the result set. Another alternative is values_list() with flat=True, which returns a list of your query results rather than dicts:
usbtrack = Usb.objects.filter(evidence=request.POST['evidence']).values_list('evidence', flat=True)
Evidence_source(evidence=usbtrack[0], frequency=usbtrac.count(), datatype='tzworksusb')
Finally, if I guessed your intentions correctly and you are passing 'evidence' in your request, you would simply just do this:
usbtrack_count = Usb.objects.filter(evidence=request.POST['evidence']).count()
Evidence_source(evidence=request.POST['evidence'], frequency=usbtrack_count, datatype='tzworksusb')

Django annotate with values: how to access values?

I have a model with "disposals" and a model with "salesman".
I want to get the average discount of everye salesman.
Here's what I'm trying:
sm = Disposal.objects.annotate(average_discount=Avg('discount')).values('average_discount','sm__fname','sm__lname').order_by('-discount')
for s in sm:
data[0] = data[0]+s.sm__fname+','+s.sm__lname+','+str(s.average_discount)
Now I get this error:
Disposal object has no attribute
sm__fname
The query runs fine when I execute it in the django shell - but how can I access the values?
Thank you very much!
Firstly, as the documentation says, using values gives you a list of dictionaries, not model objects. So each s doesn't have an attribute sm_whatever, it has a dictionary key. So try this:
s['sm__lname']
However, I must say that I don't see the need to use values here at all. You would be better off just getting the actual objects:
sm = Disposal.objects.annotate(average_discount=Avg('discount')).order_by('-discount')
and then accessing the relevant related objects normally: s.fname.