Django - Get Value Only from queryset - django

When the docs discuss values() and values_list(), or any query for that matter, they always require that you know what you are looking for, i.e.,
>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'
What about the situation where you need a value from a specific field, whether on this model or a foreign key, but you don't know the pk or the value in the specified field, and you don't care, you just need whatever is there. How do you query for it?
Alternatively, if I use this example from the docs:
>>> Entry.objects.values_list('id', flat=True).order_by('id')
<QuerySet [1, 2, 3, ...]>
Could I add slice notation to the end of the query? But even then, I might not know in advance which slice I need. In other words, how to dynamically get a value from a specified field without knowing in advance what it or its pk is? Thx.

Depending your scenario (this time a simple query) you have many of options to do it. One is to use a variable as field name. Then, feed that variable dynamically:
>>> field='headline'
>>> Entry.objects.values_list(field, flat=True).get(pk=1)
'First entry'
>>> field='body'
>>> Entry.objects.values_list(field, flat=True).get(pk=1)
'First entry body'
In order to slice results use offset/limit as follows:
Entry.objects.all()[offset:limit]
>>> field='headline'
>>> Entry.objects.values_list(field, flat=True)[5:10]

Related

Overwrite field in queryset annotate

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.

Django distinct on case sensitive entries

I have the following query:
>>> z = Restaurant.objects.values_list('city',flat=True).order_by('city').distinct()
>>> z
[u'ELURU', u'Eluru', u'Hyderabad']
As you can see, it is not completely distinct because of the case sensitivity. How do i correct this issue?
You can use annotate in conjunction with Lower (or Upper, etc...) to normalize your values and return truly distinct values like this...
from django.db.models.functions import Lower
z = Restaurant.objects.annotate(
city_lower=Lower('city')).values_list(
'city_lower',flat=True).order_by('city_lower').distinct()
Note: Make sure order_by is set to 'city_lower' and not 'city' to avoid duplicates.
I'm not sure you're going to find a solution to this since django doesn't offer a case-insensitive distinct method (currently). But then maybe it would be better to fix the values in your database anyway since you don't really want your end users to see their city in capitals since it will look ugly.
I'd suggest thinking about making a simple method that you could run either once in a data migration and stopping the city field from ever getting in this state again - or just running this periodically.
something similar to
for restaurant in Restaurant.objects.all():
if restaurant.city != restaurant.city.title():
restaurant.city = restaurant.city.title()
restaurant.save()
Try this;
z = Restaurant.objects.extra(select = {'tmp_city': lower('city')}).values_list('city',flat=True).order_by('city').distinct('tmp_city')
This works, although it is a little messy. I ended up having to use values, since distinct only works on database tables, regardless of whether or not you use annotate, extra, or rawSQL.
You end up creating an extra field with annotate, and then use that field in your list of dictionaries created by values. Once you have that list of dictionaries, you can use groupby to group dictionaries based on the Lower values key in the values list of dicts. Then, depending on how you want to select the object (in this case, just taking the first object of the group), you can select the version of the distinct that you want.
from django.db.models.functions import Lower
from itertools import groupby
restaurant = [g.next() for k, g in groupby(
list(
Restaurant.objects.annotate(city_lower=Lower('message_text')).values_list('city', flat=True)
).order_by('city').values('city_lower', 'city')
), lambda x: x['city_lower'])]

How can I retrieve a list of field for all objects in Django?

I want to retrieve a list of all of the values for one field from a query in django. For example, I have a query of users, but rather than a queryset (or list) of user objects, I want a list just the usernames (strings). In a sense this is asking to restrict only to one column of data.
Have you tried
list(User.objects.all().values_list('username', flat=True))
If you only pass in a single field, you can also pass in the flat parameter. If True, this will mean the returned results are single values, rather than one-tuples. Additionally, casting it to a list makes the returned value a list instead of a queryset
To get the list of usernames:
>>> User.objects.all().values('username')
>>> [{'username': u'u1'}, {'username': u'u2'}]
>>> User.objects.all().values_list('username')
>>> [(u'u1',), (u'u2',)]
If you want just strings, a list comprehension can do the trick:
>>> usr_names = User.objects.all().values('username')
>>> [u['username'] for u in usr_names]
>>> [u'u1', u'u2']
Using values_list:
>>> usr_names = User.objects.all().values_list('username')
>>> [u[0] for u in usr_names]
>>> [u'u1', u'u2']
As you wrote:
"but rather than a queryset (or list) of user objects"
Soulution above still queryset
usr_names = User.objects.all().values_list('username')
This solution:
usr_names = [str(elem) for elem in list(User.objects.all().values_list('username'))]
It will return a list of strings
The simplest way you can get the list of objects of an attribute is to first get a query-set of that attribute alone using values_list then converting the django query-set to a python set using set() and finally to a list using list().
In your scenario:
user_names = list(set(User.objects.all().values_list('username', flat=True)))
You replace 'field_name' with your own
users_name = [i.field_name for i in Users.objects.all()]
To get the list and easily understands it since value list returns tuples in a list
users= User.objects.all().values_list("username")
usernames= []
for item in categories:
usernames.append(item[0])

Is it possible to use Django F() expressions for atomic updates together with full_clean()?

I've just discovered that fields assigned with Django F() expressions fail to validate. I modified example from the Django doc:
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.full_clean() # My addition.
>>> product.save()
And I'm getting: ValidationError: {'number_sold': [u"'(+: (DEFAULT: ), 0)' value must be an integer."]}. Indeed, number sold is not an integer, but an instance of django.db.models.expressions.ExpressionNode.
Is there a way around this? All my models extend a base class that automatically calls full_clean() on each save and I really like to keep this base class but be able to use atomic updates.
You problem is that you are trying to save a non integer value in an integer field.
The field number_sold is expected an integer in your case.
I would assume that F('number_sold') references an empty field.
Try to replace F('number_sold') + 1 with something hardcoded such as 1.
If that work, let me know and I can supply logic to avoid your error.

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.