关联模型和嵌套表单,validation不起作用

Update2:我已经清理了代码,这似乎已经解决了一些问题。 我在这里发布了新代码作为一个新问题。

更新:组织和用户有1:多关系。 我的问题涉及一个联合注册表单,其中需要组织和用户。 在maxcal对原始post的帮助之后,我为我的嵌套表单编写了一个新的create方法(“组织有很多用户”),如下所示。 我还添加了begin...rescue...end create方法。 现在的情况/问题:

  • 提交所有有效信息,它正常工作。
  • 提交组织的无效信息(如果用户也无效,则无关紧要),它会按照我们的意愿呈现包含错误消息的页面,但它仅显示组织详细信息的错误。 此外,对于用户详细信息,它已经清空了所有字段,它不应该。
  • 仅为用户提交无效信息,它再次呈现表单但没有任何错误消息,并且用户的所有字段都已清空。

任何人都知道代码有什么问题? 对于嵌套用户而言,问题似乎与组织(父组件)相关。 users_attributes.empty? 不起作用,因为根据日志,空提交的表单仍然包含这些属性:

 Parameters: {"utf8"=>"✓", "authenticity_token"=>"***", "organization"=>{"name"=>"", "bag"=>"", "users_attributes"=>{"0"=>{"email"=>"", "username"=>"", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "usertype"=>"2", "admin"=>"true"}}}, "commit"=>"Register"} 

  def create @organization = Organization.new(new_params.except(:users_attributes)) begin if users_attributes.empty? @organisation.errors.add(:users, 'No user provided') end @organization.transaction do @organization.save! if users_attributes.any? @organization.users.create!(users_attributes) end end rescue ActiveRecord::RecordInvalid => invalid if @organization.persisted? if @organization.users.any? @organization.users.each do |single_user| single_user.send_activation_email end end flash[:success] = "Confirmation email sent." redirect_to root_url else @organization.users.build if @organization.users.blank? render :new end end end private # converts the hash of nested attributes hashes to an array def users_attributes new_params[:users_attributes].values end end 


原始问题:我有两个关联模型和一个带validation的嵌套表单。 不幸的是,它没有用。 1) 播种时会生成错误Validation failed: Users organization can't be blank 。 我之前发布了一个关于这个的问题并且过早地得出结论它已经解决了它。 它没有。 2)提交我的嵌套注册表单 ,正确填写所有字段,生成flash错误消息The form contains 1 error. Users organization can't be blank The form contains 1 error. Users organization can't be blank

我该如何调整代码来解决这些问题?

模型文件:

 #User model belongs_to :organization, inverse_of: :users validates_presence_of :organization_id, :unless => 'usertype == 1' # Organization model has_many :users, dependent: :destroy accepts_nested_attributes_for :users, :reject_if => :all_blank, :allow_destroy => true validate :check_user private def check_user if users.empty? errors.add(:base, 'User not present') end end 

组织控制器方法

  def new @organization = Organization.new @user = @organization.users.build end def create @organization = Organization.new(new_params) if @organization.save @organization.users.each do |single_user| single_user.send_activation_email # Method in user model file. end flash[:success] = "Confirmation email sent." redirect_to root_url else @organization.users.build if @organization.users.blank? render 'new' end end def new_params params.require(:organization).permit(:name, :bag, users_attributes: [:email, :username, :usertype, :password, :password_confirmation]) end 

表格:

              

在我的种子文件中,我有:

 Organization.create!(name: "Fictious business", address: Faker::Address.street_address, city: Faker::Address.city, users_attributes: [email: "helpst@example.com", username: "helpyzghtst", usertype: 2, password: "foobar", password_confirmation: "foobar"]) 

登录提交注册表单时出错:

 Started POST "/organizations" Processing by OrganizationsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"0cR***Nnx4iReMiePg==", "organization"=>{"name"=>"test21", "bag"=>"tes21", "users_attributes"=>{"0"=>{"email"=>"test21@example.com", "username"=>"test21", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "usertype"=>"2"}}}, "commit"=>"Register"} (0.2ms) BEGIN User Exists (1.1ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('test21@example.com') LIMIT 1 (0.7ms) SELECT "users"."email" FROM "users" ORDER BY "users"."username" ASC User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE LOWER(users"."username") = LOWER('test21') LIMIT 1 Organization Exists (0.6ms) SELECT 1 AS one FROM "organizations" WHERE LOWER("organizations"."name") = LOWER('test21') LIMIT 1 Organization Exists (0.4ms) SELECT 1 AS one FROM "organizations" WHERE LOWER("organizations"."bag") = LOWER('tes21') LIMIT 1 (0.2ms) ROLLBACK 

由于Catch-22,您的validation无效

要申请这份工作,你必须疯了; 但如果你疯了,你是不可接受的。

ActiveRecord模型在保存时从数据库中获取其ID。 但是,嵌套用户的validation在组织插入数据库之前运行。

你会猜测只是检查validates_presence_of会传递:

 validates_presence_of :organization, unless: -> { usertype == 1 } 

不幸的是没有。 为了使validates_presence_of :organization传递组织必须持久化到数据库。 再次捕获22。

为了validation通过,我们需要将组织和用户分成两个步骤:

 org = Organization.create(name: 'M & M Enterprises') user = org.users.build(username: 'milo_minderbinder', ...) user.valid? 

不幸的是,你不能使用accepts_nested_attributes_for :users – 至少不是直接蝙蝠。

通过使用事务,我们可以将组织插入到数据库中,并在用户无效时回滚。

 def create @organization = Organization.new(new_params.except(:users_attributes)) @organization.transaction do @organization.save! if new_params[:users_attributes].any? @organization.users.create!(new_params[:users_attributes]) end end if @organization.persisted? # ... if @organization.users.any? # send emails ... end else @organization.users.build if @organization.users.blank? render :new end end 

后续问题

我们使用@organization.persisted? 因为我们可能想要重定向到新创建的组织,无论是否创建了用户记录。

因为电子邮件是发送给用户的? 如果没有创建用户,组织将被回滚,这应该没关系。

如果没有用户创建,则不回滚事务。 仅当用户由于参数无效而无法保存时。 这是基于您的要求:

但是组织也可以(暂时)没有用户。

如果您需要@organisation在没有用户的情况下无效,您可以执行以下操作:

  @organisation.errors.add(:users, 'No users provided') unless new_params[:users_attributes].any? @organization.transaction do @organization.save! if new_params[:users_attributes].any? @organization.users.create!(new_params[:users_attributes]) end end 

你会用@organization.users.any? 检查是否有任何用户。 @organization.users.persisted? 自从.persisted?不会工作.persisted? 是模型实例上的方法 – 而不是集合。

另外,我认为不可能用这种方法(不应该)覆盖/更新现有的组织/用户,而不是总是创建新的记录?

是的,因为这将始终发出两个SQL插入语句,它不会改变现有记录。

但是,您可以创建保证数据库列唯一性的validation(IE不需要具有相同user.email或organiation.name的多个记录)。

好的一面是,在更新现有组织时,这些警告都不适用:

 def update @organisation.update(... params for org and and users ...) end 

因为在validation用户时你没有得到整鸡或鸡蛋的困境。