Combine Q objects in Django and limit one of them - django

I want to combine these 2 queries in 1 to get the Music object with name xyz and also get top 3 objects from genre 10, ordered by artist:
1. Music.objects.filter(name='xyz', genre=10)
2. Music.objects.filter(genre=10).order_by('artist')[:3]
I can use Q objects like this but I don't know how to order & filter the 3rd Q object below:
Music.objects.filter( (Q(name='xyz') & Q(genre=10)) | Q(genre=10) )

Maybe try like this?
Music.objects.filter( (Q(name='xyz') & Q(genre=10)) | Q(genre=10) ).order_by('artist')[:3]

No way to do it, use two queries but change second to avoid duplicates:
exact = Music.objects.filter(name='xyz', genre=10)
additional = Music.objects.filter(genre=10).exclude(name='xyz').order_by('artist')[:3]
do_something_with = list(exact) + list(additional)

Related

Use and, or conditions while using Case,When

Anyone know in the following query how can I use AND condition?
q = Food.objects.all().annotate(open=Case(When(days__day=today,then='blahblah'),When(start_time__lte=now and end_time__gte=now,then='blabla')))
On the second when I want to check if the now value is between start and end time but it seems like the 'and' keyword is not working there
Use Q objects so:
from django.db.models import F, Q, When
q = Food.objects.annotate(open=Case(
When(days__day=today, then='blahblah'),
When(Q(start_time__lte=now) & Q(end_time__gte=now), then='blabla'),
)
Use & to AND two Q objects and | to OR them.

How to convert list to multiple Q objects?

Taking an example from Django 3.2 documentation, I need to use an argument like this for .filter:
Q(question__startswith='Who') | Q(question__startswith='What')
In my case need to convert each of the user's selections, which I'm getting in views.py via request.META['QUERY_STRING'], into it's own Q() object.
If I tried to create Q objects from that list of parameters, it would not work, because the | would be evaluated. This seems like it must be a solved a problem, but I haven't had luck yet finding the answer. Thanks for any advice.
You can build up a complex Q object in a loop - use q_obj |= Q(...) to add another Q with OR
selections = ['Who', 'What']
or_expr = Q()
for selection in selections:
or_expr |= Q(question__startswith=selection)
MyModel.objects.filter(or_expr)
You can do that using distnict()
.filter(Q(question__startswith='Who') | Q(question__startswith='What')).distnict()
using regex might be an easy option:
Model.objects.filter(question__iregex="^(What|Who).*")

Django Exclude Model Objects from query using a list

Having trouble with my query excluding results from a different query.
I have a table - Segment that I have already gotten entries from. It is related
to another table - Program, and I want to also run the same query on it but I want to exclude
any of the programs that were already found during the segment query.
When I try to do it, the list isn't allowed to be used in the comparison... See below:
query = "My Query String"
segment_results = Segment.objects.filter(
Q(title__icontains=query)|
Q(author__icontains=query)|
Q(voice__icontains=query)|
Q(library__icontains=query)|
Q(summary__icontains=query) ).distinct()
# There can be multiple segments in the same program
unique_programs = []
for segs in segment_results:
if segs.program.pk not in unique_programs:
unique_programs.append(segs.program.pk)
program_results = ( (Program.objects.filter(
Q(title__icontains=query) |
Q(library__icontains=query) |
Q(mc__icontains=query) |
Q(producer__icontains=query) |
Q(editor__icontains=query) |
Q(remarks__icontains=query) ).distinct()) &
(Program.objects.exclude(id__in=[unique_programs])))
I can run:
for x in unique_programs:
p = Program.objects.filter(id=x)
print("p = %s" % p)
And I get a list of Programs...which works
Just not sure how to incorporate this type of logic into the results
query...and have it exclude at the same time. I tried exclude keyword,
but the main problem is it doesn't like the list being in the query - I get an
error:
TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'.
Feel like I am close...
The answer is simple, I was not comparing objects correctly in the filters, so
the correct statement would be:
program_results = (Program.objects.filter(
Q(title__icontains=query) |
Q(library__icontains=query) |
Q(mc__icontains=query) |
Q(producer__icontains=query) |
Q(editor__icontains=query) |
Q(remarks__icontains=query) )&
(Program.objects.exclude(id__in=Program.objects.filter(id__in=unique_programs))))

BIRT: Align rows in list element

I'm using the Birt list element to display my data from left to right. (see this question as reference). Eg. List element with a Grid in details and the grid set to inline.
The issue I'm facing now is, that the different rows in the grid are not aligned left to right (probably due to some rows having empty values in some fields). How can I force BIRT to align properly?
EDIT:
This is especially also a problem with longer text that wraps to more than 1 line. The wrapping /multiple lines should be reflected by all list elements in that "row of the output".
Unfortunately, I don't see any chance to accomplish this easily in the generic case - that is, if the number of records is unknown in advance, so you'd need more than one line:
student1 student2 student3
student4 student5
Let's call those line "main lines". One main line can contain up to 3 records. The number 3 may be different in your case, but we can assume it is a constant, since (at least for PDF reports) the paper width is restricted.
A possible solution could work like this:
In your data set, add two columns for each row: MAIN_LINE_NUM and COLUMN_NUM, where the meaning is obvious. For example, this could be done with pure SQL using analytic functions (untested):
select ...,
trunc((row_number() over (order by ...whatever...) - 1) / 3) + 1 as MAIN_LINE_NUM,
mod(row_number() over (order by ...whatever...) - 1), 3) +1 as COLUMN_NUM
from ...
order by ...whatever... -- The order must be the same as above.
Now you know where each record should go.
The next task is to transform the result set into a form where each record looks like this (for the example, think that you have 3 properties STUDENT_ID, NAME, ADDRESS for each student):
MAIN_LINE
STUDENT_ID_1
NAME_1
ADDRESS_1
STUDENT_ID_2
NAME_2
ADDRESS_2
STUDENT_ID_3
NAME_3
ADDRESS_3
You get the picture...
The SQL trick to achieve this is one that one should know.
I'll show this for the STUDENT_ID_1, STUDENT_ID_2 and NAME_1 column as an example:
with my_data as
( ... the query shown above including MAIN_LINE_NUM and COLUMN_NUM ...
)
select MAIN_LINE_NUM,
max(case when COLUMN_NUM=1 then STUDENT_ID else null end) as STUDENT_ID_1,
max(case when COLUMN_NUM=2 then STUDENT_ID else null end) as STUDENT_ID_2,
...
max(case when COLUMN_NUM=1 then NAME else null end) as NAME_1,
...
from my_data
group by MAIN_LINE_NUM
order by MAIN_LINE_NUM
As you see, this is quite clumsy if you need a lot of different columns.
On the other hand, this makes the output a lot easier.
Create a table item for your dat set, with 3 columns (for 1, 2, 3). It's best to not drag the dataset into the layout. Instead, use the "Insert element" context menu.
You need a detail row for each of the columns (STUDENT_ID, NAME, ADDRESS). So, add two more details rows (the default is one detail row).
Add header labels manually, if you like, or remove the header row if you don't need it (which is what I assume).
Remove the footer row, as you probably don't need it.
Drag the columns to the corresponding position in your table.
The table item should look like this now (data items):
+--------------+--------------+-------------+
+ STUDENT_ID_1 | STUDENT_ID_2 | STUDENT_ID3 |
+--------------+--------------+-------------+
+ NAME_1 | NAME_2 | NAME_3 |
+--------------+--------------+-------------+
+ ADDRESS_1 | ADDRESS_2 | ADDRESS_3 |
+--------------+--------------+-------------+
That's it!
This is one of the few examples where BIRT sucks IMHO in comparison to other tools like e.g. Oracle Reports - excuse my Klatchian.

