Рубин на рельсы комплекс инструкция Select...там должен быть лучший способ!


Я строю запрос, который может в конечном итоге выглядеть так:

SELECT "patients".* FROM "patients" 
INNER JOIN "users" ON "users"."id" = "patients"."user_id" 
WHERE (users.username LIKE '%Bob%' AND users.last_name LIKE '%Smith%' 
AND users.active = '1' AND users.disabled = '1') 
ORDER BY users.first_name DESC LIMIT 10 OFFSET 0

Ниже приведен код для создания сложных запросов, например:

conditions = []
where = ''
where_parameters = []
order = 'users.last_name ASC'
per_page = 20

if is_admin?
  if defined?(params[:username]) and !params[:username].blank?
    where += 'users.username LIKE ? AND '
    where_parameters.push("%#{params[:username]}%")
  end

  if defined?(params[:last_name]) and !params[:last_name].blank?
    where += 'users.last_name LIKE ? AND '
    where_parameters.push("%#{params[:last_name]}%")
  end

  if defined?(params[:active]) and !params[:active].blank?
    where += 'users.active = ? AND '
    where_parameters.push(params[:active])
  end

  if defined?(params[:disabled]) and !params[:disabled].blank?
    where += 'users.disabled = ? AND '
    where_parameters.push(params[:disabled])
  end

  if !where.empty?
    where = where[0, where.length - 5]
    conditions = [where, *where_parameters]
  end

  if defined?(params[:order_by]) and !params[:order_by].blank?
    order = params[:order_by] + ' '
  end

  if defined?(params[:order]) and !params[:order].blank?
    order += params[:order]
  end

  if defined?(params[:per_page]) and !params[:per_page].blank?
    per_page = params[:per_page].to_i
  end
  @patients = Patient.joins(:user).paginate(:all, :include =>:user, :conditions => conditions, :order => order, :page => params[:page], :per_page => per_page) 

Есть ли лучший способ сделать это, это просто некрасиво?



2764
4
задан 24 февраля 2011 в 10:02 Источник Поделиться
Комментарии
1 ответ

В первую очередь быть очень внимательным при построении запросов с данными запроса. Когда дом свой , где строки, вы используете параметризованные запросы, так это нормально. Но в того, строку, вы создаете фактическое SQL-кода от данных, которые вы непосредственно вывезти из параметров. Не делай этого. Это большой риск SQL-инъекций.

Что вы должны сделать, это принять заказ строки друг от друга (разделение на запятую), а затем проверить, что каждый элемент представляет собой имя столбца может идти по возрастанию или убыванию, затем поставить тетиву вместе соответственно.

Способ для этого может выглядеть так:

def safe_order_from_param(collection, order_string)
tokens = order_string.split(/ *, */)
tokens.each do |token|
column, direction = token.match(/^(\w+)(? +(\w+))?$/).captures
if Patient.column_names.include? column
# if direction is nil, it will just be turned into the empty
# string, so no problem there
collection = collection.order("#{column} #{direction}")
else
raise ArgumentError, "No such column: #{column}"
end
end
end


Во-вторых определенными?(параметр params[:фу]) делает, это проверить, что параметры определены. Это никак не проверить, что параметр params хэш и ключ :ФОО. Поскольку параметры будут всегда существовать, там действительно нет необходимости для проверки. Проверив параметр params[:фу].пустой? совершенно достаточно.


Теперь к основной части вашего вопроса: уродливое здание строке запроса:

Я исхожу из того, что вы используете соединяет метод, что это рельсы 3. В Rails 3 опции, такие как :включить, :условия и :того, являются устаревшими в пользу соответствующих методов запроса. Красоту этих методов является то, что они змеевидных. Поэтому вместо того, чтобы строить строку запроса с помощью конкатенации, это намного чище, чтобы просто связать между собой несколько , гдес.

if is_admin?
@patients = Patient.joins(:user).includes(:user).order('users.last_name ASC')

unless params[:username].blank?
@patients = @patients.where("users.username LIKE ?", params[:username])
end

# same for :last_name

unless params[:active].blank?
@patients = @patients.where("users.active" => params[:active])
end

# same for disabled

unless params[:order_by].blank?
@patients = safe_order_from_param(@patients, params[:order_by])
end

# same for order

per_page = params[:per_page].blank? ? 20 : params[:per_page].to_i
@patients = @patients.paginate(:page => params[:page], :per_page => per_page)
end

7
ответ дан 24 февраля 2011 в 11:02 Источник Поделиться