update_attribute 的参数在 SQL 语句中发生变化
Arguments to update_attribute change in SQL statement
要点
此方法调用:
user.update_attribute('image_url', 'arch.png')
生成此 SQL 语句:
UPDATE "users" SET "image_url" = ?, [["image_url", "rock.jpg"]]
SQL 声明不是预期的结果。
期望的结果是这个 SQL 语句:
UPDATE "users" SET "image_url" = ?, [["image_url", "arch.png"]]
请注意,生成的 SQL 语句对 image_url
的参数有误。
那怎么可能呢?
我该如何解决?
背景
我使用 form_for
标签生成一个表单,用户可以在其中更改个人资料图片。表单向调用另一个方法的控制器发送一个 post 请求,并传递参数。最后一个方法在记录上调用 update_attribute
,保存更改。
我正在为图像使用 gem“载波”。
调试
我已经进行了一些调试,看起来一切顺利,直到最后一个方法调用 update_attribute
。该调用生成的 SQL 语句不正确。
仅供参考,旧图片文件名为“rock.jpg”,新图片文件名为“arch.png”。
大意是执行这两行:
puts "\n\nSending #{attribute} and #{value} to update_attribute\n\n"
if user.update_attribute(attribute, value)
这导致:
Sending image_url and arch.png to update_attribute
UPDATE "users" SET "image_url" = ?, WHERE "users"."id" = ? [["image_url", "rock.jpg"], [...]]
我删除了生成的 SQL 语句的 updated_at 部分,因为它不重要。完整的 SQL 语句以及助手的更多输出可以在下面看到。
- SQL语句中的
image_url
与传递给它的值有何不同?
日志的详细输出
Received update request!!!!!
---------------------------
Attribute - image_url
Value - arch.png
--------------------------
Sending image_url and arch.png to update_attribute
(0.1ms) begin transaction
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 186]]
SQL (0.2ms) UPDATE "users" SET "image_url" = ?, "updated_at" = ? WHERE "users"."id" = ? [["image_url", "rock.jpg"], ["updated_at", "2016-04-27 01:25:49.476024"], ["id", 186]]
(90.4ms) commit transaction
代码
形式:
<%= form_for(@current_member, html: {id: 'image_form'}) do %>
<%= file_field_tag :image_url,
type: 'file', html: {id: 'file_field'} %>
<%= hidden_field_tag 'updateParam', 'image_url' %>
<%= submit_tag %>
<% end %>
控制器:
def update
update_account(@current_member, params[:updateParam], params)
end
帮手:
def update_account(user, attribute, parameters)
puts "\n\n\n\n\n"
puts "Received update request!!!!!\n
puts "---------------------------\n"
puts "Attribute - #{attribute}\n"
puts "Value - #{parameters[attribute]}\n"
puts "--------------------------\n\n\n"
if editable attribute
if valid(attribute, parameters)
value = parameters[attribute]
puts "\n\nSending #{attribute} and #{value} to update_attribute\n\n"
if user.update_attribute(attribute, value)
# ...
# ...
要点
此方法调用:
user.update_attribute('image_url', 'arch.png')
生成此 SQL 语句:
UPDATE "users" SET "image_url" = ?, [["image_url", "rock.jpg"]]
SQL 声明不是预期的结果。
期望的结果是这个 SQL 语句:
UPDATE "users" SET "image_url" = ?, [["image_url", "arch.png"]]
请注意,生成的 SQL 语句对 image_url
的参数有误。
这怎么可能?
我该如何解决?
扩展评论回答:
看起来,您的模型中有一些回调正在改变 save/update 之前的 value
。
要跳过回调,您可以使用 update_column
而不是 update_attribute
。
注意:update_column
也会跳过验证并且不会更新 updated_at
属性。
作者编辑
这个答案是正确的,回调将结果更改为可以保存在数据库中的结果。
使用 update_column
我能够看到我的参数已正确传递,但并不正确。 carrierwave
gem 使用回调来检查参数是否有效,如果无效,它将值恢复为调用前的值。如果该值有效(上传文件),则它将文件保存在 save_dir 中并将值更改为 url 以指向该文件。
我用它来进一步调试,发现我传递给 update_attribute
的值无效,因为 form_for
没有包含 multipart: true
,这是表单所必需的使用载波上传图像。添加这个解决了问题。
我不得不重新使用 update_attribute
而不是 update_column
,这样 carrierwave
的回调仍然会被执行并且上传的文件会保存在数据库中。
你的问题有很多混乱,这使得阅读和帮助变得困难。
这种 bug 搜寻的目标是减少所涉及的元素,尽可能少地担心。
绕过Rails
运行 SQL 控制台中的语句。只是为了确保您没有任何花哨的触发器和功能。从您显示的输出来看,情况并非如此。但是......它花费你1分钟。并给你一些安全感。
绕过View/Controller
运行 Rails 控制台上的代码。我倾向于将所有内容写在一行中,并在前面加上 reload!
语句。因此可以快速检查代码中的更改:
reload!; user = User.find(12345); user.update_attribute('image_url', 'arch.png'); User.find(12345).image_url
它打印什么?
我认为您的问题源于回调。所以它应该已经为 image_url 打印了错误的值。所以你可以确定问题不是由 View/Controller 层引起的。
绕过视图
如果一切正常,那么您可以确定问题出在View/Controller的某个地方。在这种情况下,我不会使用 params
来构建更新语句,而是使用硬编码值。像这样你绕过了视图层。
绕过回调
使用update_column
代替update_attribute
回调
第一:不要使用回调。第二:不要使用它们。如果你使用它们,请确保你知道你在做什么:-)
如果update_column
解决了问题,那么可能是回调的原因。
在您的代码中搜索属性名称和回调名称,看看是否有。
要点
此方法调用:
user.update_attribute('image_url', 'arch.png')
生成此 SQL 语句:
UPDATE "users" SET "image_url" = ?, [["image_url", "rock.jpg"]]
SQL 声明不是预期的结果。
期望的结果是这个 SQL 语句:
UPDATE "users" SET "image_url" = ?, [["image_url", "arch.png"]]
请注意,生成的 SQL 语句对 image_url
的参数有误。
那怎么可能呢?
我该如何解决?
背景
我使用 form_for
标签生成一个表单,用户可以在其中更改个人资料图片。表单向调用另一个方法的控制器发送一个 post 请求,并传递参数。最后一个方法在记录上调用 update_attribute
,保存更改。
我正在为图像使用 gem“载波”。
调试
我已经进行了一些调试,看起来一切顺利,直到最后一个方法调用 update_attribute
。该调用生成的 SQL 语句不正确。
仅供参考,旧图片文件名为“rock.jpg”,新图片文件名为“arch.png”。
大意是执行这两行:
puts "\n\nSending #{attribute} and #{value} to update_attribute\n\n"
if user.update_attribute(attribute, value)
这导致:
Sending image_url and arch.png to update_attribute
UPDATE "users" SET "image_url" = ?, WHERE "users"."id" = ? [["image_url", "rock.jpg"], [...]]
我删除了生成的 SQL 语句的 updated_at 部分,因为它不重要。完整的 SQL 语句以及助手的更多输出可以在下面看到。
- SQL语句中的
image_url
与传递给它的值有何不同?
日志的详细输出
Received update request!!!!!
---------------------------
Attribute - image_url
Value - arch.png
--------------------------
Sending image_url and arch.png to update_attribute
(0.1ms) begin transaction
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 186]]
SQL (0.2ms) UPDATE "users" SET "image_url" = ?, "updated_at" = ? WHERE "users"."id" = ? [["image_url", "rock.jpg"], ["updated_at", "2016-04-27 01:25:49.476024"], ["id", 186]]
(90.4ms) commit transaction
代码
形式:
<%= form_for(@current_member, html: {id: 'image_form'}) do %>
<%= file_field_tag :image_url,
type: 'file', html: {id: 'file_field'} %>
<%= hidden_field_tag 'updateParam', 'image_url' %>
<%= submit_tag %>
<% end %>
控制器:
def update
update_account(@current_member, params[:updateParam], params)
end
帮手:
def update_account(user, attribute, parameters)
puts "\n\n\n\n\n"
puts "Received update request!!!!!\n
puts "---------------------------\n"
puts "Attribute - #{attribute}\n"
puts "Value - #{parameters[attribute]}\n"
puts "--------------------------\n\n\n"
if editable attribute
if valid(attribute, parameters)
value = parameters[attribute]
puts "\n\nSending #{attribute} and #{value} to update_attribute\n\n"
if user.update_attribute(attribute, value)
# ...
# ...
要点
此方法调用:
user.update_attribute('image_url', 'arch.png')
生成此 SQL 语句:
UPDATE "users" SET "image_url" = ?, [["image_url", "rock.jpg"]]
SQL 声明不是预期的结果。
期望的结果是这个 SQL 语句:
UPDATE "users" SET "image_url" = ?, [["image_url", "arch.png"]]
请注意,生成的 SQL 语句对 image_url
的参数有误。
这怎么可能?
我该如何解决?
扩展评论回答:
看起来,您的模型中有一些回调正在改变 save/update 之前的 value
。
要跳过回调,您可以使用 update_column
而不是 update_attribute
。
注意:update_column
也会跳过验证并且不会更新 updated_at
属性。
作者编辑
这个答案是正确的,回调将结果更改为可以保存在数据库中的结果。
使用 update_column
我能够看到我的参数已正确传递,但并不正确。 carrierwave
gem 使用回调来检查参数是否有效,如果无效,它将值恢复为调用前的值。如果该值有效(上传文件),则它将文件保存在 save_dir 中并将值更改为 url 以指向该文件。
我用它来进一步调试,发现我传递给 update_attribute
的值无效,因为 form_for
没有包含 multipart: true
,这是表单所必需的使用载波上传图像。添加这个解决了问题。
我不得不重新使用 update_attribute
而不是 update_column
,这样 carrierwave
的回调仍然会被执行并且上传的文件会保存在数据库中。
你的问题有很多混乱,这使得阅读和帮助变得困难。 这种 bug 搜寻的目标是减少所涉及的元素,尽可能少地担心。
绕过Rails
运行 SQL 控制台中的语句。只是为了确保您没有任何花哨的触发器和功能。从您显示的输出来看,情况并非如此。但是......它花费你1分钟。并给你一些安全感。
绕过View/Controller
运行 Rails 控制台上的代码。我倾向于将所有内容写在一行中,并在前面加上 reload!
语句。因此可以快速检查代码中的更改:
reload!; user = User.find(12345); user.update_attribute('image_url', 'arch.png'); User.find(12345).image_url
它打印什么? 我认为您的问题源于回调。所以它应该已经为 image_url 打印了错误的值。所以你可以确定问题不是由 View/Controller 层引起的。
绕过视图
如果一切正常,那么您可以确定问题出在View/Controller的某个地方。在这种情况下,我不会使用 params
来构建更新语句,而是使用硬编码值。像这样你绕过了视图层。
绕过回调
使用update_column
代替update_attribute
回调
第一:不要使用回调。第二:不要使用它们。如果你使用它们,请确保你知道你在做什么:-)
如果update_column
解决了问题,那么可能是回调的原因。
在您的代码中搜索属性名称和回调名称,看看是否有。