Overwrite field in queryset annotate - django

I have a django model with the fields name (string) and value (integer). Say I need to return a queryset of {name:..., value:...} objects, with each value doubled. What I am trying to do now is:
queryset.annotate(value=F('value') * 2)
However, django tells me that the field value already exists.
I also tried using extra:
queryset.annotate(value_double=F('value') * 2).extra(select={'value': 'value_double'})
but that also does not work since value_double is not a valid field.
Of course, in this case I could use something like queryset.extra(select={'value': 'value * 2'}), but I have some other, more complicated cases involving a lot of functions where I really don't want to write sql, but I'd rather find a solution in django. Again, in all of my cases, annotate works perfectly fine as long as I give the result a new name.

Say your model is XYZ with fields name and val. If you want val to contain val*2 use below queryset
x = XYZ.objects.values('name').annotate(val=F('val')*2)
print(x)
Result
<QuerySet [{'name': abc, 'val': 104},...............]
If you want queryset that return name,val and doubleval. You can use below query for same.
x = XYZ.objects.values('name','val',doubleval=F('val')*2)
print(x)
Result
<QuerySet [{'name': abc, 'val':52,'doubleval': 104},...............]
Hope this help.

Related

how to get a field name knowing id number in django

I have the following query set to get the subcategory name knowing the id number:
query_sc = Post_Sub_Category.objects.filter(id='1').values('sub_category_name')
it gave me the following output:
{'sub_category_name': 'car'}
how can I get only the car? I mean I need the output to be a car only 'the value not the dictionary.
Use values_list instead of values, here's a quote straight from the docs:
A common need is to get a specific field value of a certain model instance. To achieve that, use values_list() followed by a get() call:
>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'
It looks like you're looking for values_list. With values_list you can get only the values, and if you want a flat list you can do the following.
Post_Sub_Category.objects.filter(id='1').values_list('sub_category_name', flat=True)
And the result will be ["car"]

Django get count of each age

I have this model:
class User_Data(AbstractUser):
date_of_birth = models.DateField(null=True,blank=True)
city = models.CharField(max_length=255,default='',null=True,blank=True)
address = models.TextField(default='',null=True,blank=True)
gender = models.TextField(default='',null=True,blank=True)
And I need to run a django query to get the count of each age. Something like this:
Age || Count
10 || 100
11 || 50
and so on.....
Here is what I did with lambda:
usersAge = map(lambda x: calculate_age(x[0]), User_Data.objects.values_list('date_of_birth'))
users_age_data_source = [[x, usersAge.count(x)] for x in set(usersAge)]
users_age_data_source = sorted(users_age_data_source, key=itemgetter(0))
There's a few ways of doing this. I've had to do something very similar recently. This example works in Postgres.
Note: I've written the following code the way I have so that syntactically it works, and so that I can write between each step. But you can chain these together if you desire.
First we need to annotate the queryset to obtain the 'age' parameter. Since it's not stored as an integer, and can change daily, we can calculate it from the date of birth field by using the database's 'current_date' function:
ud = User_Data.objects.annotate(
age=RawSQL("""(DATE_PART('year', current_date) - DATE_PART('year', "app_userdata"."date_of_birth"))::integer""", []),
)
Note: you'll need to change the "app_userdata" part to match up with the table of your model. You can pick this out of the model's _meta, but this just depends if you want to make this portable or not. If you do, use a string .format() to replace it with what the model's _meta provides. If you don't care about that, just put the table name in there.
Now we pick the 'age' value out so that we get a ValuesQuerySet with just this field
ud = ud.values('age')
And then annotate THAT queryset with a count of age
ud = ud.annotate(
count=Count('age'),
)
At this point we have a ValuesQuerySet that has both 'age' and 'count' as fields. Order it so it comes out in a sensible way..
ud = ud.order_by('age')
And there you have it.
You must build up the queryset in this order otherwise you'll get some interesting results. i.e; you can't group all the annotates together, because the second one for count depends on the first, and as a kwargs dict has no notion of what order the kwargs were defined in, when the queryset does field/dependency checking, it will fail.
Hope this helps.
If you aren't using Postgres, the only thing you'll need to change is the RawSQL annotation to match whatever database engine it is that you're using. However that engine can get the year of a date, either from a field or from its built in "current date" function..providing you can get that out as an integer, it will work exactly the same way.

Filter data from queryset in Django wihtout returning the entire queryset

I'm trying get an attribute from a given object in Djano. I'm getting the value properly but I'm curious as if there is a better way to grab this data.
I'm getting the name attribute by using:
owner_name = Owner.objects.filter(id=id).values('name')
And it properly returns the name attribute I'm looking for, but it is in the form of:
<QuerySet [{'name': u'John Doe'}]>
How can I get it to just return "John Doe" instead of <QuerySet [{'name': u'John Doe'}]>?
Edit: I've found out that I can get the info I need by doing:
owner_name[0].get('name')
and it will return John Doe. Is there a better way to do this still just to get 1 attribute?
You want a values_list query with the flat=True parameter. From the docs:
A common need is to get a specific field value of a certain model instance. To achieve that, use values_list() followed by a get() call:
Entry.objects.values_list('headline', flat=True).get(pk=1)
Or in your case:
Owner.objects.values_list('name', flat=True).get(id=id)
Note that get will raise an exception if there is not exactly one matching result. If that's a possibility, say because you were filtering on something other than id or because there might be no matching object, you could catch the exceptions or you could work with the list of names returned and check its length.

How to rename fields of an annotated query?

