I have a function which receives an optional argument.
I am querying a database table within this function.
What I would like is:
If the optional argument is specified, I want to add another additional .filter() to my database query.
My query line is already rather long so I don't want to do If .. else .. in which I repeat the whole query twice.
What is the way to do this?
Below is an example to my query and if my_val is specified, I need to add another filtering line.
def my_def (my_val):
query = Session.query(Table1, Table2).\
filter(Table1.c1.in_(some_val)).\
filter(Table1.c2 == 113).\
filter(Table2.c3 == val1).\
filter(Table1.c4 == val2).\
filter(Table2.c5 == val5).\
all()
You can wait to call the .all() method on the query set, something like this:
def my_def (my_val=my_val):
query = Session.query(Table1, Table2).\
filter(Table1.c1.in_(some_val)).\
filter(Table1.c2 == 113).\
filter(Table2.c3 == val1).\
filter(Table1.c4 == val2).\
filter(Table2.c5 == val5)
if my_val:
query = query.filter(Table1.c6 == my_val)
return query.all()
Related
I'm having trouble converting this SQL query into a SQL Alchemy query:
query = """
SELECT i.case_num,
to_char(i.date_time, 'FMMonth FMDD, YYYY'),
to_char(i.date_time, 'HH24:MI'),
i.incident_type,
i.incident_cat,
i.injury,
i.property_damage,
i.description,
i.root_cause,
a.corrective_action,
a.due_date,
i.user_id
FROM incident as i, action_items as a
WHERE i.case_num = a.case_id AND i.case_num = %s;
"""
I have tried the following but have received nothing but errors:
sqlalchemy.orm.exc.NoResultFound: No row was found for one()
results = dbsession.query(Incidents.case_num,
func.to_char(Incidents.date_time, 'FMMonth FMDD, YYYY'),
func.to_char(Incidents.date_time, 'HH24:MI'),
Incidents.incident_type,
Incidents.incident_cat,
Incidents.injury,
Incidents.property_damage,
Incidents.description,
Incidents.root_cause,
Actions.corrective_action,
Actions.due_date,
Incidents.user_id).join(Actions).filter_by(case_id = id).one()
AttributeError: mapper
results = dbsession.query(Incidents.case_num,
func.to_char(Incidents.date_time, 'FMMonth FMDD, YYYY'),
func.to_char(Incidents.date_time, 'HH24:MI'),
Incidents.incident_type,
Incidents.incident_cat,
Incidents.injury,
Incidents.property_damage,
Incidents.description,
Incidents.root_cause,
Incidents.user_id).join(Actions.corrective_action, Actions.due_date).filter_by(case_id = id).one()
I figure I can do two separate queries but would rather figure out how to perform one join query instead.
you shouldn't need to specify a join explicitly to get sqlalchemy to generate the statment you want.
Also, (my opinion). Avoid using filter_by.
In this case filter_by is not smart enough to realize that id is a column in Incidents, because id is a built in function. filter_by (see source)
accepts where conditions as keyword arguments, unpacks them, treating the keys as columns to be looked up, but not the values, then it calls the filter method with all the conditions conjoined.
relevant bit of code:
def filter_by(self, **kwargs):
clauses = [_entity_descriptor(self._joinpoint_zero(), key) == value
for key, value in kwargs.items()]
return self.filter(sql.and_(*clauses))
if id were provided as a left-hand value, i.e.
stmt = dbsession.query(...).join(...).filter_by(id = 123)
The statement would compile. However, the following would not compile
stmt = dbsession.query(...).join(...).filter_by(id = case_id)
because, case_id is not a variable in scope
And, the OP's version
stmt = dbsession.query(...).join(...).filter_by(case_id = id)
can resolve case_id properly, and sees that there is something in the current scope named id (the built-in), and tries to use it
This should do what you want:
results = dbsession.query(
Incidents.case_num,
func.to_char(Incidents.date_time, 'FMMonth FMDD, YYYY'),
func.to_char(Incidents.date_time, 'HH24:MI'),
Incidents.incident_type,
Incidents.incident_cat,
Incidents.injury,
Incidents.property_damage,
Incidents.description,
Incidents.root_cause,
Actions.corrective_action,
Actions.due_date,
Incidents.user_id).filter(
Actions.case_id == Incidents.id
).filter(
Incidents.case_num == 123
).one()
# ^ here's how one would add multiple filters to a query
FYI, you can save query objects and inspect them, like this:
stmt = dbsession.query(...).filter(...)
print(stmt)
And then fetch the results with
stmt.one()
# or stmt.first() or stmt.all() or ...
I need to generate/build sqlalchemy query dynamically using dynamic columns and their values.
Example -
I have a table in SQL called "Convo" and it has columns like - UserID, ConvoID, ContactID.
I need to get rows based on the below criteria.
criteria = (('UserID', 2), ('ConvoID', 1) ,('ContactID', 353))
I have used "Baked query" logic for this. But Some how I am not able to run this query successfully.
Below is the my code.
criteria = (('UserID', 2), ('ConvoID', 1) ,('ContactID', 353))
baked_query = bakery(lambda session: session.query(tablename))
for key1 in condition:
baked_query += lambda q: q.filter(tablename.key1 == condition[key1])
result = baked_query(self.session).all()
I am getting error as -
AttributeError: type object 'Convo' has no attribute 'key1'
Please help me out with this
criteria = (('UserID', 2), ('ConvoID', 1) ,('ContactID', 353))
query = session.query(tablename)
for _filter, value in criteria:
query = query.filter(getattr(tablename, _filter) == value)
result = query.all()
If you're using dynamic keys and "simple" equality checks, the filter_by method might be more convenient, as it takes keyword arguments that match you property names and assembles that into where clause.
So your iterative query construction could look like that:
baked_query = bakery(lambda session: session.query(tablename))
for key, value in condition.items():
baked_query += lambda q: q.filter_by(key=value)
Plus, since filter_by takes mulitple keyword arguments, you can probably simplify your query construction to a single filter_by invocation:
baked_query = bakery(lambda session: session.query(tablename))
baked_query += lambda q: q.filter_by(**condition)
All of the above obviously assuming that your condition variable refers to a dictionary.
For the SQL statement, I want to do an OR filter only if the input variable is not None.
E.g
# input variable
var_1 = "apple"
var_2 = "pear"
query = session.query(Table)
if var_1:
query = query.filter(or_(Table.field1 == var_1))
if var_2:
query = query.filter(or_(Table.field2 == var_2))
But this is not possible. The 2nd query became an AND statement instead.
I don't want to do this because I don't want to compare an input variable if it's null or empty string.
query = query.filter(or_(Table.field1 == var_1, Table.field2 == var_2))
How do I solve this?
You can dynamically construct the "OR" part:
query = session.query(Table)
conditions = []
if abc:
conditions.append(Table.field1 == abc)
if def:
conditions.append(Table.field2 == def)
query = query.filter(or_(*conditions))
Also note that the def is a reserved word in Python, consider renaming this variable.
I have a sql query as follows
select cloumn1,column2,count(column1) as c
from Table1 where user_id='xxxxx' and timestamp > xxxxxx
group by cloumn1,column2
order by c desc limit 1;
And I successed in write the sqlalchemy equvalent
result = session.query(Table1.field1,Table1.field2,func.count(Table1.field1)).filter(
Table1.user_id == self.user_id).filter(Table1.timestamp > self.from_ts).group_by(
Table1.field1,Travelog.field2).order_by(desc(func.count(Table1.field1))).first()
But I want to avoid using func.count(Table1.field1) in the order_by clause.
How can I use alias in sqlalchemy? Can any one show any example?
Aliases are for tables; columns in a query are given a label instead. This trips me up from time to time too.
You can go about this two ways. It is sufficient to store the func.count() result is a local variable first and reuse that:
field1_count = func.count(Table1.field1)
result = session.query(Table1.field1, Table1.field2, field1_count).filter(
Table1.user_id == self.user_id).filter(Table1.timestamp > self.from_ts).group_by(
Table1.field1, Travelog.field2).order_by(desc(field1_count)).first()
The SQL produced would still be the same as your own code would generate, but at least you don't have to type out the func.count() call twice.
To give this column an explicit label, call the .label() method on it:
field1_count = func.count(Table1.field1).label('c')
and you can then use that same label string in the order_by clause:
result = session.query(Table1.field1, Table1.field2, field1_count).filter(
Table1.user_id == self.user_id).filter(Table1.timestamp > self.from_ts).group_by(
Table1.field1, Travelog.field2).order_by(desc('c')).first()
or you could use the field1_count.name attribute:
result = session.query(Table1.field1, Table1.field2, field1_count).filter(
Table1.user_id == self.user_id).filter(Table1.timestamp > self.from_ts).group_by(
Table1.field1, Travelog.field2).order_by(desc(field1_count.name)).first()
Can also using the c which is an alias of the column attribute but in this case a label will work fine as stated.
Will also point out that the filter doesn't need to be used multiple times can pass comma separated criterion.
result = (session.query(Table1.field1, Table1.field2,
func.count(Table1.field1).label('total'))
.filter(Table1.c.user_id == self.user_id, Table1.timestamp > self.from_ts)
.group_by(Table1.field1,Table1.field2)
.order_by(desc('total')).first())
I'm building a web2py controller in which I need to query a table for a combination of value x in one field AND value y in a second field (in the same row). To query on a single field, I would just write
db.table.field == x
But I don't know how to write a query that looks for field==x AND field2==y
(db.table.field1==x)&(db.table.field2==y)
See the book section on logical operators.
For a more advanced version, you can append a query to a list and use python's reduce function.
queries=[]
if arg1 == "x": queries.append(db.table.field == x)
if arg2 == "y": queries.append(db.table.otherfield == y)
# many conditions here....
query = reduce(lambda a,b:(a&b),queries)
db(query).select()