【问题标题】:How to do a sqlalchemy query using a string variable as table name?如何使用字符串变量作为表名进行 sqlalchemy 查询?
【发布时间】:2022-03-09 03:55:25
【问题描述】:

如果我不清楚,我提前道歉,英语不是我的母语。 如果我犯了太多错误,请随时告诉我:)

我是使用 flask_sqlalchemy 的新手,在互联网上花费数小时寻找答案后感到沮丧。

我想要的是做一个这样的查询:

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

但我不想使用 ModelName 来做类似的事情:

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

错误是:

'str' object has no attribute 'query'

在尝试将 db.session.add() 与字符串变量一起使用时,我当然会遇到同样的问题。

我知道查询需要某种 _BoundDeclarativeMeta 对象而不是字符串,但是如何从列表中获取该对象以将其传递给 for 循环?

我尝试使用 'db.metadata.tables[my_variable]' 但我得到一个 Table 对象而不是 _BoundDeclarativeMeta 对象,它的工作方式不一样。

我设法使用

进行查询
db.session.query(db.metadata.tables[my_variable])

而不是

my_variable.query

但我不能将该方法用于“db.session.add()”(或者有可能吗?)

有没有办法将字符串变量“转换”为正确类型的 sqlalchemy 对象?

【问题讨论】:

    标签: python-3.x flask-sqlalchemy


    【解决方案1】:

    我需要一种通过引用模型的表名进行查询的方法。这是我想出的:

    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)
    

    然后,像这样使用它:

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

    重要的是db.Model.__subclasses__() - 给出模型类(对象?)的列表。

    【讨论】:

      【解决方案2】:

      试试这个:

      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()  
      

      【讨论】:

        【解决方案3】:

        希望还为时不晚。我想你可以试试这个:

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

        Model1 和 Model2 是用户、角色等类。

        【讨论】:

          【解决方案4】:

          如果您使用的是 python > 2.7,我认为 importlib 可能会对您有所帮助

          importlib.import_module('module to import')
          

          对于您的情况: 如果您的项目结构如下所示,并且 models.py 定义了所有 OR 映射的类。

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

          代码应该是这样的:

          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()
          

          【讨论】:

          • 感谢您的回复 :) 但我真的不知道“the_module”使用哪个模块来获取正确类型的对象。
          【解决方案5】:

          解释@timothyh 给出的答案,因为我没有足够的声誉点来发表评论,并相信它对某人有用。我使用抽象表并依赖类继承。必须包括:

          if hasattr(table,'__tablename__')
          

          到字典理解的末尾,否则会出错。然后,确保您继承了抽象表和基础模型。

          例如:

          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"
          

          还有我的功能:

          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)
              
          

          【讨论】:

            【解决方案6】:

            我设法得到了我想要的东西

            eval(my_variable)
            

            似乎 eval() 允许我获取一个由变量值命名的 db 对象,而不是一个普通的字符串对象。

            我看到了有关使用 eval() 的安全警告,但我想这没关系,因为评估的变量不是来自用户输入。

            当然,如果有人看到更好的方法来做同样的事情,我会很高兴知道。

            【讨论】:

            • eval 不安全
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多