I have the following query
a = Mainfee.objects.values('collected_by__username').
distinct().annotate(Sum('amount'))
The result looks like this
[{'collected_by__username': u'maindesk', 'amount__sum': 800}]
How can I rename the first key to a and second key to b?
I tried the following
m = Mainfee.objects.extra(select =
{'a':'collected_by__username'}).values('a').distinct().
annotate(Sum('amount'))
and received this
DatabaseError: no such column: collected_by__username
I also tried
m = Mainfee.objects.extra(select =
{'a':'collected_by__username'}).values('collected_by__username').distinct().
annotate(Sum('amount'))
and got
[{'collected_by__username': u'maindesk', 'amount__sum': 800}]
PS: I want to rename the second field too
You can change the dictionary key of the annotated value by using keyword arguments:
m = Mainfee.objects.values('collected_by__username').annotate(b=Sum('amount'))
[{'collected_by__username': u'maindesk', 'b': 800}]
There is no quick and easy way to rename a related field, though. You can convert it in Python, but you'll have to ask yourself if that is really necessary:
m = Mainfee.objects.values('collected_by__username').annotate(b=Sum('amount'))
m = [{'a': x['collected_by__username'], 'b': x['b']} for x in m]
Or maybe this is slightly faster:
m = Mainfee.objects.values_list('collected_by__username').annotate(Sum('amount'))
m = [{'a': x[0], 'b': x[1]} for x in m]
Both methods will of course force evaluation of the whole queryset and don't allow further filtering/ordering etc. through the queryset's methods.
Also note that the call to distinct() is redundant when you're using values() or values_list() together with annotate().
You can use an F() expression on collected_by__username:
An F() object represents the value of a model field or annotated
column. It makes it possible to refer to model field values and
perform database operations using them without actually having to pull
them out of the database into Python memory... F() can be used to create dynamic fields on your models...
If you do this inside an annotate clause, we can 'rename' the field to a. You don't need to use an F() expression when calculating the total. See below:
from django.db.models import F
(Mainfee.objects.values('collected_by__username')
.annotate(a=F('collected_by__username'), b=Sum('amount'))
.values('a', 'b'))
Making some assumptions about your model, this would produce something like:
<QuerySet [{'a': 'item1', 'b': 1234}, {'a': 'item2', 'b': 4321}]>
I'm not sure how to rename the first key, it seems there is a solution here: How to rename items in values() in Django? but it returns error in your case since the generated SQL would be:
SELECT collected_by__username AS a WHERE ...
And of course collected_by__username doesn't exist.
Note: this feature is requested here https://code.djangoproject.com/ticket/16735
The 2nd key can be renamed by using: annotate(b=Sum('amount')
So try this first:
m = (Mainfee.objects
.extra(select={'a': 'collected_by.username'}) # check your DB for the exact table and field name
.values('a')
.distinct()
.annotate(b=Sum('amount')))
I guess it would spit out an exception but it worths a try. Otherwise don't try to rename the first key for now since it would be very hacky anway.
Hope it helps.
Just in case someone stumbles on this page.
m = Mainfee.objects.extra(select = 'a':'collected_by.username'}).values('collected_by__username','a').distinct().annotate(b=Sum('amount'))

How to rename items in values() in Django?

I want to do pretty much the same like in this ticket at djangoproject.com, but with some additonal formatting. From this query
>>> MyModel.objects.values('cryptic_value_name')
[{'cryptic_value_name': 1}, {'cryptic_value_name': 2}]
I want to get something like that:
>>> MyModel.objects.values(renamed_value='cryptic_value_name')
[{'renamed_value': 1}, {'renamed_value': 2}]
Is there another, more builtin way or do I have to do this manually?
From django>=1.8 you can use annotate and F object
from django.db.models import F
MyModel.objects.annotate(renamed_value=F('cryptic_value_name')).values('renamed_value')
Also extra() is going to be deprecated, from the django docs:
This is an old API that we aim to deprecate at some point in the future. Use it only if you cannot express your query using other queryset methods. If you do need to use it, please file a ticket using the QuerySet.extra keyword with your use case (please check the list of existing tickets first) so that we can enhance the QuerySet API to allow removing extra(). We are no longer improving or fixing bugs for this method.
Without using any other manager method (tested on v3.0.4):
from django.db.models import F
MyModel.objects.values(renamed_value=F('cryptic_value_name'))
Excerpt from Django docs:
An F() object represents the value of a model field or annotated
column. It makes it possible to refer to model field values and
perform database operations using them without actually having to pull
them out of the database into Python memory.
It's a bit hacky, but you could use the extra method:
MyModel.objects.extra(
select={
'renamed_value': 'cryptic_value_name'
}
).values(
'renamed_value'
)
This basically does SELECT cryptic_value_name AS renamed_value in the SQL.
Another option, if you always want the renamed version but the db has the cryptic name, is to name your field with the new name but use db_column to refer to the original name in the db.
I am working with django 1.11.6
( And the key:value pair is opposite to that of accepted answer )
This is how i am making it work for my project
def json(university):
address = UniversityAddress.objects.filter(university=university)
address = address.extra(select={'city__state__country__name': 'country', 'city__state__name': 'state', 'city__name': 'city'})
address = address.values('country', 'state', "city", 'street', "postal_code").get()
return address
Note that adding simultanous objects.filter().extra().values() is same as above.
Try passing as kwargs:
MyModel.objects.annotate(**{'A B C':F('profile_id')}).values('A B C')
In my case, there were spaces and other special characters included in the key of each value in the result set so this did the trick.
Its more than simple if you want to rename few fields of the mode.
Try
projects = Project.objects.filter()
projects = [{'id': p.id, 'name': '%s (ID:%s)' % (p.department, p.id)} for p in projects]
Here i do not have a name field in the table, but i can get that after tweaking a little bit.