关联的模型和带有验证的嵌套表单不起作用 [英] Associated models and a nested form with validation not working

查看:84
本文介绍了关联的模型和带有验证的嵌套表单不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Update2 :我已经清理了代码,这似乎已经解决了一些问题.我在此处将新代码发布为新问题.

Update2: I've cleaned up the code, which seems to have solved some of the problems. I've posted the new code as a new question here.

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

Update: Organization and User have a 1:many relationship. My question concerns a joined signup form where both an organization and user are required. After maxcal's help on the original post, I've written a new create method for my nested form ("organization has many users"), as shown below. Also I added begin...rescue...end to the create method. The situation/problem now:

  • 提交了所有有效信息,即可正常工作.
  • 提交了无效的组织信息(无论用户是否也是无效的),它都会按照我们希望的方式呈现带有错误消息的页面,但仅显示有关组织详细信息的错误.另外,对于用户详细信息,它随后清空了所有不应该的字段.
  • 仅向用户提交了无效信息,它将再次呈现该表单,但没有任何错误消息,并且已清空用户的所有字段.

任何人都知道代码有什么问题吗?问题似乎是嵌套用户而不是组织(父级).同样,users_attributes.empty?不起作用,因为根据日志,空的已提交表单仍包含此类属性:

Anyone got an idea what is wrong with the code? The problem seems to be more with the nested user than with organization (the parent). Also, users_attributes.empty? doesn't work, since an empty submitted form still includes such attributes, according to the log:

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



原始问题: 我有两个关联的模型和一个带有验证的嵌套表单.不幸的是,它不起作用. 1)在播种上,它会生成错误Validation failed: Users organization can't be blank.我先前发布的有关此问题并过早得出结论它已经解决了.还没有. 2)提交巢状的注册表单并正确填写所有字段,会产生Flash错误消息The form contains 1 error. Users organization can't be blank.



Original question: I have two associated models and a nested form with validation. Unfortunately, it’s not working. 1) On seeding it generates the error Validation failed: Users organization can't be blank. I previously posted a question about this and prematurely concluded it had solved it. It hasn’t. 2) Submitting my nested signup form with all fields filled in correctly, produces the flash error message The form contains 1 error. Users organization can't be blank.

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

How should I adjust my code to solve these issues?

模型文件:

#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

表格:

  <%= form_for @organization, url: organizations_path do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <%= f.text_field :name %>
    <%= f.text_field :bag %>
    <%= f.fields_for :users do |p| %>
      <%= p.email_field :email %>
      <%= p.text_field :username %>
      <%= p.text_field :fullname %>
      <%= p.password_field :password %>
      <%= p.password_field :password_confirmation %>
      <%= p.hidden_field :usertype, value: 2 %>
    <% end %>

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

In my Seeds file I have:

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"])

关于提交注册表单的错误的日志:

The log on the error on submitting the signup form:

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

推荐答案

由于抓住22

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

To apply for this job, you would have to be insane; but if you are insane, you are unacceptable.

ActiveRecord模型在保存时会从数据库中获取其ID. 但是对嵌套用户的验证是在将组织插入数据库之前进行的.

ActiveRecord models get their ID from the database when they are saved. But the validation on the nested user runs before the organization is inserted into the the database.

您会猜想,仅检查validates_presence_of会通过:

You would guess that just checking validates_presence_of instead would pass:

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

不幸的是没有.为了使validates_presence_of :organization通过,必须将组织持久保存到数据库中.再次赶上22.

Unfortunatly not. In order for validates_presence_of :organization to pass the organization must be persisted to the database. Catch-22 again.

为了使验证通过,我们需要将创建组织和用户分为两个步骤:

In order for the validation to pass we would need to split creating the organization and user into two steps:

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

不幸的是,您不能使用accepts_nested_attributes_for :users的方式-至少不能直接使用.

Unfortunatly the means that you cannot use accepts_nested_attributes_for :users - well at least not straight off the bat.

通过使用交易,我们可以将组织插入数据库,如果用户无效,则回滚.

By using a transaction we can insert the organization into the the database and and roll back if the user is not valid.

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?是因为无论是否创建了用户记录,我们都想重定向到新创建的组织.

Followup questions

We use @organization.persisted? since we presumably want to redirect to the newly created organisation no matter if the there is a User record created.

因为电子邮件已发送给用户?没关系,因为如果没有创建用户,则组织会回退.

because the emails are sent to users? It shouldn't matter since organization is rolled back if no user is created.

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

The transaction is not rolled back if there is no user created. Only if the user(s) fails to save due to invalid parameters. This is based on your requirement:

但是组织也可以(临时)没有用户.

But an organization can also (temporarily) have no users.

如果您需要@organisation在没有用户的情况下无效,则可以这样做:

If you need the @organisation to be invalid without users you could do:

  @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?是模型实例上的方法-不是集合.

You would use @organization.users.any? to check if there are any users. @organization.users.persisted? will not work since .persisted? is a method on model instances - not collections.

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

On a different note, I assume it's not possible to overwrite/update an existing organization/user with this method (which shouldn't be) instead of always creating a new record?

对,因为这将始终发出两个SQL插入语句,所以不会更改现有记录.

Right, since this will always issue two SQL insert statements it will not alter existing records.

但是,由您决定是否创建验证来保证数据库列的唯一性(即,您不希望具有相同user.email或organisation.name的多个记录).

It is up to you however to create validations that guarantee the uniqueness of the database columns (IE you don't want several records with the same user.email or organiation.name).

从好的方面来说,更新现有组织时这些警告均不适用:

On the plus side is that none of these caveats apply when updating an existing organization:

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

由于在验证用户时不会遇到整个鸡或蛋的困境.

Since you don't get the whole chicken or egg dilemma when validating the users.

这篇关于关联的模型和带有验证的嵌套表单不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