SQLAlchemy add condition to query - python

Beginning of my raw sql statement looks like this:
select if(substr(Table.order_id,1,8)='STRING', Table1.name, t=Table2.type)
Tried to rewrite it in SQLAlchemy query:
query = db_session.query(Table,\
Table1.name if Table.order_id[1:8] == 'STRING' else Table2.type)
But it returned Operator 'getitem' is not supported on this expression.
How can I add this condition to my ORM query without touching models?
Or how can I add raw sql statement in query parameters?
PS: I would prefer not to make any changes to models.

You need to use Functions:
from sqlalchemy import func
q = db_session.query(
func.IF(func.substr(Table.order_id, 1, 8) == 'STRING', Table1.name, Table2.type)
)

Related

How to use a list in a raw django SQL query that uses `WHERE ... IN (...)`?

How do you inject a list parameter into a Django raw query?
Given a list of UUIDs i'd like to be able to inject them into the () in a WHERE ... IN (...)
query.
list_of_uuids = [
"<UUID_1>",
"<UUID_2>
]
Output for SQL:
SELECT *
FROM some_model_table
WHERE some_model_table.uuid IN ( "<UUID_1>", "<UUID_2>" )
So I tried doing the following using raw django queries:
query_set = SomeModel.objects.raw("SELECT * FROM some_model_table where some_model_table.uuid IN %s", [list_of_uuids])
Unfortunately, the above will give the following error:
django.db.utils.ProgrammingError: syntax error at or near "ARRAY":
LINE X: WHERE uuid IN ARRAY['00123...
Which means the list is interpreted as an array and passed to the SQL query as an array, which cannot be used in an IN query.
Enclosing the injected array in () also doesn't work:
LINE X: WHERE uuid IN (ARRAY['00123...
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
Note: I'm aware this particular example can be done with Django ORM, but my actual query is more complex than this and has to be done with raw queries, and contains this WHERE ... IN (...) syntax. I just kept the irrelevant code out of the question to make it easier to parse. For those who want to use ORM, here is the code you need:
query_set = SomeModel.objects.filter(uuid__in=list_of_uuids)
I have tried building the delimited list of UUID strings myself and injecting it, unfortunately this is injected in the format WHERE uuid IN ('"some_uuid", "other_uuid"')
You could use .format(...) on the query string before handing it over to Django ORM, but this opens us up to SQL injection.
I've also looked into a way of getting SQL to interpret the Array as the input for the WHERE IN query but I haven't had much luck there.
Is there another way? Could we somehow parse the array given to the SQL into valid syntax for the WHERE ... IN (...) query?
You can use ANY(%s), for example in your case:
query_set = SomeModel.objects.raw("SELECT * FROM some_model_table where some_model_table.uuid = ANY(%s)", [list_of_uuids])

How can a SQLAlchemy query use MySQL's REGEXP operator?

How can this SQL query:
SELECT * from table where field REGEXP 'apple|banna|pear';
be written using SQLAlchemy?
My base query looks like this:
query = session.query(TableFruitsDTO)
The SQLAlchemy docs describe how to use the MySQL REGEXP operator. Since there is no built-in function, use .op() to create the function:
query = session.query(TableFruitsDTO).filter(
TableFruitsDTO.field.op('regexp')(r'apple|banana|pear')
)

How do I do a "starts with" query using SQL alchemy?

I am learning to use SQL alchemy to connect to a mysql database. I want to pull records from the DB that start with a given string. I know that for simple equality all I need to do is this
queryRes = ses.query(Table).filter(Table.fullFilePath == filePath).all()
result = []
How do I do something like this?
queryRes = ses.query(Table).filter(Table.fullFilePath.startsWith(filePath)).all()
result = []
Maybe the query would look like this?
q = ses.query(Table).filter(Table.fullFilePath.like('path%')).all()
SQLAlchemy has a startswith column property, so it works exactly as you'd think:
queryRes = ses.query(Table).filter(Table.fullFilePath.startswith(filePath)).all()
This is the pure SQL:
SELECT * FROM table WHERE field LIKE "string%"
The SQL alchemy is:
q = ses.query(Table).filter(Table.fullFilePath.like('path%')).all()
If you need a case insensitive comparison, use ilike:
session.query(SomeTable).filter(SomeTable.some_column.ilike('bla%')).all()

Using OR in SQLAlchemy ORM query

I have some sql queries I'm trying to run as sqlalchemy.orm.query objects and I'm wondering if there is a way to use OR. I can use AND by adding commas into the filter statement but I don't know how to do an OR. Example code below:
query = MyTable.q.filter(MyTable.id == some_number)
However I don't know how to do a query with an OR
query = MyTable.q.filter(MyTable.id == some_number OR MyTable.id == some_other_number)
Any help is greatly appreciated
from sqlalchemy.sql import or_
query = MyTable.q.filter(
or_(MyTable.id == some_number, MyTable.id == some_other_number)
)
Of course, there's no point in this case since you can solve it with in_.
query = MyTable.q.filter(MyTable.id.in_([some_number, some_other_number])

SQLAlchemy ORM: modify the columns returned from a query

If I've got an SQLAlchemy ORM query:
admin_users = Session.query(User).filter_by(is_admin=True)
Is it possible to modify the columns returned by that query?
For example, so that I could select only the User.id column, and use that in a sub query:
admin_email_addresses = Session.query(EmailAddress)\
.filter(EmailAddress.user_id.in_(admin_users.select_columns(User.id))
Note: the .values() method will not work, as it executes the query and returns an iterable of results (so, ex, EmailAddress.user_id.in_(admin_users.values(User.id)) will perform two queries, not one).
I know that I could modify the first query to be Session.query(User.id), but I'm specifically wondering how I could modify the columns returned by a query.
I feel your pain on the values() thing. In 0.6.5 I added with_entities() which is just like values() except doesn't iterate:
q = q.with_entities(User.id)
Assuming that your Address.user_id defines a ForeignKey, the query below will do the job more efficiently compared to IN operator:
admin_email_addresses = session.query(EmailAddress).\
join(User).filter(User.is_admin==True)
If you do not have a ForeignKey (although you should), you can specify the join condition explicitely:
admin_email_addresses = session.query(EmailAddress).\
join(User, User.id==EmailAddress.user_id).filter(User.is_admin==True)
But if you really would like to do it with in_ operator, here you go (note the subquery):
subq = session.query(User.id).filter(User.is_admin==True).subquery()
admin_email_addresses = session.query(EmailAddress).\
filter(EmailAddress.user_id.in_(subq))

Categories