Django queryset How to retrieve related fields with annotate() - django

I have this table:
id supply event_date value_average
----------------------------------------
1 a 01-01-2018 5
2 b 02-01-2018 6
3 a 02-01-2018 7
4 b 03-01-2018 8
5 c 03-01-2018 9
I am trying to get the latest value for each supply based on event_date column. I can get the latest event, but I did not found a way to return the value_average as well.
values_average = Purchase.objects \
.values('supply') \
.annotate(last=Max('event_date')) \
.order_by()
current return:
a 02-01-2018
b 03-01-2018
c 03-01-2018
expected return:
a 02-01-2018 7
b 03-01-2018 8
c 03-01-2018 9

I found a way to do that by following this answer:
Django: select values with max timestamps or join to the same table
values_average = Purchase.objects \
.filter(farm=farm, supply__in=queryset) \
.order_by('supply', '-event_date') \
.distinct('supply')
It will only work with Postgres. The final result will be a normal queryset with the latest events. Just take care if your model has Meta ordering.
Django docs on this:
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.distinct

I think you just have to add the value_average attribute to the values you want to return :
values_average= Purchase.objects.values('supply','value_average').annotate(last=Max('event_date'))

Related

How can I do to filter on max values using Django ORM?

I have that kind of entries :
id user number
1 Peter 1
2 Jack 3
3 Kate 2
4 Carla 3
The name of my table is User so I would like to get only the user with the highest number but in some cases I don't know this number.
I thought to do something like that :
max_users = User.objects.filter(number=3)
But the problem is in that case I suppose I know that the highest number is 3 whereas it is not always the case. Could you help me please ?
Thank you very much !
Try the following snippet:
from django.db.models import Max
max_number = User.objects.aggregate(Max('number'))['number__max'] # Returns the highest number.
max_users = User.objects.filter(number=max_number) # Filter all users by this number.

Django ORM. Select only duplicated fields from DB

I have table in DB like this:
MyTableWithValues
id | user(fk to Users) | value(fk to Values) | text | something1 | something2 ...
1 | userobject1 | valueobject1 |asdasdasdasd| 123 | 12321
2 | userobject2 | valueobject50 |QWQWQWQWQWQW| 515 | 5555455
3 | userobject1 | valueobject1 |asdasdasdasd| 12345 | 123213
I need to delete all objects where are repeated fields user, value and text, but save one from them. In this example will be deleted 3rd record.
How can I do this, using Django ORM?
PS:
try this:
recs = (
MyTableWithValues.objects
.order_by()
.annotate(max_id=Max('id'), count_id=Count('user__id'))
#.filter(count_id__gt=1)
.annotate(count_values=Count('values'))
#.filter(count_icd__gt=1)
)
...
...
for r in recs:
print(r.id, r.count_id, , r.count_values)
it prints something like this:
1 1 1
2 1 1
3 1 1
...
Dispite the fact, that in database there are duplicated values. I cant understand, why Count function does not work.
Can anybody help me?
You should first be aware of how count works.
The Count method will count for identical rows.
It uses all the fields available in an object to check if it is identical with fields of other rows or not.
So in current situation the count_values is resulting 1 because Count is using all fields excluding id to look for similar rows.
Count is including user,value,text,something1,something2 fields to check for similarity.
To count rows with similar fields you have to use only user,values & text field
Query:
recs = MyTableWithValues.objects
.values('user','values','text')
.annotate(max_id=Max('id'),count_id=Count('user__id'))
.annotate(count_values=Count('values'))
It will return a list of dictionary
print(recs)
Output:
<QuerySet[{'user':1,'values':1,'text':'asdasdasdasd','max_id':3,'count_id':2,'count_values':2},{'user':2,'values':2,'text':'QWQWQWQWQWQW','max_id':2,'count_id':1,'count_values':1}]
using this queryset you can check how many times a row contains user,values & text field with same values
Would a Python loop work for you?
import collections
d = collections.defaultdict(list)
# group all objects by the key
for e in MyTableWithValues.objects.all():
k = (e.user_id, e.value_id, e.text)
d[k].append(e)
for k, obj_list in d.items():
if len(obj_list) > 1:
for e in obj_list[1:]:
# except the first one, delete all objects
e.delete()

How to get the latest value of one filed(Django Model.objects.filter())

