Why won't my django queryset work? - django

I am trying to run a queryset with the value of the filter being the values inside a dictionary.
latest_entries = Entry.objects.filter(zipcode__in=nearestzips.values())
print latest_entries
>>>TypeError: Cannot use a multi-field GeoValuesQuerySet as a filter value.
second attempt
latest_entries = Entry.objects.filter(zipcode__in=nearestzips.values_list('code', flat=True))
print latest_entries
>>>ProgrammingError: invalid reference to FROM-clause entry for table "cities_postalcode" HINT perhaps you meant to reference the table alias"u0"
How can I accomplish this? Should I just take the extra step of creating a new list and appending the dictionary values into the list? And then run the queryset on the list? I'm not sure what to do.
EDIT:
when I print nearestzips I get:
[<PostalCode:97201>,<PostalCode:97202>]
BUT, when I print nearestzips.values(), I get:
[distance:0, code: 97201, name: Portland, subregion: Multnomah] etc.

Looks like the nearestzips is a subclass of the QuerySet which is lost some compatibility. Try to convert values_list() to the simple python list:
zip_codes = list(nearestzips.values_list('code', flat=True))
latest_entries = Entry.objects.filter(zipcode__in=zip_codes)

Related

How to insert a value from a dict in a django annotation using each id from queryset [duplicate]

This question already has an answer here:
Django annotate field value from external dictionary
(1 answer)
Closed last month.
I have a simple dict containing ids with values associated like: {1:True, 2:False}
My model:
class Profile(models.Model):
user_id = models.IntegerField(unique=True, blank=True, null=True)
I also have a queryset that have the same ids listed above in an atribute named user_id, but I need to add a new field to my objects on queryset with the values from my dict.
Lets suppose the field I want to add is called is_admin, so I need to create a field on the object with id 1 on my queryset with the value True.
What I tried to do is:
my_dict= {1:True, 2:False}
queryset = queryset.annotate(
is_admin=Value(my_dict.get(F("user_id")), output_field=BooleanField())
)
But what happen is that I'm receiving null on is_admin. I tried to do the code below and it works:
queryset = queryset.annotate(
is_admin=Value(my_dict.get(1), output_field=BooleanField())
)
So I think is something wrong with my use of F expression. I will appreciate any help.
I guess it is not ok to mix non-trivial python code into the query expression, beacause it needs to be translated to raw SQL.
I suggest setting this property after you get the data from DB. Something like this:
results = list(queryset)
for r in results:
r.is_admin = my_dict.get(r.id, False)
Other alternatives, if you want to use this in multiple places:
create new #property def is_admin(self): return my_dict.get(self.id, False)
add it as a new field for your model
The problem is that the expression F is used to modify strings while your dictionary keys are integer values. Modify the my_dict.get() value by casting it to an integer using int()
>>> newString = f"{user_id}"
>>> newString
'2'
>>> print(my_dict.get(newString))
None
>>> newInt = int(newString)
>>> print(my_dict.get(newInt))
False

Django Querset Extract PKs

I have the following code.
queryset = Registry.objects.filter(
epas_id__contains=uuid_obj_id
).values_list('pk', flat=True)
Based on my data, this correctly returns the following:
<QuerySet [UUID('d9a0977c-5bc0-4667-af24-5e95b83761d4'), UUID('b2d0f086-0a55-44cc-b1ba-3ebf598d24ae')]>
But what I want to do is extract just the values of pk in a list, so something like this:
['d9a0977c-5bc0-4667-af24-5e95b83761d4', 'b2d0f086-0a55-44cc-b1ba-3ebf598d24ae']
Any thoughts and help on how I can achieve this would be appreciated.
We can work with list comprehension, and call str(…) on each element, like:
queryset = Registry.objects.filter(
epas_id__contains=uuid_obj_id
).values_list('pk', flat=True)
result = [str(pk) for pk in queryset]

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: remove a filter condition from a queryset

I have a third-party function which gives me a filtered queryset (e.g. records with 'valid'=True) but I want to remove a particular condition (e.g. to have all records, both valid and invalid).
Is there a way to remove a filter condition to an already-filtered queryset?
E.g.
only_valid = MyModel.objects.filter(valid=True)
all_records = only_valid.**remove_filter**('valid')
(I know that it would be better to define 'all_records' before 'only_valid', but this is just an example...)
Although there is no official way to do this using filter notation, you may easily do it with Q-notation.
For example, if you ensure that third-part function returns a Q object, not a filtered QuerySet, you may do the following:
q = ThirdParty()
q = q | Q(valid=False)
And the resulting SQL conditions will be joined using OR operator.
From the docs:
Each time you refine a QuerySet, you get a brand-new QuerySet that is in no way bound to the previous QuerySet. Each refinement creates a separate and distinct QuerySet that can be stored, used and reused.
I doubt therefore, that there is a standard way to do it. You could dig into the code, see, what filter() does and try a bit. If that doesn't help, my assumption is, you're out of luck and need to re-build the query yourself.
Use this function
from django.db.models import Q
def remove_filter(lookup, queryset):
"""
Remove filter lookup in queryset
```
>>> queryset = User.objects.filter(email='user#gmail.com')
>>> queryset.count()
1
>>> remove_filter('email', queryset)
>>> queryset.count()
1000
```
"""
query = queryset.query
q = Q(**{lookup: None})
clause, _ = query._add_q(q, self.used_aliases)
def filter_lookups(child):
return child.lhs.target != clause.children[0].lhs.target
query.where.children = list(filter(filter_lookups, query.where.children))
Here's what I did in a similar case.
all_records = MyModel.objects.all()
only_valid = MyModel.objects.filter(valid=True)
only_valid.original = all_records
...
all_records = only_valid.original
Obviously this clears any other filters too so it won't be right for every case.
original_query_set = MyModel.objects.filter(**conditions)
model_class = orginal_query_set.model
new_query_set = model_class.objects.filter(**new_conditions)
You can use the .model attribute on a QuerySet to get the model class, then use the model class to make a brand new QuerySet.
Thanks for making me check the source code Boldewyn. So, it seems there's a new method right below filter() with the same parameters... called exclude() (as of commit mini-hash ef6c680, for when it loses its line number)
Return a new QuerySet instance with NOT (args) ANDed to the existing set.
So, to answer the original question:
only_valid = MyModel.objects.filter(valid=True)
filtered_results = only_valid.exclude(the_condition_to_remove=True)

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)