arrays - Rails 表单:如何存储(和检索)数组?
问题描述
我对 Rails 很陌生,想将 Rails 表单中的复选框值存储到数据库字段中的数组中。
我想在没有数据库关系/连接表的情况下执行此操作(表单值很少会改变,如果有的话),所以我在表单内的哈希中提供复选框选项。
使用这个答案作为指南,我已经完成了一些工作。
表单显示复选框选项,当我更新记录时,它确实在数据库字段中存储了一个包含选中值的数组(作为字符串)。但是,当我编辑记录时,表单中的复选框都是空白的。
我相信我需要告诉表单如何预先“检查”第一个参数中的适当框(:频率,这是数据库中的一个文本字段),但我不知道该怎么做。我试过了:
- :frequency[] 和其他变体
- 在 Building 模型中添加序列化
我已经看到在 Postgres 中使用 hstore 的参考资料,但我对此并不熟悉,也没有尝试过。
非常感谢...
使用:Rails 6.0.2.2、Postgres 12
_form.html.erb
<%= form_with(model: building, local: true) do |f| %>
...
<%= f.collection_check_boxes(:frequency, { 'Yearly': 1, 'Quarterly': 2, 'Monthly': 3, 'Weekly': 4 }, :last, :first) %>
building.rb
class Building < ApplicationRecord
serialize :frequency, Array
Server log
(检查表格中的第三个框后):
Processing by BuildingsController#update as HTML
Parameters: {"authenticity_token"=>"p2fFVpKZnw5HroZvy3qGAbguTxf+3oEOjOrhEGTu4ImS7GpVwXrcj78XPt5ENcN6GQx9K9Cn/c3ZvMwOSqVYbQ==", "building"=>{"frequency"=>["", "3"]}, "commit"=>"Update Building", "id"=>"5"}
Building Update (0.8ms) UPDATE "buildings" SET "frequency" = $1, "updated_at" = $2 WHERE "buildings"."id" = $3 [["frequency", "---\n- ''\n- '3'\n"], ["updated_at", "2020-04-05 17:56:22.142645"], ["id", 5]]
↳ app/controllers/buildings_controller.rb:40:in `update'
(1.4ms) COMMIT
↳ app/controllers/buildings_controller.rb:40:in `update'
Redirected to http://localhost:3000/buildings/5
Completed 302 Found in 14ms (ActiveRecord: 3.7ms | Allocations: 6210)
解决方案
做什么都不要用serialize
。它是来自黑暗时代的肮脏黑客,通过将数据序列化为 JSON 或 YAML 将复杂类型存储在 text/varchar 列中。它不应该与原生数组/json/hstore 类型一起使用,因为序列化是由数据库适配器处理的。如果你使用serialize
数组列,你会得到类似["[1,2,3]"]
插入数据库的东西,如果你使用 JSON,你会得到"{ \"foo\":\"bar\" }"
.
相反,如果您想使用位掩码数组,只需将该列创建为数组:
create_table :buildings do |t|
t.integer 'frequency', array: true
end
数据库适配器将自动处理它与数组的转换。
如果您想使用 HStore 或 JSON,它们都可以完成哈希在 ruby 中所做的工作:
create_table :buildings do |t|
t.json 'frequency'
# or
t.hstore 'frequency'
end
但恕我直言,我会使用该程序并创建一个连接表。这不值得加入可以解决的大惊小怪和蹩脚的查询/问题。ActiveRecord 是围绕表和关系模型构建的。不是 hstore/array/json 列。
class Frequency
has_many :building_frequencies
has_many :frequencies, through: :building_frequencies
end
class BuildingFrequency
belongs_to :building
belongs_to :frequency
end
class Building
has_many :building_frequencies
has_many :frequencies, through: :building_frequencies
end
<%= f.collection_check_boxes(:frequency_ids, Frequency.all, :id, :name) %>
def building_params
params.require(:building)
.permit(
:foo, :bar,
frequency_ids: []
)
end
它完成了工作,为您提供了参照完整性,并允许您使用内部连接来过滤频率上的行,这实际上比在数组/hstore 中插入要高效得多。
推荐阅读
- reactjs - 如何在 Gatsby 中从 graphql 输出 HTML
- python - 如何从另一个文件夹导入
- c# - wpf中的自定义控件,添加控件时添加和删除默认值
- python - python - 如何在Python中使用日期时间类型的列在图中显示日期而不是月份?
- xcode - Xcode 12.4 链接错误:找不到库
- schema - NHS 数据仓库
- reactjs - 为什么异步函数中的多个 setState 调用会导致多次渲染?
- firebase - 仅获取 FireBase 集合中的文档,其 id 与单独集合中的文档匹配
- vue.js - 使用“this”引用属性时,Vue 中的 Typescript 不安全成员访问错误。
- mongodb - MongoDB聚合查询优化:$match、$lookup和double $unwind