I have models as below:
class ProjectRecord(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE, null=True, blank=True,related_name='user_projects')
project = models.ForeignKey(Project,on_delete=models.CASCADE, null=True, blank=True, related_name='user_projects')
project_type = models.CharField(max_length=30, choices=(("project_a","A"),("project_b","B")),null=True)
version = models.FloatField(null=True, blank=True)
I want to filter the latest value of version with this: project = list(ProjectRecord.objects.filter(user=self.request.user, project_type='project_a')) , but I don't know how to achieve it.the data in database is similar as below:
id project_id version project_type
1 5 1.0 project_a
2 5 1.0 project_b
3 4 1.0 project_a
4 4 1.0 project_b
5 5 2.0 project_a
6 5 2.0 project_b
7 5 3.0 project_a
8 5 3.0 project_b
For example, I want to get the latest value of project_id=5 to exact match other data and do not delete other project_id's value if their versions are not updated, the queryset should be display as below
id project_id version project_type
1 4 1.0 project_a
2 4 1.0 project_b
3 5 3.0 project_a
4 5 3.0 project_b
Thanks so much for any advice and assistance.
Or you can try with this:
from django.db.models import Max
queryset = ProjectRecord.objects.filter(user=self.request.user) \
.values('project_id', 'project_type') \
.annotate(max_version=Max('version')
Try this,
from django.db.models import Max
queryset = ProjectRecord.objects.filter(user=self.request.user, project_type='project_a')
max_version = queryset.aggregate(max=Max('version')).get('max')
required_queryset = queryset.filter(version=max_version)
I'm not sure about the performance, but this will definitely work
You could order_by('version') and then take latest object and watch if the second one have the same ver. so you take and it in and the third ...

django - OR query using lambda

I want to perform OR query using django ORM. I referred this answer and it fits my need.
I have a list of integers which gets generated dynamically. These integers represent user id in a particular table. This table also has a date field. I want to query the database for all user ids in the list for a given date.
For example: From below table, I want records for user ids 2 and 3 for the date 2015-02-28
id | date
---------------
1 | 2015-02-23
1 | 2015-02-25
1 | 2015-02-28
2 | 2015-02-28
2 | 2015-03-01
3 | 2015-02-28
I am unable to figure out which of the following two should be perfect for my use case:
Table.objects.filter(reduce(lambda x, y: (x | y) & Q(date=datetime.date(2015, 2, 28)), [Q(user_id=i) for i in ids])
OR
Table.objects.filter(reduce(lambda x, y: (x | y), [Q(user_id=i) for i in ids]) & Q(date=datetime.date(2015, 2, 28))
Both of the above yield similar output at the moment. Without lambda, below query would fit my need:
Table.objects.filter(Q(user_id=3) & Q(date=datetime.date(2015, 2, 28))| Q(user_id=2) & Q(date=datetime.date(2015, 2, 28)))
I think you do not need reduce and Q objects here, you can just do:
Table.objects.filter(
user_id__in=[2,3],
date=datetime.date(2015, 2, 28),
)

Django group by in many to many relationships

I have a model named Evaluation with following schema:
user = models.ForeignKey(User)
value = models.IntegerField()
The value field will take value in 0,1,2,3.
Now I want to get the count of evaluations of a given user with each value. For example, suppose my data are:
user.id | value
1 | 0
1 | 0
1 | 1
1 | 2
1 | 3
1 | 3
I want to get the result
value | count
0 | 2
1 | 1
2 | 1
3 | 2
I use the query
Evaluation.objects.filter(user=request.user).annotate(count=Count('value')).order_by('value')
But it does not return the correct answer. Could anyone help?
you can do it this way:
Evaluation.objects.filter(user=request.user).values('value').annotate(count=Count('value')).order_by('value')
Add the values() method:
Evaluation.objects.filter(user_id=request.user) \
.values('value').annotate(count=Count('value')) \
.order_by('value')
You could build reverse query and query the User model instead:
User.objects.filter(user=request.user).values('evaluation__value').annotate(count=Count('evaluation__user'))
which will produce below results:
[{'count': 1, 'evaluation__value': 1}, {'count': 1, 'evaluation__value': 2}, {'count': 2, 'evaluation__value': 0}, {'count': 2, 'evaluation__value': 3}]
Additionally you might want to sort the results:
queryset.order_by('-count') # sorts by count desc
Unfortunately you cannot alias the value in values queryset method hence the ugly evaluation__value as field name. See this Django ticket.
HTH.