Почему модель.поведение поиска в развернутой среде отличается от поведения в среде разработки?

StackOverflow https://stackoverflow.com/questions/2214367

Вопрос

Используя Ruby on Rails в сочетании с capistrano и git, я столкнулся с досадной проблемой..

У меня есть контроллер "people" с индексным действием, которое выглядит примерно так:

def index
  @people = Person.find( :conditions => params[:search] )
end

В таблице Person есть логический столбец "is_admin".Предполагая, что некоторые люди являются администраторами, а некоторые нет, вызов get для http://localhost:3000/people ?поиск[is_admin]=истина следует заполнить @люди с некоторыми пользователями...И это верно для моего локального компьютера, когда я запускаю приложение в развитие режим..

Но....Когда я выполняю развертывание в своей учетной записи сервера ( railsplayground ), вызов http://mydomain.com/people ?поиск[is_admin]=истина не удается найти ни одного подходящего элемента.Однако, если я изменю ...?поиск[is_admin]=true Для ...?поиск[is_admin]=1 ответ возвращает пользователей-администраторов, как и ожидалось...

Вернувшись на мой локальный компьютер, использование "1" вместо "true" завершается неудачей.

Суть в том, что

Person.find( :all, :conditions => { :is_admin => 'true' } )

работает в моей среде разработки, и

Person.find( :all, :conditions => { :is_admin => 1 } )

работает в моей развернутой среде.

Почему это происходит?и как я могу это исправить?

В идеале я хотел бы размещать ссылки типа:

link_to( "Administrators", {
  :controller => '/people',
  :action => :index,
  :search => { :is_admin => true }
})

и получите списки администраторов :).

Возможно, стоит отметить, что моя база данных разработки представляет собой файл sqlite3, а production - это база данных mysql...

Редактировать:Я понимаю возражения против пользовательского ввода, но в данном исключительном случае это очень, очень незначительная угроза.Кроме того, мой текущий код отлично работает либо на моем компьютере разработки, либо в моей производственной учетной записи, но не на обоих.Кажется, самое простое решение изменить способ сохранения и интерполяции логических значений sqlite3, поэтому я бы изменил свой вопрос на "как мне изменить способ сохранения логических значений sqlite"...Если бы sqlite идеально имитировал поведение mysql, это идеально соответствовало бы потребностям моих разработок...

Это было полезно?

Решение

Проблема в том, что вы передаете логическое значение в виде строки, и окончательное поведение зависит от активной базы данных.Вот более подробное объяснение.

Когда вы читаете params[:search] переменная, содержимое которой представляет собой строку, и у нее нет типа.Это происходит потому, что строка запроса не может понять, является ли

params[:search][:is_admin] = "true"

на самом деле означает

params[:search][:is_admin] = "true"
params[:search][:is_admin] = true

Аналогично, когда вы проходите 1, вы в конечном итоге получаете

params[:search][:is_admin] = "1"

что отличается от

params[:search][:is_admin] = 1

Когда вы передаете значение в запрос, поскольку вы не передаете логическое значение, значение не преобразуется адаптером базы данных.Ваш окончательный результат запроса выглядит примерно так

SELECT * FROM `persons` WHERE `persons.is_admin` = 'true'

SQLite3 хранит логическое значение в виде t / f строк. true хранится как 't' и false хранится как 'f'.Я предполагаю, что он также понимает значение true / false и автоматически переводит ваш запрос.Напротив, MySQL понимает только 0/1 и true / false, и это приводит к сбою.

Когда вы переключаете поведение, передавая 1 вместо того , чтобы true, вы вызываете сбой SQLite, потому что он не может перевести строку "1" Для 't'.Напротив, MySQL может, и это делает.

В обоих случаях вы делаете это неправильно.Вы должны передавать не строку, а логическое значение.Кроме того, вы никогда не должны доверять пользовательскому вводу.

Мое предложение состоит в том, чтобы нормализовать ваш params[:search] перед кормлением Person.find.

Другие советы

Я думаю, что разница между использованием sqlite и mysql является причиной вашей проблемы.

Но разрешение передавать active record conditioned непосредственно из строки запроса кажется мне плохой идеей и может оставить ваше приложение открытым для атак с использованием sql-инъекций.

Ответ @Simone объясняет, почему у вас такое поведение.Чтобы получить правильный результат как в sqlite3, так и в mysql, вы должны передать:

Person.find( :all, :conditions => { :is_admin => true } )

Если у вас будет только один уровень параметров (у вас есть params[:search][:something] но ничего глубже), чем вы можете создать правильный хэш с такими условиями, как это:

my_conditions = Hash.new
params[:search].each do |item, value|
  my_conditions[item.to_sym] = value == 'true' ? true : value == 'false' ? false : value
end

Он выполнит итерацию по всем параметрам поиска и изменит все 'true' Для true и 'false' Для false.Если будет другое значение, оно оставит его таким, какое оно есть.Конечно, вы можете поместить сюда любую логику, какую пожелаете.Если это станет сложнее, чем вы можете переписать его с помощью if или с switch.

Чем ты можешь:

Person.all(:conditions => my_conditions)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top