Ruby 关于 Rails PG 数据库问题,使用具有 table 实体的嵌套表单,该实体具有 table-less 模型关系 'has many'
Ruby On Rails PG Database problem using nested form with a table entity that has a table-less model relationship 'has many'
我正在处理的任务的主要目的是为管理员创建一个包含多个时间范围的表单,这样我就可以将这些时间范围作为 jsonb 属性保存在我的数据库中。我可以根据需要添加任意数量的时间范围。前端动态已经完成了,但是我在后端和我想得到的参数上挣扎了很多。
我想从我的简单表单中获得的参数应该如下所示:
admin: {first_name, last_name, age, time_ranges: [{start_time, end_time}, {start_time, end_time},{start_time, end_time}...]}
我已经阅读了很多关于嵌套表单的内容,但我无法实现我想要的。我已经尝试为 DateRanges 创建一个 tables-less 模型,所以在我的 SuperAdmin 模型中(在我的数据库中有一个 table)我可以使用 has_many: time_ranges和 accepts_nested_attributes_for:date_ranges.
class Admin < ApplicationRecord
has_many :time_ranges
accepts_nested_attributes_for :time_ranges
end
class TimeRange < ApplicationRecord
include ActiveModel::Model
include ActiveModel::Attributes
attribute :end_time
attribute :start_time
attr_accessor :start_time, :end_time
belongs_to :admin
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
在我的 admin_controller 我的参数中有这个:
def admin_params
params.require(:admin).permit(:first_name, :last_name, :age, time_ranges_attributes: [:start_date, :end_date])
end
我在视图中使用了简单的表单,所以我的前端看起来像这样:
<%= simple_form_for @admin, url: admin_path(@admin), method: :put do |f|%>
<tr>
<td>Name</td>
<td><%= f.input :first_name %></td>
<td>Last Name</td>
<td><%= f.input :last_name %></td>
</tr>
<%= f.simple_fields_for :time_ranges do |p|%>
<tr>
<td>
<p class='ml-1'>Start</p>
<%= p.input :start_time, as: :time, ignore_date: true %>
</td>
<td>
<p class='ml-1'>End</p>
<%= p.input :end_time, as: :time, ignore_date: true %>
</td>
<td>
</td>
</tr>
<% end %>
--------------...I could add as many simple_fields_for as I want (front-end logic using StimulusJS) ---------------------------------------------
<%= f.button :submit, class: 'button is-info' %>
<% end %>
我发现的这种方式可以给我想要的结果,但是当我尝试访问我的视图时,我立即收到以下错误:
PG::UndefinedTable: ERROR: relation "time_ranges" does not exist
LINE 1: SELECT "time_ranges".* FROM "time_ranges" WHERE "time_ranges...
我知道这是因为 time_range 实体在我的数据库中不存在,但我想得到你的帮助来解决这个问题,或者如果你给我另一个解决方案来获得我想要的参数。我正在使用 rails 5.2.
has_many
和嵌套属性用于关联。这不是一个关联,它只是一个包含字符串的列,Rails 将对其进行序列化和反序列化。这就是 Rails 对 JSONB 的全部了解。
您传递的不是嵌套属性,而是数组参数。
def admin_params
params.require(:admin).permit(:first_name, :last_name, :age, time_ranges: [])
end
由管理员验证 time_ranges 是否包含正确的对象。
您的表单将接受名称为 admin[time_ranges][][start_date]
和 admin[time_ranges][][end_date]
的输入。参见 Parameter Naming Conventions。
由于 Rails 不了解 JSONB 关联,因此需要做更多的工作。这可以更简单地完成,作为与真实 table 和单个 tztsrange or daterange column 的常规关联(不清楚您是在存储时间还是日期)。
create_table :admin_availability do
t.belongs_to :admin
t.tztsrange :availability, null: false
end
class AdminAvailability < ApplicationRecord
belongs_to :admin
end
class Admin < ApplicationRecord
has_many :admin_availabilities
accepts_nested_attributes_for :admin_availability
end
现在嵌套属性机制可以工作了。您可能仍然需要将 start_time/end_time 参数转换为单个 Range 对象。向您的控制器添加便捷访问器可能会更简单。
class AdminAvailability < ApplicationRecord
belongs_to :admin
def start_time
availability.begin
end
def start_time=(start)
self.availability = Range.new(start, end_time)
end
def end_time
availability.end
end
def end_time=(end)
self.availability = Range.new(start_time, end)
end
end
我正在处理的任务的主要目的是为管理员创建一个包含多个时间范围的表单,这样我就可以将这些时间范围作为 jsonb 属性保存在我的数据库中。我可以根据需要添加任意数量的时间范围。前端动态已经完成了,但是我在后端和我想得到的参数上挣扎了很多。
我想从我的简单表单中获得的参数应该如下所示:
admin: {first_name, last_name, age, time_ranges: [{start_time, end_time}, {start_time, end_time},{start_time, end_time}...]}
我已经阅读了很多关于嵌套表单的内容,但我无法实现我想要的。我已经尝试为 DateRanges 创建一个 tables-less 模型,所以在我的 SuperAdmin 模型中(在我的数据库中有一个 table)我可以使用 has_many: time_ranges和 accepts_nested_attributes_for:date_ranges.
class Admin < ApplicationRecord
has_many :time_ranges
accepts_nested_attributes_for :time_ranges
end
class TimeRange < ApplicationRecord
include ActiveModel::Model
include ActiveModel::Attributes
attribute :end_time
attribute :start_time
attr_accessor :start_time, :end_time
belongs_to :admin
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
在我的 admin_controller 我的参数中有这个:
def admin_params
params.require(:admin).permit(:first_name, :last_name, :age, time_ranges_attributes: [:start_date, :end_date])
end
我在视图中使用了简单的表单,所以我的前端看起来像这样:
<%= simple_form_for @admin, url: admin_path(@admin), method: :put do |f|%>
<tr>
<td>Name</td>
<td><%= f.input :first_name %></td>
<td>Last Name</td>
<td><%= f.input :last_name %></td>
</tr>
<%= f.simple_fields_for :time_ranges do |p|%>
<tr>
<td>
<p class='ml-1'>Start</p>
<%= p.input :start_time, as: :time, ignore_date: true %>
</td>
<td>
<p class='ml-1'>End</p>
<%= p.input :end_time, as: :time, ignore_date: true %>
</td>
<td>
</td>
</tr>
<% end %>
--------------...I could add as many simple_fields_for as I want (front-end logic using StimulusJS) ---------------------------------------------
<%= f.button :submit, class: 'button is-info' %>
<% end %>
我发现的这种方式可以给我想要的结果,但是当我尝试访问我的视图时,我立即收到以下错误:
PG::UndefinedTable: ERROR: relation "time_ranges" does not exist
LINE 1: SELECT "time_ranges".* FROM "time_ranges" WHERE "time_ranges...
我知道这是因为 time_range 实体在我的数据库中不存在,但我想得到你的帮助来解决这个问题,或者如果你给我另一个解决方案来获得我想要的参数。我正在使用 rails 5.2.
has_many
和嵌套属性用于关联。这不是一个关联,它只是一个包含字符串的列,Rails 将对其进行序列化和反序列化。这就是 Rails 对 JSONB 的全部了解。
您传递的不是嵌套属性,而是数组参数。
def admin_params
params.require(:admin).permit(:first_name, :last_name, :age, time_ranges: [])
end
由管理员验证 time_ranges 是否包含正确的对象。
您的表单将接受名称为 admin[time_ranges][][start_date]
和 admin[time_ranges][][end_date]
的输入。参见 Parameter Naming Conventions。
由于 Rails 不了解 JSONB 关联,因此需要做更多的工作。这可以更简单地完成,作为与真实 table 和单个 tztsrange or daterange column 的常规关联(不清楚您是在存储时间还是日期)。
create_table :admin_availability do
t.belongs_to :admin
t.tztsrange :availability, null: false
end
class AdminAvailability < ApplicationRecord
belongs_to :admin
end
class Admin < ApplicationRecord
has_many :admin_availabilities
accepts_nested_attributes_for :admin_availability
end
现在嵌套属性机制可以工作了。您可能仍然需要将 start_time/end_time 参数转换为单个 Range 对象。向您的控制器添加便捷访问器可能会更简单。
class AdminAvailability < ApplicationRecord
belongs_to :admin
def start_time
availability.begin
end
def start_time=(start)
self.availability = Range.new(start, end_time)
end
def end_time
availability.end
end
def end_time=(end)
self.availability = Range.new(start_time, end)
end
end