根据 Rails 上 Ruby 中的参数接收到的数据创建对象
Create objects based on received data from params in Ruby on Rails
描述
我正在 Rails 上 Ruby 中的浏览器中基于所选(通过复选框)users
创建 messages
。
快照:
重现步骤
- 我的架构
ActiveRecord::Schema.define(version: 2021_11_13_142255) do
create_table "messages", force: :cascade do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "role"
t.integer "phone"
t.boolean "admin"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
- messages_controller.rb
class MessagesController < ApplicationController
def new
@users = User.all
@message = Message.new(message_params)
end
def create
params[:user_objs].each do |u|
# "params.inspect" returns
# {"authenticity_token"=>"[FILTERED]",
# "user_objs"=>
# ["{\"id\":1,\"name\":\"Alex\",\"role\":\"Engineer\",\"phone\":998943333303,\"admin\":true,\"created_at\":\"2021-11-13T14:37:54.962Z\",\"updated_at\":\"2021-11-13T14:37:54.962Z\"}",
# "{\"id\":2,\"name\":\"Lucy\",\"role\":\"Accountant\",\"phone\":998943333303,\"admin\":false,\"created_at\":\"2021-11-13T14:39:52.742Z\",\"updated_at\":\"2021-11-13T14:39:52.742Z\"}"],
# "message"=>{"content"=>"Message from the browser"},
# "commit"=>"Send"}
person = JSON.parse(u)
@message = person.messages.new(message_params)
if @message.save
redirect_to root_path
else
@users = User.all
render :new
end
end
end
private
def message_params
params.permit(
:content,
:user_id
)
end
end
- 消息 => new.html.erb
<div>
<h1>Create and send a new message!</h1>
<%= form_for(@message) do |form| %>
<% if @message.errors.any? %>
<div class="alert alert-danger">
<h5 class="fw-bold">Invalid input!</h5>
<%= @message.errors.full_messages.each do |error| %>
<div><%= error %></div>
<% end %>
</div>
<% end %>
<% @users.each do |u| %>
<div>
<p><%= check_box_tag "user_objs[]", u.to_json %> <%= u.name %></p>
</div>
<% end %>
<p class="mb-3">
<%= form.label :content, class: "form-label" %>
<%= form.text_field :content, class: "form-control", autofocus: true, placeholder: "John_D" %>
</p>
<p class="mb-3">
<%= form.submit "Send", class: "btn btn-primary" %>
</p>
<% end %>
</div>
<%= params.inspect %>
- 型号
# user.rb
class User < ApplicationRecord
has_many :messages
end
# message.rb
class Message < ApplicationRecord
belongs_to :user
end
预期行为
我期待为所有选定的用户创建消息
实际行为
NoMethodError in MessagesController#create
undefined method `messages' for #<Hash:0x000000011fe2b420>
我尝试了不同的方法,但无法在我的 params user_objs[]
中将 Ruby 对象转换为 JSON
,因此我可以在我的控制器中解析它以根据所选对象创建消息user_objs[]
参数中的用户。
环境信息
ruby -v
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [arm64-darwin20]
rails -v
Rails 6.1.4.1
感谢您的帮助
问题是您在 person = JSON.parse(u)
中分配 json object/hash。这不是活动记录,因此在执行 person.messages
时会抛出错误。我相信您在创建操作中需要的是:
user = JSON.parse(u)
# make sure user.inspect gives you the user object you want
person = User.find(user["id"])
# person.inspect should give you the active record for the user
如果您想创建一个系统,您可以在其中向多个用户发送一条消息,您可以设置一个加入 table:
class User < ApplicationRecord
has_many :user_messages
has_many :recieved_messages, though: :user_messages,
source: :message,
inverse_of: :recipients
end
# rails g model user_message user:belongs_to message:belongs_to read:boolean
class UserMessage < ApplicationRecord
belongs_to :user
belongs_to :message
# make sure to add a compound unique index to the migration as well
validates_uniqueness_of :user_id, scope: :message_id
delegate :content, to: :message
end
class Message < ApplicationRecord
has_many :user_messages
has_many :recipients, though: :user_messages,
source: :user,
inverse_of: :recieved_messages
end
has_many :recipients
将创建一个 recipient_ids=
setter 和一个 recipient_ids
getter,您可以在表单中使用它们:
<div>
<h1>Create and send a new message!</h1>
<%= form_with(model: @message) do |form| %>
<% if @message.errors.any? %>
<div class="alert alert-danger">
<h5 class="fw-bold">Invalid input!</h5>
<%= @message.errors.full_messages.each do |error| %>
<div><%= error %></div>
<% end %>
</div>
<% end %>
<p class="mb-3">
<%= form.collection_checkboxes(:recipient_ids, @users, :id, :name) %>
</p>
<p class="mb-3">
<%= form.label :content, class: "form-label" %>
<%= form.text_field :content, class: "form-control", autofocus: true, placeholder: "John_D" %>
</p>
<p class="mb-3">
<%= form.submit "Send", class: "btn btn-primary" %>
</p>
<% end %>
</div>
绝对没有必要将整个记录作为 JSON 传递 - 您只需传递一个 ID 数组,rails 将完成创建连接 table 行的所有工作给你:
class MessagesController < ApplicationController
def new
@users = User.all
@message = Message.new
end
def create
@message = Message.new(message_params)
if @message.save
redirect_to root_path
else
@users = User.all
render :new
end
end
private
def message_params
params.require(:message)
.permit(
:content,
recipient_ids: []
)
end
end
这避免了从单个请求创建多个记录的复杂性以及整个难题,即您将表单绑定到 Message 的单个实例但创建一堆记录,这必然会导致混淆。
如果您想一次创建多条记录,可以做到,但复杂性要高得多,您必须处理诸如创建一条消息失败时如何处理错误之类的问题,这可能超出您当前的技能水平.
描述
我正在 Rails 上 Ruby 中的浏览器中基于所选(通过复选框)users
创建 messages
。
快照:
重现步骤
- 我的架构
ActiveRecord::Schema.define(version: 2021_11_13_142255) do
create_table "messages", force: :cascade do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "role"
t.integer "phone"
t.boolean "admin"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
- messages_controller.rb
class MessagesController < ApplicationController
def new
@users = User.all
@message = Message.new(message_params)
end
def create
params[:user_objs].each do |u|
# "params.inspect" returns
# {"authenticity_token"=>"[FILTERED]",
# "user_objs"=>
# ["{\"id\":1,\"name\":\"Alex\",\"role\":\"Engineer\",\"phone\":998943333303,\"admin\":true,\"created_at\":\"2021-11-13T14:37:54.962Z\",\"updated_at\":\"2021-11-13T14:37:54.962Z\"}",
# "{\"id\":2,\"name\":\"Lucy\",\"role\":\"Accountant\",\"phone\":998943333303,\"admin\":false,\"created_at\":\"2021-11-13T14:39:52.742Z\",\"updated_at\":\"2021-11-13T14:39:52.742Z\"}"],
# "message"=>{"content"=>"Message from the browser"},
# "commit"=>"Send"}
person = JSON.parse(u)
@message = person.messages.new(message_params)
if @message.save
redirect_to root_path
else
@users = User.all
render :new
end
end
end
private
def message_params
params.permit(
:content,
:user_id
)
end
end
- 消息 => new.html.erb
<div>
<h1>Create and send a new message!</h1>
<%= form_for(@message) do |form| %>
<% if @message.errors.any? %>
<div class="alert alert-danger">
<h5 class="fw-bold">Invalid input!</h5>
<%= @message.errors.full_messages.each do |error| %>
<div><%= error %></div>
<% end %>
</div>
<% end %>
<% @users.each do |u| %>
<div>
<p><%= check_box_tag "user_objs[]", u.to_json %> <%= u.name %></p>
</div>
<% end %>
<p class="mb-3">
<%= form.label :content, class: "form-label" %>
<%= form.text_field :content, class: "form-control", autofocus: true, placeholder: "John_D" %>
</p>
<p class="mb-3">
<%= form.submit "Send", class: "btn btn-primary" %>
</p>
<% end %>
</div>
<%= params.inspect %>
- 型号
# user.rb
class User < ApplicationRecord
has_many :messages
end
# message.rb
class Message < ApplicationRecord
belongs_to :user
end
预期行为
我期待为所有选定的用户创建消息
实际行为
NoMethodError in MessagesController#create
undefined method `messages' for #<Hash:0x000000011fe2b420>
我尝试了不同的方法,但无法在我的 params user_objs[]
中将 Ruby 对象转换为 JSON
,因此我可以在我的控制器中解析它以根据所选对象创建消息user_objs[]
参数中的用户。
环境信息
ruby -v
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [arm64-darwin20]
rails -v
Rails 6.1.4.1
感谢您的帮助
问题是您在 person = JSON.parse(u)
中分配 json object/hash。这不是活动记录,因此在执行 person.messages
时会抛出错误。我相信您在创建操作中需要的是:
user = JSON.parse(u)
# make sure user.inspect gives you the user object you want
person = User.find(user["id"])
# person.inspect should give you the active record for the user
如果您想创建一个系统,您可以在其中向多个用户发送一条消息,您可以设置一个加入 table:
class User < ApplicationRecord
has_many :user_messages
has_many :recieved_messages, though: :user_messages,
source: :message,
inverse_of: :recipients
end
# rails g model user_message user:belongs_to message:belongs_to read:boolean
class UserMessage < ApplicationRecord
belongs_to :user
belongs_to :message
# make sure to add a compound unique index to the migration as well
validates_uniqueness_of :user_id, scope: :message_id
delegate :content, to: :message
end
class Message < ApplicationRecord
has_many :user_messages
has_many :recipients, though: :user_messages,
source: :user,
inverse_of: :recieved_messages
end
has_many :recipients
将创建一个 recipient_ids=
setter 和一个 recipient_ids
getter,您可以在表单中使用它们:
<div>
<h1>Create and send a new message!</h1>
<%= form_with(model: @message) do |form| %>
<% if @message.errors.any? %>
<div class="alert alert-danger">
<h5 class="fw-bold">Invalid input!</h5>
<%= @message.errors.full_messages.each do |error| %>
<div><%= error %></div>
<% end %>
</div>
<% end %>
<p class="mb-3">
<%= form.collection_checkboxes(:recipient_ids, @users, :id, :name) %>
</p>
<p class="mb-3">
<%= form.label :content, class: "form-label" %>
<%= form.text_field :content, class: "form-control", autofocus: true, placeholder: "John_D" %>
</p>
<p class="mb-3">
<%= form.submit "Send", class: "btn btn-primary" %>
</p>
<% end %>
</div>
绝对没有必要将整个记录作为 JSON 传递 - 您只需传递一个 ID 数组,rails 将完成创建连接 table 行的所有工作给你:
class MessagesController < ApplicationController
def new
@users = User.all
@message = Message.new
end
def create
@message = Message.new(message_params)
if @message.save
redirect_to root_path
else
@users = User.all
render :new
end
end
private
def message_params
params.require(:message)
.permit(
:content,
recipient_ids: []
)
end
end
这避免了从单个请求创建多个记录的复杂性以及整个难题,即您将表单绑定到 Message 的单个实例但创建一堆记录,这必然会导致混淆。
如果您想一次创建多条记录,可以做到,但复杂性要高得多,您必须处理诸如创建一条消息失败时如何处理错误之类的问题,这可能超出您当前的技能水平.