Rails: 如何将对象放入 setter 方法?
Rails: how to get object into setter method?
我正在尝试通过 setter 方法保存关联模型的属性。
我的代码:
class Task < ActiveRecord::Base
belongs_to :project
belongs_to :employee
attr_accessor :company_id
attr_accessor :employee_name
def employee_name
employee.try(:name)
end
def employee_name=(name)
self.employee = Employee.find_or_create_by(name: name,
company_id: company_id.to_i) if name.present?
end
end
但是,这会将属性保存为 nil
。
为什么 company_id
属性在 setter 方法内部使用时变成 nil
?
它的值在 setter 方法之外的模型中可用。
validate :is_company_id_available
def is_company_id_available
if company_id != nil
errors.add(:task, "#{company_id.inspect}")
end
end
returns错误信息中的正确company_id
。
此外,在 de setter 方法中手动定义 company_id = 1
保存就好了。
如何正确设置 setter 方法中的属性?
编辑:
是否有可能 company_id
属性需要以某种方式设置为该方法的参数?
或者我可以在 employee
记录创建后更新 company_id
。
Rails方式是通过使用accepts_nested_attributes_for
:
class Employee
has_many :tasks
accepts_nested_attributes_for :tasks
end
Employee.create(name: 'max', tasks_attributes: [{ description: 'Fetchez La Vache' }])
你当然也可以反过来做:
class Task
belongs_to :employee
accepts_nested_attributes_for :employee
end
Task.create(description: 'Fetchez La Vache', employee_attributes: { name: 'max' })
如果嵌套属性包含 id,这将更新嵌套记录。
@task = @employee.tasks.create(description: "Dot the T's")
@employee.update(tasks_attributes: [{ id: @task.id, description: "Cross the T's" }])
@task.reload.description == "Cross the T's" # true
不过要注意的是要避免深入许多层。你最终会得到可怕的过度膨胀的控制器和非常复杂的逻辑。
虽然可以设置委派,因为您已经尝试过它非常混乱,但您必须记住,必须先将父记录插入数据库,然后子记录才能获得 parent_id
.
多级示例
class Project
has_many :projects
end
class Task
belongs_to :employee
accepts_nested_attributes_for :employee, reject_if: :employee_exists?
def employee_exists?(attrs)
return true if employee.any? # its set already
e = Employee.find_by(name: attrs[:name] company_id: attrs[:company_id])
employee = e if e
e.any?
end
end
class Employee
has_many :tasks
end
<%= form_for(@project) do |p| %>
<%= fields_for(:tasks) do |t| %>
<p>Select an employee</p>
<%= f.collection_select(:employee_id, Employee.all, :id, :name, prompt: true) %>
<p>Or create a new one</p>
<%= fields_for(:employee) do |e| %>
<%= f.text_field(:name) %>
<%= f.collection_select(:company_id, Company.all, :id, :name, prompt: true) %>
<% end %>
<% end %>
<% end %>
您通常会将此与一些 javascript 结合使用,这会在用户选择现有记录时使员工字段变灰。但是,如果您可以选择,最好使用 AJAX 来创建嵌套记录。
这让您可以将用户体验创建为一系列原子事务,而不是一个巨大的做或死表单提交。
max 的回答是 Railistic 的做法。
但是attr_accessor :company_id
真的有必要吗?
尝试删除它或只使用 read_attribute(:company_id).to_i
或 self[:company_id].to_i
代替。
[编辑]
我认为你的参数应该有 {task_attributes: {company_id: company_id}}
或者只有在使用attributes=
时才会出现的问题。
你可以覆盖 attributes=
并绕过它
def attributes=(task_attributes)
binding.pry
end
[编辑 2]
还要确保 task_attributes: [:company_id]
在强参数中是允许的
我正在尝试通过 setter 方法保存关联模型的属性。
我的代码:
class Task < ActiveRecord::Base
belongs_to :project
belongs_to :employee
attr_accessor :company_id
attr_accessor :employee_name
def employee_name
employee.try(:name)
end
def employee_name=(name)
self.employee = Employee.find_or_create_by(name: name,
company_id: company_id.to_i) if name.present?
end
end
但是,这会将属性保存为 nil
。
为什么 company_id
属性在 setter 方法内部使用时变成 nil
?
它的值在 setter 方法之外的模型中可用。
validate :is_company_id_available
def is_company_id_available
if company_id != nil
errors.add(:task, "#{company_id.inspect}")
end
end
returns错误信息中的正确company_id
。
此外,在 de setter 方法中手动定义 company_id = 1
保存就好了。
如何正确设置 setter 方法中的属性?
编辑:
是否有可能 company_id
属性需要以某种方式设置为该方法的参数?
或者我可以在 employee
记录创建后更新 company_id
。
Rails方式是通过使用accepts_nested_attributes_for
:
class Employee
has_many :tasks
accepts_nested_attributes_for :tasks
end
Employee.create(name: 'max', tasks_attributes: [{ description: 'Fetchez La Vache' }])
你当然也可以反过来做:
class Task
belongs_to :employee
accepts_nested_attributes_for :employee
end
Task.create(description: 'Fetchez La Vache', employee_attributes: { name: 'max' })
如果嵌套属性包含 id,这将更新嵌套记录。
@task = @employee.tasks.create(description: "Dot the T's")
@employee.update(tasks_attributes: [{ id: @task.id, description: "Cross the T's" }])
@task.reload.description == "Cross the T's" # true
不过要注意的是要避免深入许多层。你最终会得到可怕的过度膨胀的控制器和非常复杂的逻辑。
虽然可以设置委派,因为您已经尝试过它非常混乱,但您必须记住,必须先将父记录插入数据库,然后子记录才能获得 parent_id
.
多级示例
class Project
has_many :projects
end
class Task
belongs_to :employee
accepts_nested_attributes_for :employee, reject_if: :employee_exists?
def employee_exists?(attrs)
return true if employee.any? # its set already
e = Employee.find_by(name: attrs[:name] company_id: attrs[:company_id])
employee = e if e
e.any?
end
end
class Employee
has_many :tasks
end
<%= form_for(@project) do |p| %>
<%= fields_for(:tasks) do |t| %>
<p>Select an employee</p>
<%= f.collection_select(:employee_id, Employee.all, :id, :name, prompt: true) %>
<p>Or create a new one</p>
<%= fields_for(:employee) do |e| %>
<%= f.text_field(:name) %>
<%= f.collection_select(:company_id, Company.all, :id, :name, prompt: true) %>
<% end %>
<% end %>
<% end %>
您通常会将此与一些 javascript 结合使用,这会在用户选择现有记录时使员工字段变灰。但是,如果您可以选择,最好使用 AJAX 来创建嵌套记录。
这让您可以将用户体验创建为一系列原子事务,而不是一个巨大的做或死表单提交。
max 的回答是 Railistic 的做法。
但是attr_accessor :company_id
真的有必要吗?
尝试删除它或只使用 read_attribute(:company_id).to_i
或 self[:company_id].to_i
代替。
[编辑]
我认为你的参数应该有 {task_attributes: {company_id: company_id}}
或者只有在使用attributes=
时才会出现的问题。
你可以覆盖 attributes=
并绕过它
def attributes=(task_attributes)
binding.pry
end
[编辑 2]
还要确保 task_attributes: [:company_id]
在强参数中是允许的