您如何使用表单对象来编辑另一个 class 实例?
How do you use form objects to edit another class instance?
我需要能够通过名为 UserForm
的表单对象创建和编辑 class User
的实例。表单未持久化,User
使用表单 save
方法保存。
我得到了 new/create 操作,但是因为 UserForm
没有持久化(没有 id
)我不知道如何加载现有的 User
编辑通过 UserForm
.
如何使用现有 User
数据“填充”UserForm
对象?我尝试使用 url id
加载 User
但表单字段仍未填充 User
数据。
UserForm.rb
class UserForm
include ActiveModel::Model
include ActiveModel::Validations::Callbacks
attr_accessor :fname, :lname, :email
before_validation :build_user
def initialize(params = {})
super(params)
@account = Account.find(account_id)
@user = User.find(user_id)
end
def build_user
@user ||= User.new do |user|
user.fname = fname
user.lname = lname
user.email = email
end
end
def save
user.account_id = @account.id
user.save
end
end
UsersController.rb
class Admin::UsersController < AdminController
def new
@user_form = UserForm.new(account_id: current_account.id)
end
def create
@user_form = UserForm.new(user_form_params)
if @user = @user_form.save
flash[:success] = "User created"
redirect_to admin_user_path(@user)
else
render "new"
end
end
def edit
@user = current_account.users.find(params[:id])
@user_form = UserForm.new(user: @user)
end
def update
if @user.update(user_form_params)
flash[:success] = "User updated"
redirect_to admin_user_path(@user)
else
render "edit"
end
end
end
new/edit form.html.erb
<%= simple_form_for @user_form, url: admin_users_path do |f| %>
<%= f.input :fname %>
<%= f.input :lname %>
<%= f.input :email %>
end
问题:
我可以通过表单对象创建一个新的 User
,但我无法通过 UserForm
加载和编辑相同的 User
,因为表单没有填充现有 User
数据。
我认为你的问题是
attr_accessor :fname, :lname, :email
没有意义,因为你有,例如表单上没有 fname
个属性。
试试
delegate_missing_to :user
https://www.rubydoc.info/github/rails/rails/Module%3Adelegate_missing_to
此外,您的初始化程序中可能没有 user_id
和 account_id
属性。因此,您要么必须通过 params[:user_id]
访问它们,要么还为它们指定属性读取器。
我会使用 delegation pattern 以便您的表单对象包装底层用户而不是复制属性:
class UserForm
include ActiveModel::Model
attr_reader :user
delegate :fname, :lname, :email, :account, to: :user
# Just an example of how the validation uses delegation
validates :fname, presence: true
def initialize(object, **attributes)
case object
when User
@user = object
when Account
@user = object.users.new(**attributes)
end
end
def save
# this triggers the validation callbacks
return false unless valid?
@user.save
end
def update(**attributes)
@user.assign_attributes(**attributes)
save
end
def to_model
@user
end
end
与其滥用验证回调来执行赋值,不如将属性传递给底层模型。
module Admin
class UsersController < AdminController
before_action :set_user, except: [:new, :index, :create]
def new
@user_form = UserForm.new(current_account)
end
def create
@user_form = UserForm.new(current_account, **user_form_params)
if @user_form.save
flash[:success] = "User created"
redirect_to [:admin, @user_form]
else
render "new"
end
end
def edit
@user_form = UserForm.new(@user)
end
def update
@user_form = UserForm.new(@user)
if @user_form.update(**user_form_params)
flash[:success] = "User updated"
redirect_to [:admin, @user_form]
else
render "edit"
end
end
private
def set_user
@user = current_account.users.find(params[:id])
end
end
end
一些注意事项:
避免params = {}
并使用真正的关键字参数。不要使用范围结果运算符 ::
声明嵌套 classes/modules。它会导致自动加载错误和令人惊讶的持续查找。
使用关联而不是直接分配 ID。这将触发关联的回调并避免将模型的实现细节泄露到控制器层。
class Account < ApplicationRecord
has_many :users
# ...
end
您的 save
方法很可能具有与您正在包装的对象相同的 return 签名和 return 布尔值。
我需要能够通过名为 UserForm
的表单对象创建和编辑 class User
的实例。表单未持久化,User
使用表单 save
方法保存。
我得到了 new/create 操作,但是因为 UserForm
没有持久化(没有 id
)我不知道如何加载现有的 User
编辑通过 UserForm
.
如何使用现有 User
数据“填充”UserForm
对象?我尝试使用 url id
加载 User
但表单字段仍未填充 User
数据。
UserForm.rb
class UserForm
include ActiveModel::Model
include ActiveModel::Validations::Callbacks
attr_accessor :fname, :lname, :email
before_validation :build_user
def initialize(params = {})
super(params)
@account = Account.find(account_id)
@user = User.find(user_id)
end
def build_user
@user ||= User.new do |user|
user.fname = fname
user.lname = lname
user.email = email
end
end
def save
user.account_id = @account.id
user.save
end
end
UsersController.rb
class Admin::UsersController < AdminController
def new
@user_form = UserForm.new(account_id: current_account.id)
end
def create
@user_form = UserForm.new(user_form_params)
if @user = @user_form.save
flash[:success] = "User created"
redirect_to admin_user_path(@user)
else
render "new"
end
end
def edit
@user = current_account.users.find(params[:id])
@user_form = UserForm.new(user: @user)
end
def update
if @user.update(user_form_params)
flash[:success] = "User updated"
redirect_to admin_user_path(@user)
else
render "edit"
end
end
end
new/edit form.html.erb
<%= simple_form_for @user_form, url: admin_users_path do |f| %>
<%= f.input :fname %>
<%= f.input :lname %>
<%= f.input :email %>
end
问题:
我可以通过表单对象创建一个新的 User
,但我无法通过 UserForm
加载和编辑相同的 User
,因为表单没有填充现有 User
数据。
我认为你的问题是
attr_accessor :fname, :lname, :email
没有意义,因为你有,例如表单上没有 fname
个属性。
试试
delegate_missing_to :user
https://www.rubydoc.info/github/rails/rails/Module%3Adelegate_missing_to
此外,您的初始化程序中可能没有 user_id
和 account_id
属性。因此,您要么必须通过 params[:user_id]
访问它们,要么还为它们指定属性读取器。
我会使用 delegation pattern 以便您的表单对象包装底层用户而不是复制属性:
class UserForm
include ActiveModel::Model
attr_reader :user
delegate :fname, :lname, :email, :account, to: :user
# Just an example of how the validation uses delegation
validates :fname, presence: true
def initialize(object, **attributes)
case object
when User
@user = object
when Account
@user = object.users.new(**attributes)
end
end
def save
# this triggers the validation callbacks
return false unless valid?
@user.save
end
def update(**attributes)
@user.assign_attributes(**attributes)
save
end
def to_model
@user
end
end
与其滥用验证回调来执行赋值,不如将属性传递给底层模型。
module Admin
class UsersController < AdminController
before_action :set_user, except: [:new, :index, :create]
def new
@user_form = UserForm.new(current_account)
end
def create
@user_form = UserForm.new(current_account, **user_form_params)
if @user_form.save
flash[:success] = "User created"
redirect_to [:admin, @user_form]
else
render "new"
end
end
def edit
@user_form = UserForm.new(@user)
end
def update
@user_form = UserForm.new(@user)
if @user_form.update(**user_form_params)
flash[:success] = "User updated"
redirect_to [:admin, @user_form]
else
render "edit"
end
end
private
def set_user
@user = current_account.users.find(params[:id])
end
end
end
一些注意事项:
避免params = {}
并使用真正的关键字参数。不要使用范围结果运算符 ::
声明嵌套 classes/modules。它会导致自动加载错误和令人惊讶的持续查找。
使用关联而不是直接分配 ID。这将触发关联的回调并避免将模型的实现细节泄露到控制器层。
class Account < ApplicationRecord
has_many :users
# ...
end
您的 save
方法很可能具有与您正在包装的对象相同的 return 签名和 return 布尔值。