【问题标题】:Is SQLAlchemy still recommended if only used for raw sql query?如果仅用于原始 sql 查询,是否仍然推荐使用 SQLAlchemy?
【发布时间】:2012-07-17 14:24:24
【问题描述】:

使用 Flask,我很想知道 SQLAlchemy 是否仍然是使用原始 SQL(直接SELECT x FROM table WHERE ...)而不是使用 ORM 查询我的数据库的最佳方法,或者是否有更简单但功能强大的替代方法?

感谢您的回复。

【问题讨论】:

    标签: python orm sqlalchemy flask


    【解决方案1】:

    我一直使用 SQLAlchemy 进行直接查询。

    主要优势:它为您提供针对 SQL 注入攻击的最佳保护。 SQLAlchemy 做正确的事,无论你给它什么参数。

    我发现它还可以根据条件调整生成的 SQL。显示带有多个过滤器控件的结果集?只需在一组 if/elif/else 结构中构建您的查询,您就知道您的 SQL 仍然是黄金。

    以下是一些实时代码的摘录(旧的 SA 版本,因此语法可能会有所不同):

    # Pull start and end dates from form
    # ...
    # Build a constraint if `start` and / or `end` have been set.
    created = None
    if start and end:
        created = sa.sql.between(msg.c.create_time_stamp, 
            start.replace(hour=0, minute=0, second=0),
            end.replace(hour=23, minute=59, second=59))
    elif start:
        created = (msg.c.create_time_stamp >= 
                   start.replace(hour=0, minute=0, second=0))
    elif end:
        created = (msg.c.create_time_stamp <= 
                   end.replace(hour=23, minute=59, second=59))
    
    # More complex `from_` object built here, elided for example
    # [...]
    # Final query build
    query = sa.select([unit.c.eli_uid], from_obj=[from_])
    query = query.column(count(msg.c.id).label('sent'))
    query = query.where(current_store)
    if created:
        query = query.where(created)
    

    此代码的来源要复杂得多,但我想在此处突出显示日期范围代码。如果我必须使用字符串格式构建 SQL,我可能会在某处引入 SQL 注入孔,因为它更容易忘记引用值。

    【讨论】:

      【解决方案2】:

      在完成我的一个小项目后,我决定尝试只使用 MySQLDB,而不使用 SQL Alchemy。

      它工作得很好,而且很容易使用,这是一个例子(我创建了一个小类来处理数据库的所有工作)

      import MySQLdb
      from MySQLdb.cursors import DictCursor
      
      class DatabaseBridge():
          def __init__(self, *args, **kwargs):
              kwargs['cursorclass'] = DictCursor
              self.cnx = MySQLdb.connect (**kwargs)
              self.cnx.autocommit(True)
              self.cursor = self.cnx.cursor()
      
          def query_all(self, query, *args):
              self.cursor.execute(query, *args)
              return self.cursor.fetchall()
      
          def find_unique(self, query, *args):
              rows = self.query_all(query, *args);
              if len(rows) == 1:
                  return rows[0]
      
              return None
      
          def execute(self, query, params):
              self.cursor.execute(query, params)
              return self.cursor.rowcount
      
          def get_last_id(self):
              return self.cnx.insert_id()
      
          def close(self):
              self.cursor.close()
              self.cnx.close()
      
      database = DatabaseBridge(**{
              'user': 'user',
              'passwd': 'password',
              'db': 'my_db'
          })
      
      rows = database.query_all("SELECT id, name, email FROM users WHERE is_active = %s AND project = %s", (1, "My First Project"))
      

      (这是一个愚蠢的例子)。

      它就像一个魅力但是你必须考虑到这些:

      • 不支持多线程!如果您不使用 Python 中的 multiprocessing 也没关系。
      • 您不会拥有 SQLAlchemy 的所有优势(数据库到类(模型)包装器、查询生成(select、where、order_by 等))。 这是您希望如何使用数据库的关键点。

      但另一方面,就像 SQLAlchemy 一样,有针对 SQL 注入攻击的保护措施:

      一个基本的查询应该是这样的:

      cursor.execute("SELECT * FROM users WHERE data = %s" % "Some value") # THIS IS DANGEROUS
      

      但你应该这样做:

      cursor.execute("SELECT * FROM users WHERE data = %s", ("Some value",)) # This is secure!
      

      看到区别了吗?再读一遍;)

      不同之处在于我将% 替换为, :我们将参数作为...参数传递给执行,这些都被转义了。当使用% 时,参数不会被转义,从而启用 SQL 注入攻击!

      这里的最后一句话是,这取决于您的使用情况以及您打算如何处理您的项目。对我来说,SQLAlchemy 太过分了(它是一个基本的 shell 脚本!),所以 MysqlDB 是完美的。

      【讨论】:

        猜你喜欢
        • 2012-08-05
        • 1970-01-01
        • 1970-01-01
        • 2012-06-28
        • 1970-01-01
        • 2017-01-10
        • 1970-01-01
        • 1970-01-01
        • 2021-03-17
        相关资源
        最近更新 更多