Reference subquery field for greater than clause in django - django

I have a query that I can do with raw sql but am trying to convert it to use the Django ORM. The query is:
SELECT a.journey_pattern_ref_id
FROM(
SELECT * FROM journeypatterntiminglink
WHERE from_stop_id = '0180BAC30249'
) a,
journeypatterntiminglink b
WHERE a.journey_pattern_ref_id = b.journey_pattern_ref_id
AND b.to_stop_id = '0180BAC30035'
AND b.to_seq_no > a.from_seq_no;
The part that is giving me trouble is b.to_seq_no > a.from_seq_no. So far I have
jps = (JourneyPattern.objects
.filter(journeypatterntiminglink__from_stop=origin)
.filter(journeypatterntiminglink__to_stop=destination))

Well, you should use F() expressions to reference other fields in query construction rather than constants.
Inferring the definition of your model from your question it should be something like this:
from django.db.models import F
jps = (JourneyPattern.objects
.filter(journeypatterntiminglink__from_stop=origin)
.filter(journeypatterntiminglink__to_stop=destination)
.filter(journeypatterntiminglink__to_seq_no__gt=F('journeypatterntiminglink__from_seq_no'))

Related

Django orm subquery - in clause without substitution

I need to build a query using Django ORM, that looks like this one in SQL:
select * from A where id not in (select a_id from B where ... )
I try to use such code:
ids = B.objects.filter(...)
a_objects = A.object.exclude(id__in=Subquery(ids.values('a__id'))).all()
The problem is that instead of nested select Django generates query that looks like
select * from A where id not in (1, 2, 3, 4, 5 ....)
where in clause explicitly lists all ids that should be excluded, making result sql unreadable when it is printed into logs. Is it possible to adjst this query, so nested select is used?
So I see that your goal is to get all the A's that have no foreign key relations from B's. If I'm right, then you can just use inverse lookup to do it.
So, when you define models like that:
class A:
pass
class B:
a = ForeignKey(to=a, related_name='bs')
You can filter it like this:
A.objects.filter(bs__isnull=True)
Also, if you don't define related_name, it will default to b_set, so you will be able to A.objects.filter(b_set__isnull=True)
to make a filter on B you can
ids = B.objects.filter(x=x).values_list('id',flat=true)
you get a list of ids then make
a_objects = A.object.exclude(id__in=ids)
as mentioned before if there is a relation
You don't need to do anything special, just use the queryset directly in your filter.
ids = B.objects.filter(...)
a_objects = A.object.exclude(id__in=ids).all()
# that should generate the subquery statement
select * from A where NOT (id in (select a_id from B where ... ))

QuerySet in Django

How can I perform this SQL query
SELECT *
FROM table_name
WHERE column_name != value
in the QuerySet of Django?
I tried this but isn't correct way:
To execute sql queries just use raw method of Model like this:
posts = Post.objects.raw("SELECT * FROM table_name WHERE column_name != %s;", [value])
for post in posts:
# do stuff with post object
But i don't think you need raw query (unless you want to get rid of ORM overhead to fetch records quicker) you can just use ORM like this:
posts = Post.objects.all().exclude(column_name=value)
You can do it using Q:
from django.db.models import Q
posts = Post.objects.filter(~Q(column_name=value))
I think you want to do this query using Django ORM (If I am not wrong). You can do this using Q Expression in Django (https://docs.djangoproject.com/en/4.0/topics/db/queries/#s-complex-lookups-with-q-objects). For != you can use ~ sign. In your case, the query will look like this
Post.objects.filter(~Q(<column_name>=<value>))
Another way to use exclude method (https://docs.djangoproject.com/en/4.0/ref/models/querysets/#s-exclude)
Post.objects.exclude(<column_name>=<value>)
Both queries generate the same raw query in your case:
SELECT * FROM <table_name> WHERE NOT (<column_name>=<value)
If you want to do the raw query then you can use raw method (https://docs.djangoproject.com/en/4.0/topics/db/sql/)
posts = Post.objects.raw("SELECT * FROM table_name WHERE column_name != %s;", [value])
If you want to execute a custom raw query directly then use cursor from django.db connection (https://docs.djangoproject.com/en/4.0/topics/db/sql#s-executing-custom-sql-directly)

Django - getting list of values after annotating a queryset

I have a Django code like this:
max_id_qs = qs1.values('parent__id').\
annotate(max_id = Max('id'),).\
values_list('max_id', flat = True)
The problem is that when I use max_id_qs in a filter like this:
rs = qs2.filter(id__in = max_id_qs)
the query transforms into a MySQL query of the following structure:
select ... from ... where ... and id in (select max(id) from ...)
whereas the intended result should be
select ... from ... where ... and id in [2342, 233, 663, ...]
In other words, I get subquery instead of list of integers in the MySQL query which slows down the lookup dramatically. What surprises me is that I thought that Django's values_list returns a list of values.
So the question, how should I rewrite the code to achieve the desired MySQL query with integers instead of id in (select ... from...) subquery
Querysets are lazy, and .values_list still returns a queryset object. To evaluate it simply convert it into a list:
rs = qs2.filter(id__in=list(max_id_qs))

Using existing field values in django update query

I want to update a bunch of rows in a table to set the id = self.id. How would I do the below?
from metadataorder.tasks.models import Task
tasks = Task.objects.filter(task_definition__cascades=False)
.update(shared_task_id=self.id)
The equivalent SQL would be:
update tasks_task t join tasks_taskdefinition d
on t.task_definition_id = d.id
set t.shared_task_id = t.id
where d.cascades = 0
You can do this using an F expression:
from django.db.models import F
tasks = Task.objects.filter(task_definition__cascades=False)
.update(shared_task_id=F('id'))
There are some restrictions on what you can do with F objects in an update call, but it'll work fine for this case:
Calls to update can also use F expressions to update one field based on the value of another field in the model.
However, unlike F() objects in filter and exclude clauses, you can’t introduce joins when you use F() objects in an update – you can only reference fields local to the model being updated. If you attempt to introduce a join with an F() object, a FieldError will be raised[.]
https://docs.djangoproject.com/en/dev/topics/db/queries/#updating-multiple-objects-at-once
I stumbled upon this topic and noticed Django's limitation of updates with foreign keys, so I now use raw SQL in Django:
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("UPDATE a JOIN b ON a.b_id = b.id SET a.myField = b.myField")

Alias Name in Query Set

I'm using following code set to pull data from specific columns of the table...
cityList = City.objects.using(settings.DATABASE_CONF).filter(
status=1).values('city_name_en', 'city_id')
How can I set an alias for the city_name_en column? I have two other columns with city_name_fr city_name_de but the client can understand only city_name.
You can annotate the fields as you want, with the F expression:
from django.db.models import F
cityList = City.objects.using(settings.DATABASE_CONF).filter(status=1).values(
'city_name_en', 'city_id')
# use F expression to annotate with an alias
cityList = cityList.annotate(cityname=F('city_name_en'))
I think giving alias in Django query is not possible. However you can refer to this answer.