Подтвердить что ты не робот

Является ли SQLAlchemy по-прежнему рекомендуемым, если он используется только для сырого SQL-запроса?

С помощью Flask мне интересно узнать, является ли SQLAlchemy лучшим способом для запроса моей базы данных с необработанным SQL (direct SELECT x FROM table WHERE ...) вместо использования ORM или если есть более простая, но мощная альтернатива?

Спасибо за ваш ответ.

4b9b3361

Ответ 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"))

(Это тупой пример).

Он работает как шарм НО, вы должны учитывать это:

  • Многопоточность не поддерживается! Это нормально, если вы не работаете с multiprocessing с Python.
  • У вас не будет всех преимуществ 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!

Видел разницу? Прочтите еще раз;)

Разница в том, что я заменил % на ,: Мы передаем аргументы как... аргументы в execute, и они экранированы. При использовании % аргументы не экранируются, а активируются атаки SQL Injection!

Последнее слово здесь заключается в том, что это зависит от вашего использования и того, что вы планируете делать с вашим проектом. Для меня SQLAlchemy был переполнен (это базовая оболочка script!), Поэтому MysqlDB был идеальным.