Rails 表单中的虚拟属性未从表单或控制台更新数据库

Rails virtual attribute in form not updating db from form or console

所以在我的 Rails 网络应用程序中,我有一段代码提示未成年用户在创建帐户时输入 parent/guardian 电子邮件。在我的数据库中,我想将其存储为相应的 ID,但用户自然会想要输入电子邮件。

我表单的这一部分如下所示:

<%= f.label :guardian_email, "Guardian Email" %>
<%= f.text_field :guardian_email, class: 'form-control' %>

我的模型中相应的代码如下所示:

def guardian_email
    User.find_by_id(guardian_id).email if guardian_id
end

def guardian_email=(g_email)
    guardian = User.find_by_email(g_email)
    print guardian.id
    self.guardian_id = guardian.id
    print self.guardian_id
end

(我在调试时添加的打印行)但是,提交表单不会更新所需的属性。然后我从控制台尝试并输入:

User.find_by_email("example-5@site.org").guardian_email=("example-7@site.org")

我得到输出:

User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "example-5@site.org"], ["LIMIT", 1]]
User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "example-7@site.org"], ["LIMIT", 1]]
88 => "example-7@mtb.org" 

但是当我之后加载用户时,属性 guardian_id 仍然是零。奇怪的是,两个打印语句 return 都是正确的值(在本例中为 8),但赋值从未发生,尽管打印出的第二个 8 是值 "self.guardian_id"。

我已经按照 railscasts #16 - 虚拟属性得到了我现在拥有的代码,谢谢。

编辑:

上述代码无法运行的原因是因为我允许控制器中的实际属性而不是虚拟属性。尽管正如 max 指出的那样,此代码中还存在其他几种不良做法。

我仍然不明白的是,在控制台中调用 guardian_email=() 方法仍然不会更新属性,但它在网站上运行良好。

对于一项简单的任务来说,这是一种非常复杂的方法。

class User  < ApplicationRecord
  belongs_to :guardian,
    class_name: 'User',
    optional: true

  def guardian_email=(email)
    self.guardian ||= User.find_by_email(email)
    @guardian_email = email
  end

  def guardian_email
    @guardian_email || guardian.try(:email)
  end
end

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :email
      # ...
      t.belongs_to :guardian, foreign_key: { to_table: :users }
      t.timestamps
    end
    add_index :users, :email, unique: true
  end
end

您还需要一个自定义验证,如果 guardian_email 不为空,它会启动,检查电子邮件是否确实属于用户。

class User  < ApplicationRecord
  # ...
  validate :guardian_exists, unless: ->{ self.guardian_email.blank? }

  # ...

  private

  def guardian_exists
    errors.add(:guardian_email) unless guardian || User.find_by_email(guardian_email)
  end
end

未进行任何更改的原因是因为在用户控制器中我将 guardian_id 而不是 guardian_email 作为允许的参数。尝试使用虚拟属性更新模型时,在控制器中应该允许表单中包含的虚拟属性。