12

I apologize in advance if I am not clear, English is not my native language. Feel free to tell me if I make too many mistakes :)

I am a newbie in using flask_sqlalchemy and get frustrated after spending hours on Internet searching for an answer.

What I want is doing a query like that one :

ModelName.query.filter_by(name='something').all() 

But instead of using ModelName I want to do something like that :

model_list = ['Model1', 'Model2', 'Model3'] for model in model_list: some_var = model.query.filter_by(name='something').all() # do some stuff with the variable 

The error is :

'str' object has no attribute 'query' 

I have of course the same kind of problem when trying to use db.session.add() with a string variable.

I understand that the query requires some kind of _BoundDeclarativeMeta object instead of a string but then how to get that object from a list to pass it to a for loop ?

I tryed using 'db.metadata.tables[my_variable]' but I get a Table object instead of a _BoundDeclarativeMeta object and it doesn't work the same.

I manage to do a query using

db.session.query(db.metadata.tables[my_variable]) 

instead of

my_variable.query 

but I can't use that method for the 'db.session.add()' (or is it possible ?)

Is there any way to 'cast' the string variable into the right type of sqlalchemy object ?

6 Answers 6

5

I needed a way to query by referring to the model's tablename. This is what I came up with:

class Model1(db.Model): __tablename__ = 'table1' # or whatever, doesn't matter class Model2(db.Model): __tablename__ = 'table2' tables_dict = {table.__tablename__: table for table in db.Model.__subclasses__()} def table_object(table_name): return tables_dict.get(table_name) 

Then, use it like this:

model_list = ['table1', 'table2'] for model in model_list: some_var = db.session.query(table_object(table_name=model)).filter_by(name='something').all() 

The important bit is db.Model.__subclasses__() - gives a list of the model classes (objects?).

Sign up to request clarification or add additional context in comments.

Comments

3

Hope it's not too late. I think you can try this:

class Model1: pass class Model2: pass model_list = [Model1, Model2] for model in model_list: db.session.query(model) 

Model1 and Model2 are classes like Users, Roles ,etc.

1 Comment

Although this works and should suffice, OP asked "using strings as table names", and used Models objects.
3

Expounding on the answer that @timothyh gave because I don't have enough reputation points to comment and believe it will be useful for someone. I use abstract tables and rely on class inheritance. It is necessary to include:

if hasattr(table,'__tablename__') 

to the end of the dictionary comprehension, otherwise an error will occur. Then, ensure you inherit both your abstract table as well as a Base Model.

For example:

class MasterTable(db.Model): __abstract__ = True column_1 = some_column_schema column_2 = some_column_schema column_x = some_column_schema class SimilarTable(MasterTable,db.Model): __tablename__ = 'SubTab_1' class OtherSimilarTable(MasterTable,db.Model): __tablename__ = 'SubTab_2" 

And my function:

def get_table_class(tablename): '''Uses a dictionary that will contain table names as key and Model object as value. Returns the db.Model object that can be queried.''' my_tables_dict = {table.__tablename__: table for table in db.Model.__subclasses__() if hasattr(table,'__tablename__')} return my_tables_dict.get(tablename) 

Comments

2

I think importlib may help you here if you are using python > 2.7

importlib.import_module('module to import') 

For your case: If your project structure looks like below and models.py defines all the OR mapped classes.

a | + - __init__.py - b | + - __init__.py - models.py 

The code should be like:

model_list = ['Model1', 'Model2', 'Model3'] the_module = importlib.import_module('module to import') # eg. importlib.import_module('a.b.models') for model in model_list: the_class = getattr(the_module, model) some_var = the_class.query.filter_by(name='something').all() 

1 Comment

Thank you for responding :)but I dont really know which module use for 'the_module' to get the right type of object.
2

Try this:

mapping_str_to_object = {} for model in BaseModel._decl_class_registry.values(): if hasattr(model, '__tablename__'): mapping_str_to_object[model.__tablename__] = model model_list = ['Model1', 'Model2', 'Model3'] for model in model_list: some_var = mapping_str_to_object[model].query.filter_by(name='something').all() 

Comments

-5

I sort of managed to get what I wanted using

eval(my_variable) 

It seems that eval() allow me to get a db object named by the variable value rather than a plain string object.

I saw security warnings about the use of eval() but I gess it's OK since the evaluated variable doesn't come from user input.

Of course if anyone sees a better way to do the same thing I would be glad know it.

1 Comment

eval is insecure

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.