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_iself[:company_id].to_i 代替。

[编辑]

我认为你的参数应该有 {task_attributes: {company_id: company_id}}

或者只有在使用attributes=时才会出现的问题。 你可以覆盖 attributes= 并绕过它

def attributes=(task_attributes)
  binding.pry
end

[编辑 2]

还要确保 task_attributes: [:company_id] 在强参数中是允许的