你如何使用表单对象来编辑另一个类实例?
Posted
技术标签:
【中文标题】你如何使用表单对象来编辑另一个类实例?【英文标题】:How do you use form objects to edit another class instance? 【发布时间】:2021-10-24 02:20:51 【问题描述】:我需要能够通过名为UserForm
的表单对象创建和编辑类User
的实例。表单没有持久化,User
使用表单save
方法保存。
我得到了新/创建操作,但因为 UserForm
没有持久化(没有 id
)我不知道如何加载现有的 User
以通过 @987654328 进行编辑@。
如何使用现有的 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
新建/编辑 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
数据。
【问题讨论】:
【参考方案1】:我会使用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 =
并使用真正的关键字参数。不要使用范围结果运算符::
声明嵌套类/模块。它会导致自动加载错误和令人惊讶的不断查找。
使用关联而不是直接分配 ID。这将触发关联上的回调,并避免将模型的实现细节泄漏到控制器层。
class Account < ApplicationRecord
has_many :users
# ...
end
您的 save
方法应该很可能与您正在包装的对象具有相同的返回签名并返回一个布尔值。
【讨论】:
【参考方案2】:我认为你的问题是
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]
访问它们,要么还为它们指定属性读取器。
【讨论】:
以上是关于你如何使用表单对象来编辑另一个类实例?的主要内容,如果未能解决你的问题,请参考以下文章