I need a condition in sqlalchemy that allow me to use a list of ids stored in a TEXT column as a parameter for a in_. The ids are simply stored like 1,2,3,4. I tried to use an in_ but it's seems that a "list" stored in a TEXT column is not the right way to do.
This is what I've tried:
filters.append(and_(Organisation.id.in_(Session.list_id), Session.token == token))
That gives me this error:
sqlalchemy.exc.InvalidRequestError: in_() accepts either a list of expressions, a selectable, or an "expanding" bound parameter: Column('list_id', Text(), table=<MYTABLE>)
So I try to find a split to do something like:
filters.append(and_(Organisation.id.in_(Session.list_id.split(',')), Session.token == token))
But split, explode or every kind of dissociate function doesn't exist!
I try to use a more SQL way, but i always bump in the use of a function that doesn't exist!
filters.append(Organisation.id.in_(ds.query(Session.list_id).filter(Session.token == token)))
What is the best way to achieve this? My last idea is to create a table to store each id in single row, but it seems a little bit overkill for what i need. Or do it in a separate query.
Any hint will be appreciated.
Thanks
Related
I have a database which I need to return any value that matches the query or looks similar.
Using flask-sqlalchemy I can filter manually, but I'm having trouble getting a list of objects using list comprehensions or any other more pythonic method.
I have already tried to create a dict with all model columns (which I have a list ) and the search value passed to the filter_by query
column_dict_and_value = {column_name:'value' for column_name in columns}
a = model.query.filter_by(**column_dict_and_value).all()
...but doesn't even return an existing object, although
a = model.query.filter_by(column_name='value').all()
actually returns the object.
I tried filter(.like('%')) and it sort of works
a = model.query.filter(model.column_name.like('valu%')).all()
returns me a list of all the objects that matches that pattern but for that column, and I want to iterate over all columns. Basically a full blown search since I'm not sure what the user is going to look for and I want to show the object if the query exist on any column or a list of objects if the query is incomplete, like the * in any search.
I tried to use list comprehensions to iterate through the model attributes but I'm not allowed to do that apparently:
a = [model.query.filter(model.column.like('valu%')) for column in columns]
...this complains that column is not an attribute of the model, which makes sense due to the syntax, but I had to try anyway, innit?
I'm a newbie regarding databases and class objects so please be gentle. I tried to look for similar answers but I can't find the one that suits me. the filter_by(one_column, second_column, etc) doesn't look to me very pythonic and if for any reason I change the model i need to change the query too where as creating a dict comprehension of the model seems to me more foolproof.
SOLUTION
Based on calestini proposed answer I wrote this code that seems to do the trick. It also cleans the list because if all() doesn't have any result (since it looks in every field) it returns None and adds up to the list. I'd rather prefer it clean.
Note: All my fields are text. Please check the third proposed solution from calestini if yours differ. Not tested it though.
columns = [
"foo",
"bar",
"foobar"
]
def list_everything(search):
d = {column: search for column in columns}
raw = [
model.query.filter(getattr(model, col).ilike(f"{val}%")).all()
for col, val in d.items()
]
return [item for item in raw if item]
I'll keep optimizing and update this code if I come with a better solution. Thanks a lot
Probably the reason why you are not getting any result from
a = model.query.filter_by(**column_dict_and_value).all()
is because filter_by is testing for direct equality. In your case you are looking for a pattern, so you will need to loop and use filter as opposed to filter_by.
Assuming all your columns are String or Text types, you can try the following:
a = model.query
for col, val in column_dict_and_value.items():
a = a.filter(getattr(model, col).ilike(f'{val}%')) #ilike for case insensitive
a = a.all()
Or vs And
The issue can also be that in your case you could be testing for intersection but expecting union. In other words, you are returning something only if the pattern matches in all columns. If instead you want a result in case any column matches, then you need to tweak the code a bit:
condition = or_(*[getattr(model, col).ilike(f'{val}%') for col, val in column_dict_and_value.items()])
a = model.query.filter(codition).all()
Now if you actually have other data types among the columns, then you can try to first verify if the column type and then pass the same logic:
for col, val in column_dict_and_value.items():
## check if field python equivalent is string
if isinstance(class_.__table__.c[col].type.python_type, str):
...
I don't understand what is the meaning of distinct and how use of it. i have search for related answer but it seems like distinct is somehow related to list. really appreciate the help.
list_of_stocks = db.stocks.distinct("symbol")
As the OP confirmed, this is a PyMongo call to a MongoDB database, which allows for a distinct find, in the form of:
db.collection_name.distinct("property_name")
This returns all distinct values for a given property in a collection.
Optionally, if you specify a document filter (effectively a find() filter) as a second parameter, your query will first be reduced by that filter, then the distinct will be applied. For example:
list_of_stocks = db.stocks.distinct("symbol", {"exchange": "NASDAQ"})
Distinct keyword is used in DB and it is to return record set only with distinct elements for that particular column.
I'm using SQLAlchemy and I have a query from which one of the columns I obtain is a constant QUOTE_STATUS_ERROR, the values in this column are integers. Since the constant value doesn't mean anything to the end-user, I'd like to convert that value from within the query itself to show a string by mapping the values of that column to a dictionary that I have in the app using a function I have in place for that purpose already. I haven't been able to find a way to implement it since Columns in the query are object not value of the column itself. To make my question clear this is an example of what I have:
Query:
q = meta.session.query(MyModel.id, MyModel.quote_status).join(AnotherModel).subquery("q")
Function I want to use:
def get_status_names(status_value):
return QUOTE_STATUS_NAMES[status_value]
Is there a way to this directly from SQLAlchemy by attaching/passing a function (get_status_names()) to the column (MyModel.quote_status). If not what could be the best approach? I prefer not iterate over the values once I get the results in case the list of results is extensive. I would appreciate a push in the right direction.
UPDATE: I'm joining the resulting subquery with other tables
There are a few things you can do.
Off the top of my head...
If you just want to display things, you can use a property decorator:
QUOTE_STATUS__ID_2_NAME = {}
class MyModel(object):
id = Column()
quote_status_id = Column()
#property
def quote_status_string(self):
if self.quote_status_id:
return QUOTE_STATUS__ID_2_NAME[self.quote_status_id]
return None
If you want to render/accept strings and have sqlalchemy convert from string/int transparently, you can use a TypeDecorator -- http://docs.sqlalchemy.org/en/rel_0_9/core/types.html#custom-types
Personally, I usually go for the property decorator.
I'm using Python 3.2.3, with the MySQL/Connector 1.0.7 module. Is there a way to return the column names, if the MySQL query returns an empty result?
For example. Say I have this query:
SELECT
`nickname` AS `team`,
`w` AS `won`,
`l` AS `lost`
WHERE `w`>'10'
Yet, if there's nobody over 10, it returns nothing, obviously. Now, I know I can check if the result is None, but, can I get MySQL to return the column name and a NULL value for it?
If you're curious, the reason I'm wondering if this is possible, is because I'm dynamically building dict's based on the column names. So, the above, would end up looking something like this if nobody was over 10...
[{'team':None,'won':None,'lost':None}]
And looks like this, if it found 3 teams over 10...
[{'team':'Tigers','won':14,'lost':6},
{'team':'Cardinals','won':12,'lost':8},
{'team':'Giants','won':15,'lost':4}]
If this kind of thing is possible, then I won't have to write a ton of exception checks all over the code in case of empty dict's all over the place.
You could use a DESC table_name first, you should get the column names in the first column
Also you already know the keys in the dict so you can construct yourself and then append things to it if the result has values.
[{'team':None,'won':None,'lost':None}]
But what I fail to see why you need this. If you have a list of dictionaries, I am guessing you will have for loop operations. For loop will not do anything to a empty list, so you would not have to bother about exception checks
If you have to do something like result[0]['team'] then you should definitely check if len(result)>0
so I'm using mysql to grab data from a database and feeding it into a python function. I import mysqldb, connect to the database and run a query like this:
conn.query('SELECT info FROM bag')
x = conn.store_result()
for row in x.fetch_row(100):
print row
but my problem is that my data comes out like this (1.234234,)(1.12342,)(3.123412,)
when I really want it to come out like this: 1.23424, 1.1341234, 5.1342314 (i.e. without parenthesis). I need it this way to feed it into a python function. Does anyone know how I can grab data from the database in a way that doesn't have parenthesis?
Rows are returned as tuples, even if there is only one column in the query. You can access the first and only item as row[0]
The first time around in the for loop, row does indeed refer to the first row. The second time around, it refers to the second row, and so on.
By the way, you say that you are using mySQLdb, but the methods that you are using are from the underlying _mysql library (low level, scarcely portable) ... why??
You could also simply use this as your for loop:
for (info, ) in x.fetch_row(100):
print info