Django, get list of an attribute for multiple attributes - django

id_list = Foo.objects.values_list('id', flat=True)
name_list = Foo.objects.values_list('name', flat=True)
Can I get the two list conveniently, efficiently?

If by efficiently you mean in a single query, then you can get both attributes in one call and use zip to decompose them into separate lists:
values = Foo.objects.values_list('id', 'name')
id_list, name_list = zip(*values)

Related

Extract values from Django <QuerySet> in Python 3

I have some python code:
UnitTestCollection.objects.filter(unit__type=unit_type)
which outputs data in format:
<QuerySet [<UnitTestCollection: VALUE1>, <UnitTestCollection: VALUE2>...
How can I extract a list of the values only i.e.
[VALUE1, VALUE2....]
Why not a simple list comprehension?
qs = UnitTestCollection.objects.filter(unit__type=unit_type)
my_values = [item.field_name for item in qs]
Or use values_list() to just fetch the specific field for each item directly in your queryset, with the advantage of lazy evaluation:
qs = UnitTestCollection.objects.filter(unit__type=unit_type)\
.values_list('field_name', flat=True)
values_list is better than a list comprehension because querysets are lazily evaluated. They won't be executed until you actually need them.
queryset = UnitTestCollection.objects.filter(unit__type=unit_type).values_list('<insert field>', flat=True)
This will return [field1, field2, field3...]
if you use .values('<insert field>') you'll get [{field: field value1}, {field: field value2}, {field: field value3}...]

Django queryset get max id's for a filter

I want to get a list of max ids for a filter I have in Django
class Foo(models.Model):
name = models.CharField()
poo = models.CharField()
Foo.objects.filter(name__in=['foo','koo','too']).latest_by_id()
End result a queryset having only the latest objects by id for each name. How can I do that in Django?
Edit: I want multiple objects in the end result. Not just one object.
Edit1: Added __in. Once again I need only latest( as a result distinct) objects for each name.
Something like this.
my_id_list = [Foo.objects.filter(name=name).latest('id').id for name in ['foo','koo','too']]
Foo.objects.filter(id__in=my_id_list)
The above works. But I want a more concise way of doing it. Is it possible to do this in a single query/filter annotate combination?
you can try:
qs = Foo.objects.filter(name__in=['foo','koo','too'])
# Get list of max == last pk for your filter objects
max_pks = qs.annotate(mpk=Max('pk')).order_by().values_list('mpk', flat=True)
# after it filter your queryset by last pk
result = qs.filter(pk__in=max_pks)
If you are using PostgreSQL you can do the following
Foo.objects.order_by('name', '-id').distinct('name')
MySQL is more complicated since is lacks a DISTINCT ON clause. Here is the raw query that is very hard to force Django to generate from ORM function calls:
Foo.objects.raw("""
SELECT
*
FROM
`foo`
GROUP BY `foo`.`name`
ORDER BY `foo`.`name` ASC , `foo`.`id` DESC
""")

Boosting one queryset over another queryset

We are doing search in django and we have two query sets: one AND queryset and one OR queryset.
q_list = [Q(x) for x in predicates if x[1]]
_filter_AND = reduce(operator.and_, q_list)
_filter_OR = reduce(operator.or_, q_list)
_queryset_AND = queryset.filter(_filter_AND)
_queryset_OR = queryset.filter(_filter_OR).exclude(id__in=[c.id for c in _queryset_AND])
queryset = list(chain(_queryset_AND, _queryset_OR))
Now, when returning, data all the data is mixed. Also, I want to have the results from AND queryset to appear first.
Would anyone know how to do that in django?
Thanks in advance!

Filter on a distinct field with TastyPie

Suppose I have a Person model that has a first name field and a last name field. There will be many people who have the same first name. I want to write a TastyPie resource that allows me to get a list of the unique first names (without duplicates).
Using the Django model directly, you can do this easily by saying something like Person.objects.values("first_name").distinct(). How do I achieve the same thing with TastyPie?
Update
I've adapted the apply_filters method linked below to use the values before making the distinct call.
def apply_filters(self, request, applicable_filters):
qs = self.get_object_list(request).filter(**applicable_filters)
values = request.GET.get('values', '').split(',')
if values:
qs = qs.values(*values)
distinct = request.GET.get('distinct', False) == 'True'
if distinct:
qs = qs.distinct()
return qs
values returns dictionaries instead of model objects, so I don't think you need to override alter_list_data_to_serialize.
Original response
There is a nice solution to the distinct part of the problem here involving a light override of apply_filters.
I'm surprised I'm not seeing a slick way to filter which fields are returned, but you could implement that by overriding alter_list_data_to_serialize and deleting unwanted fields off the objects just before serialization.
def alter_list_data_to_serialize(self, request, data):
data = super(PersonResource, self).alter_list_data_to_serialize(request, data)
fields = request.GET.get('fields', None)
if fields is not None:
fields = fields.split(',')
# Data might be a bundle here. If so, operate on data.objects instead.
data = [
dict((k,v) for k,v in d.items() if k in fields)
for d in data
]
return data
Combine those two to use something like /api/v1/person/?distinct=True&values=first_name to get what you're after. That would work generally and would still work with additional filtering (&last_name=Jones).

Django: copy and manipulate a QuerySet?

I need to re-order a Django queryset, because I want to put None values at the bottom of an ORDER BY DESC query on a FloatField.
Unfortunately, I'm struggling to find an elegant way to manipulate a Django queryset. Here's what I have so far:
cities = City.objects.filter(country__id=country.id).order_by('value')
if cities.count() > 1:
cities_sorted = cities
del manors_sorted[:]
for city in cities:
cities_sorted += city
# Add code to
cities = cities_sorted
Currently, this fails with 'QuerySet' object does not support item deletion.
Any idea how I can copy and re-order this QuerySet to put None items last?
The queryset will be evaluated to a list, if you eg. call list() on it:
cities_sorted = list(cities)