我将两个表中的数据加载到单个表单中。在表单上,我为模型B(孩子)创建新记录,而不为模型A(父母)更改任何内容。如果对于模型B,输入错误的内容(例如,没有' .com'的电子邮件地址)则会显示模型B的验证错误。纠正问题(输入正确的电子邮件)并按“提交”按钮后,将调用模型A的更新操作。但在模型A中没有变化!为什么在这种情况下调用模型A的更新操作?没有数据可以更新到数据库。
以下是更详细的解释:
我使用一个注册表单输入两个模型的数据:租户和用户。任何租户都会有很多用户。当新用户想要注册时,他还会输入有关其租户(公司)的数据。如果输入的租户不存在,则会与新用户一起创建新租户。这部分运作良好。问题是当新用户输入数据库中已存在的租户名称时,仅用户输入错误信息(例如,错误的电子邮件格式)。如果输入错误,则验证错误(例如,电子邮件不能为空)出现 AND 同一租户的所有先前保存的用户也会显示为render 'new'
的结果在TenantController中。
要仅显示触发验证错误的新记录,我在注册表单中添加了<% if u.object.new_record? %>
(也许这不是正确的方法吗?):
<%= f.fields_for(:users) do |u| %>
<% if u.object.new_record? %>
...
<% end %>
<% end %>
在这种情况下,正确显示注册表单,但,当我更正验证错误的原因(例如,输入正确的电子邮件)并按保存按钮,然后执行更新操作而不是创造行动。这就是问题,因为我不希望任何用户能够在注册阶段更新租户数据。在注册时,用户应该只能创建他的个人数据,而不能更新他的租户数据。请建议如何做到这一点。
租户模式:
class Tenant < ActiveRecord::Base
has_many :users, dependent: :destroy, inverse_of: :tenant
accepts_nested_attributes_for :users
before_validation do
self.status = 0
self.name = name_orig.upcase
email.downcase!
end
validates :name_orig, presence: true, length: { maximum: 255 }
validates :name, uniqueness: { case_sensitive: false }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :status, presence: true
end
用户模型:
class User < ActiveRecord::Base
belongs_to :tenant, inverse_of: :users
before_validation do
self.status = 0
self.email = email.downcase
end
validates :tenant, presence: true
VALID_USERNAME_REGEX = /\A\w+\s?\w*\z/i
validates :name, presence: true, length: { maximum: 50 },
format: { with: VALID_USERNAME_REGEX },
uniqueness: { case_sensitive: false }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
validates :status, presence: true
end
租户控制人:
class TenantsController < ApplicationController
def show
@tenant = Tenant.find(params[:id])
end
def new
@tenant = Tenant.new
@tenant.users.build
end
def create
@tenant = Tenant.find_by(name: tenant_params[:name_orig].upcase)
if @tenant == nil
@tenant = Tenant.new(name_orig: tenant_params[:name_orig],
email: tenant_params[:email])
@user = @tenant.users.build(tenant_params[:users_attributes]["0"])
@tenant.save
if @tenant.save
flash[:success] = "Welcome!"
redirect_to @user # redirects to user profile
else
render 'new'
end
else
@user = @tenant.users.build(tenant_params[:users_attributes]["0"])
if @user.save
flash[:success] = "Welcome!"
redirect_to @user # redirects to user profile
else
render 'new'
end
end
end
private
def tenant_params
params.require(:tenant).permit(:name_orig, :email,
users_attributes: [:name, :email, :password, :password_confirmation])
end
end
注册表单:
<%= form_for(@tenant) do |f| %>
<%= render 'shared/tenant_error_messages' %>
<%= f.label :name_orig, "Company name" %>
<%= f.text_field :name_orig, class: 'form-control' %>
<%= f.label :email, "Company e-mail" %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.fields_for(:users) do |u| %>
<% if u.object.new_record? %>
<%= u.label :name, "User name" %>
<%= u.text_field :name, class: 'form-control' %>
<%= u.label :email, "User e-mail" %>
<%= u.email_field :email, class: 'form-control' %>
<%= u.label :password, "Password" %>
<%= u.password_field :password, class: 'form-control' %>
<%= u.label :password_confirmation, "Password confirmation" %>
<%= u.password_field :password_confirmation, class: 'form-control' %>
<% end %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
答案 0 :(得分:0)
让我先回答一下为什么你的代码没有按预期工作。
创建操作下的行
@tenant = Tenant.find_by(name:tenant_params [:name_orig] .upcase)
分配已存在于数据库中的租户对象。因此,当您将@tenant对象提供给form_for(@tenant)时,操作将自动转换为更新而不是创建。这就是为什么你从来没有遇到过新租户创造的任何问题。
解决方案是明确设置form_for url:
= form_for(@tenant,as :: tenant,url:tenant_path(@tenant),method :: post)
这总是会向创建操作发送一个帖子请求。
我对改进设计的两分钱
此外,如果在任何时间点只有一个用户和一个租户将通过表单提交,那么您可以在表单中使用User作为父级而不是Tenant(因为User对象是主要对象,如果我是对的,则以该表格提交。你可以想到像
这样的逆转 = form_for(@user) do |f|
= # Fields for user goes here
= f.fields_for(@tenant) do |t|
= # Fields for tenant goes here
这种方法的优点是,即使租户已经存在,您也可以在表单中使用该id作为tenant_id。当然,您需要将控制器更改为UserController和其他控制器代码。希望这会有所帮助。
PS:使用Haml代替Erb道歉。