How not to order a list of pk's in a query?

I have a list of pk's and I would like to get the result in the same order that my list is defined... But the order of the elements is begging changed. How any one help me?
print list_ids
[31189, 31191, 31327, 31406, 31352, 31395, 31309, 30071, 31434, 31435]
obj_opor=Opor.objects.in_bulk(list_ids).values()
for o in obj_oportunidades:
print o
31395 31435 31434 30071 31309 31406 31189 31191 31352 31327
This object should be used in template to show some results to the user... But how you can see, the order is different from the original list_ids
Would have been nice to have this feature in SQL - sorting by a known list of values.
Instead, what you could do is:
obj_oportunidades=Opor.objects.in_bulk(list_ids).values()
all_opor = []
for o in obj_oportunidades:
print o
all_opor.append(o)
for i in list_ids:
if i in all_opor:
print all_opor.index(i)
Downside is that you have to get all the result rows first and store them before getting them in the order you want. (all_opor could be a dictionary above, with the table records stored in the values and the PKeys as dict keys.)
Other way, create a temp table with (Sort_Order, Pkey) and add that to the query:
Sort_Order PKey
1 31189
2 31191
...
So when you sort on Sort_Order and Opor.objects, you'll get Pkeys it in the order you specify.
I found a solution in: http://davedash.com/2010/02/11/retrieving-elements-in-a-specific-order-in-django-and-mysql/ it's suited me perfectly.
ids = [a_list, of, ordered, ids]
addons = Addon.objects.filter(id__in=ids).extra(
select={'manual': 'FIELD(id,%s)' % ','.join(map(str,ids))},
order_by=['manual'])
This code do something similiar to MySQL "ORDER BY FIELD".
This guy: http://blog.mathieu-leplatre.info/django-create-a-queryset-from-a-list-preserving-order.html
Solved the problem for both MySQL and PostgreSQL!
If you are using PostgreSQL go to that page.