为什么部署环境中的 Model.find 行为与开发环境中的行为不同?

发布于 2024-08-20 03:48:58 字数 1532 浏览 6 评论 0原文

将 Ruby on Rails 与 capistrano 和 git 结合使用,我遇到了一个恼人的问题。

我有一个控制器“people”,其索引操作如下所示:

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

Person 表中有一个布尔值“is_admin”列。假设有些人是管理员,有些人不是,则调用 http ://localhost:3000/people?search[is_admin]=true 应该用一些用户填充 @people...这对于我的本地电脑来说是正确的,当我在开发模式下运行应用程序..

但是....当我部署到我的服务器帐户(railsplayground)时,调用http://mydomain.com/people?search[is_admin]=true 找不到任何匹配的项目。 但是,如果我将 ...?search[is_admin]=true 更改为 ...?search[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 文件,而生产数据库是一个 mysql 数据库...

编辑:我理解对用户输入的反对,但在这种特殊情况下,这是一个非常非常小的威胁。另外,我当前的代码在我的开发电脑或我的生产帐户中都可以完美运行,但不能同时在两者上运行。最简单的解决方案似乎改变了 sqlite3 保存和解释布尔值的方式,所以我将我的问题更改为“如何更改 sqlite 保存布尔值的方式”...如果 sqlite 能够完美地模仿 mysql 的行为,它将服务于我的开发需要完美...

Using Ruby on Rails combined with capistrano and git, I've run into an annoying problem..

I have a controller "people" with the index action that looks something like this:

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

There is a boolean "is_admin" column in the Person table. Assuming that some of the people are admin and some are not, a get call to http://localhost:3000/people?search[is_admin]=true should populate @people with some users... And this is true for my local pc when I run the app in development mode..

But.... When I deploy to my server account ( railsplayground ), a call to http://mydomain.com/people?search[is_admin]=true fails to find any matching items.
However, if I change ...?search[is_admin]=true to ...?search[is_admin]=1 the response returns the administrator users as expected...

Back on my local pc, the use of "1" instead of "true" fails.

The bottom line is that

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

works my development environment, and

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

works in my deployed environment.

Why is this? and how can I fix it?

Ideally I would like to place links like:

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

and get a lists of administrators :).

Might be worthy to note that my development database is a sqlite3 file, and production is a mysql database...

EDIT: I understand the objections to user-input, but in this exceptional case it is a very very minor threat. Also, my current code works perfectly in either my development pc, or my production account, but not on both. The easiest sollution seems to change the way sqlite3 saves and interperets boolean values, so I would change my question to "how do I change the way sqlite saves boolean values"... If sqlite would mimic mysql's behavior perfectly, it would serve my developments needs perfectly...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

手心的温暖 2024-08-27 03:48:58

问题是您将布尔值作为字符串传递,最终行为取决于活动数据库。这是更详细的解释。

当您读取 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'。我想它也能理解真/假,并自动翻译您的查询。
相反,MySQL 只理解 0/1 和 true/false,并且失败。

当您切换行为并传递 1 而不是 true 时,会导致 SQLite 失败,因为它无法转换字符串 "1"'t'。相反,MySQL 可以而且确实做到了。

在这两种情况下,你都做错了。您不应该传递字符串,而应该传递布尔值。另外,您永远不应该相信用户输入。

我的建议是在提供 Person.find 之前规范化您的 params[:search]

The problem is that you are passing a boolean value as string and the final behavior depends on the active database. Here's a more detailed explanation.

When you read the params[:search] variable, the content is a string and there's no type. This is because the query string can't understand whether

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

actually means

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

Likewise, when you pass 1 you end up with

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

which is different than

params[:search][:is_admin] = 1

When you pass the value to the query, because you are not passing a boolean, the value is not translated by the database adapter. Your final query result in something like

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

SQLite3 stores boolean as t/f strings. true is stored as 't' and false is stored as 'f'. I guess it also understands true/false and it automatically translates your query.
On the opposite, MySQL understands only 0/1 and true/false and it fails.

When you switch the behavior, passing 1 instead of true, you are causing SQLite to fail because it can't translate the string "1" to 't'. On the opposite, MySQL can and it does.

In both case, you are doing it wrong. You shouldn't pass a string but a boolean value. Also, you should never trust user input.

My suggestion is to normalize your params[:search] before feeding Person.find.

左耳近心 2024-08-27 03:48:58

我认为使用 sqlite 和 mysql 之间的差异是导致你的问题的原因。

但是,允许直接从查询字符串传入条件活动记录对我来说似乎是一个坏主意,并且可能会使您的应用程序容易受到 SQL 注入攻击。

I think the difference between using sqlite and mysql is the cause of your issue.

But allowing active record conditioned to be passed in directly from the query string seems like a bad idea to me, and could leave your app open to sql injection attacks.

半寸时光 2024-08-27 03:48:58

@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' 更改为 假。如果还有其他价值,它将保持原样。当然,您可以在这里放置任何您想要的逻辑。如果它变得比您更复杂,可以使用 ifswitch 重写它。

比你可以:

Person.all(:conditions => my_conditions)

@Simone answer explains why you get such a behavior. To get correct result in both sqlite3 and mysql you should pass:

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

If you will have only one level of parameters (you have params[:search][:something] but nothing deeper) than you can create correct hash with conditions like this:

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

It will iterate over all search params and will change all 'true' to true and 'false' to false. If there will be other value it will leave it as it is. Of course you can put here any logic you want. If it will get more complicated than you can rewrite it with if or with switch.

Than you can:

Person.all(:conditions => my_conditions)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文