Django __in lookup doesn't look of None values - django

Consider I have a model called Subject, and I want to filter the values based on the list of values.
Consider the below list.
["John", "Brad", None]
When I try to filter out the result using __in lookup it doesn't look for None values. For example
Subject.objects.filter(user__in=["John", "Brad", None])
This will provide the queryset for John and Brad but not None. What I'm missing here? Can anyone please help me

NULLs in DB (None in django) are not reqular values, so they need different approach:
Subject.objects.filter(Q(user__in=["John", "Brad", ]) | Q(user__isnull=True,))

Try filtering like this
from django.db.models import Q
Subject.objects.filter(Q(user__in=["John", "Brad"]) | Q(user__isnull=True))

Using Q objects is better suited for this:
Subject.objects.filter(Q(user__in=["John", "Brad"]) | Q(user__isnull=True))

You can use Q objects for OR, AND and NOT operations. Q objects provide you complete control over the where clause of the query.
Your queryset would be something like:
from django.db.models import Q
Subject.objects.filter(Q(user=None) | Q(user__in=["John", "Brad"]))
You can combine the Q objects in more complex ways to generate complex queries.

Related

Django ORM, Q statements and customized sorting

I am simplifying this for clarity. Let's say I had this function that finds Document records based on a requestedColor.
def find_docs(requestedColor):
docs = Document.objects.filter(Q(color=requestedColor) | Q(other_color=requestedColor))
I'd like to order the results so that Document found using color will appear before objects found with other_color and I want to do it within the query - without any external sorting.
Is there a way to do this within the ORM query? I could not find a way to do that.
Pointers will be appreciated.
You can use Conditional Expressions to annotate a value which would indicate which field matched and order by this value:
from django.db.models import Case, IntegerField, Value, When
docs = Document.objects.annotate(
color_order=Case(
When(color=requestedColor, then=Value(1, output_field=IntegerField())),
When(other_color=requestedColor, then=Value(2, output_field=IntegerField())),
default=Value(0, output_field=IntegerField()),
)
).filter(
Q(color=requestedColor) | Q(other_color=requestedColor)
).order_by('color_order')

Filters chain rather than Q() combination in Queryset

I have an article model with m2m relationships to model Tag,
I intend to filter articles which have tags both "python" and "django"
I tried Q along with &
In [184]: from django.db.models import Q
In [185]: articles = Article.objects.filter(Q(tags__name="python") & Q(tags__name=
...: "django"))
In [186]: articles
Out[186]: <QuerySet []>
It return a null Queryset
Alternatively I tested
In [202]: articles = Article.objects.filter(Q(tags__name="python")).filter(Q(tags_
...: _name="django"))
In [203]: articles
Out[203]: <QuerySet [<Article: Test new tags>, <Article: Django Tutorial>]>
I worked and solved the problem.
However, I am very confused with the failing of Q()&Q(), should I alway utilize filter chain rather than Q() combination to avoid mistakes?
By doing .filter(Q(tags__name="python") & Q(tags__name="django"), you're asking filter to look for Articles with a related Tag that satisfies both name="python" and name="django" at the same time, which naturally can't be true.
By doing two separate .filter()s, the related Tags that satisfy the two conditions don't have to be the same one, since they are done on two separate querysets, so they will be able to find Articles that have a Tag whose name is 'python', and another Tag whose name is 'django'.
Use | operator (Logical OR operator) instead of & operator (Logical AND operator).
Try this,
from django.db.models import Q
Article.objects.filter(Q(tags__name="python") | Q(tags__name="django"))
^^^^^
See this So post also,
Django Filters -OR

Error on OR on filter Django

i'm try create or on filter on django
This is my little example :
products=Products.objects.values('name', 'price').all().filter(status=1|0)
The problem is that don't validate the two options (1|0)
don't get a error on the print(products.query) only validate one option don't the 2 options..!!
Please thanks !!
To filter using OR in django you need a special class called Q.
Documentation about Complex lookups with Q objects
from django.db.models import Q
products = Products.objects.values('name', 'price').filter(Q(status=1) | Q(status=0))
It's good to use Q object
manager.filter(Q(status=1) | Q(status=0))
You need to know that the method all() on a manager just delegates to get_queryset().
To use filter(), you would already have the QuerySet
Rather than all() whose calls the queryset, and then filter whose already call the queryset,
just do manager.filter()
all().filter() becomes just filter() because it's redundant
There it is:
from django.db.models import Q
products = Product.objects.values('name','price').filter(
Q(status=1) | Q(status=0),
)

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'])]

django icontains with __in lookup

So I want to find any kind of matching given some fields, so for example, this is what I would like to do:
possible_merchants = ["amazon", "web", "services"]
# Possible name --> "Amazon Service"
Companies.objects.filter(name__icontains__in=possible_merchants)
sadly it is not possible to mix icontains and the __in lookup.
It seems to be a pretty complex query so if at least I could ignore case the name that would be enough, for example:
Companies.objects.filter(name__ignorecase__in=possible_merchants)
Any ideas?
P.D.: The queries I posted don't work, it's just a way to express what I need (just in case heh)
You can create querysets with the Q constructor and combine them with the | operator to get their union:
from django.db.models import Q
def companies_matching(merchants):
"""
Return a queryset for companies whose names contain case-insensitive
matches for any of the `merchants`.
"""
q = Q()
for merchant in merchants:
q |= Q(name__icontains = merchant)
return Companies.objects.filter(q)
(And similarly with iexact instead of icontains.)
I find it a cleaner approach using reduce and or_ operator:
from django.db.models import Q
from functools import reduce
from operator import or_
def get_companies_from_merchants(merchant_list):
q_object = reduce(or_, (Q(name__icontains=merchant) for merchant in merchant_list))
return Companies.objects.filter(q_object)
This would create a list of Q objects querying the name to contain a single element in merchant list. This would happpen for all the elements in merchant_list and all these Q objects would be reduced to a single Q object having mutliple ORs which can be directly applied to the filter query.
This is the approach that I adopted:
class MyManager(models.Manager):
def exclusive_in(self, lookup, value_list):
return self.filter(reduce(or_, (Q(**{lookup:_}) for _ in value_list)))
Here is now to use it:
Companies.objects.exclusive_in('name__icontains', possible_merchants])
It was inspired by other answers in this thread, as well as Django filter queryset __in for *every* item in list.
Another approach would be to simulate the actions that Django normally does for iexact queries (it converts both parts of the comparison statement to the upper case via SQL Upper function.
This way, the query will look like this:
Companies.objects.annotate(
upper_name=models.Upper("name")
).filter(
upper_name__in=[rchant.upper() for merchant in possible_merchants]
)