querying using Q objects and also using Q single object - django

i have 2 queries which i have to join using '|' and apply the final one to get
the result.First i have list of countries i.e eu countries.and second i have a country from which the user is logged in.I want to join both ones and get the result using Q.First one is q= Q(country=auth.country)and second one is
q2 = Q(country = settings.euCountries), in which settings.enCountries is a django list.Any help in this matter is highly appreciated

If you need an OR for all countries
q = [Q(country=auth.country)] + [Q(country=i) for i in settings.euContries]
Then
import operator
Model.objects.filter(reduce(operator.or_, q))

I don't think you need multiple Q() objects here. You can use the __in lookup.
Q(country_in=[auth.country] + settings.euCountries)
Depending on your code, you might not need the Q object at all if you can do something like the following:
queryset = MyModel.objects.filter(country_in=[auth.country] + settings.euCountries)

Related

Django Queryset

I need to do something like this in order to match some objects.
This is the best way to do it?
Is possible to do it in a faster way?
if User.objects.filter(first_name=name, age=age).exists():
user = User.objects.filter(first_name=name, age=age)
function_x(user)
elif User.objects.filter(full_name__icontains=name, age=age).exists():
user = User.objects.filter(full_name__icontains=name, age=age)
function_x(user)
If you want use one of that condition, just use Q object, it allows you use logic or operator in your query.
from django.db.models import Q
User.objects.filter(Q(first_name=name) |
Q(full_name__icontains=name),
age=age)
In that case | means or and , means and, so age is required in both conditions.
Given the variable name, I guess you are expecting the above query to return a single user. Thus, you can eliminate one more db hit using .first():
.first() basically returns the first object matched by the queryset, or None if there isn't one. By this way, you don't have to perform .exists().
user = User.objects.filter(Q(first_name=name) | Q(full_name__icontains=name), age=age).first()

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 to add more conditions in where clause?

I have made a loop to retrieve conditions:
for level in levels:
requete += ' and level_concat like %'+level+'%'
and i made in my query:
countries = Country.objects.extra(where=['continent_id = "'+continent_id+'"', requete])
I have tried to add condition to my where clause, but return error:
not enough arguments for format string
Respected results:
SELECT * FROM `Country` WHERE country_id = "US-51" AND level_concat LIKE %level_test%
Is there a way to add requete to my 'where' clause?
Firstly, it is not a good practice to keep data in a relational database as a "list of [values] concatenated by coma" - you should create a new table for those values.
Still, even now you can use filter(), instead of extra() (which should be always your last resort - I don't see the rest of your code, but if you don't properly escape levels values you may even be introducing an SQL Injection vulnerability here).
An example of a secure, extra()-less code, that does the exact same thing:
from django.db.models import Q
q = Q()
for level in levels:
q &= Q(level_concat__contains=level)
countries = Country.objects.filter(q)
or the same functionality, but in even less number of lines:
from django.db.models import Q
q = (Q(level_concat__contains=l) for l in levels)
countries = Country.objects.filter(*q)
You can read more about Q object in Django docs.
I think you need to escape the % sign in your query:
' and level_concat like %%'+level+'%%'

Using .extra() on fields created by .annotate() in Django

I want to retrieve a sum of two fields (which are aggregations themselves) for each object in a table.
The following may describe a bit better what I'm after but results in an Unknown column in field list-Error:
items = MyModel.objects.annotate(
field1=Sum("relatedModel__someField"),
field2=Sum("relatedModel__someField")).extra(
select={"sum_field1_field2": "field1 + field2"})
I also tried using F() for the field lookups but that gives me an invalid sql statement.
Any ideas on how to solve this are much appreciated.
it this what you want?
items = MyModel.objects.extra(
select = {'sum_field1_field2': 'SUM(relatedModel__someField) + SUM(relatedModel__someField)'},
)
To make it work for many to many or for many to one (reverse) relations, you may use the following:
items = MyModel.objects.extra(
select = {'sum_field1_field2': 'SUM("relatedModel"."someField") + SUM("relatedModel"."someField")'},
)
But this will break also if you need another annotate, like for a count, because extra will add the statement to the GROUP BY clause, whereas aggregate functions are not allowed in there.

How can join two django querysets in one?

I need order a Queryset by date in desc order, but i need put in the end the objects at the end, I do this:
qs1 = Model.objects.exclude(date=None).order_by('-date')
qs2 = Model.objects.filter(date=None).order_by('-date')
and my list is:
l = list(qs1)+list(qs2)
There is a more efficiently way for this?
Model.objects.extra(select={'nodate':'ISNULL(date)'}, order_by=['nodate', '-date'])
So, you're looking for all the objects that have a date ... and all the objects that don't have a date?
Wouldn't this work better:
Model.objects.order_by('-date)
Anyway, here's a good place to start ... read about Django filter chaining: http://docs.djangoproject.com/en/dev/topics/db/queries/#id1.
As a side note, isn't your query canceling itself out?
>>> d = MyModel.objects.filter(date=None).exclude(date=None).order_by('-date')
>>> d
[]
>>> d.query.get_compiler('default').as_sql()
('SELECT "date" FROM "table" WHERE ("table"."date" IS NULL AND NOT ("table"."date" IS NULL)) ORDER BY "table"."date" DESC', ())
I'm probably not understanding what you're asking...