如何通过_ids参数保存HABTM关系

时间:2016-01-25 16:50:40

标签: ruby ruby-on-rails-4

我有一个项目,用户和项目之间存在has_and_belongs_to_many关系。我尝试使用项目表单上的复选框创建项目以选择用户,但它在保存阶段失败并显示错误Users is invalid

我的用户模型是:

class User < ActiveRecord::Base
  has_and_belongs_to_many :projects

  devise :database_authenticatable,
    :recoverable,
    :rememberable,
    :trackable,
    :lockable,
    :timeoutable

  validates :username,
    presence: true,
    length: { minimum: USER_NAME_MIN_LENGTH,
              maximum: USER_NAME_MAX_LENGTH },
    allow_blank: false,
    allow_nil: false,
    uniqueness: { case_sensitive: false }

  validates_format_of :email,
    with: Devise.email_regexp,
    if: :email_changed?

  validates_presence_of :password,
    on: :create

  validates_confirmation_of :password
  validates_length_of :password,
     within: Devise.password_length
  validates_presence_of :role

我的项目模型是:

class Project < ActiveRecord::Base
  has_and_belongs_to_many :users
  validates :name,
    uniqueness: true,
    presence: true,
    length: {within: 1..PROJECT_NAME_MAX_LENGTH}

  validates :base_year,
    presence: true,
    numericality:
       {only_integer: true,
       greater_than_or_equal_to: BASE_YEAR_MIN,
       less_than_or_equal_to: BASE_YEAR_MAX}

  validates :client, length: {maximum: CLIENT_NAME_MAX_LENGTH}

  validates :work_order, length: {maximum: WORK_ORDER_MAX_LENGTH}
end

我的表单视图(HAML)是:

= form_for(project) do |f|
  - if project.errors.any?
    #error_explanation
      %h2
        = pluralize(project.errors.count, "error")
        prevented this project being saved:
      %ul
        - project.errors.full_messages.each do |message|
          %li= message
  %table
    %tr.field
      %td= f.label :name
      %td= f.text_field :name
    %tr.field
      %td= f.label :base_year
      %td= f.number_field :base_year
    %tr.field
      %td= f.label :work_order
      %td= f.text_field :work_order
    %tr.field
      %td= f.label :client
      %td= f.text_field :client
    %tr.field
      %td= f.label :active
      %td= f.check_box :active
  %h2 Users
  - User.all.each do |user|
    %td= check_box_tag 'project[user_ids][]', user.id,     @project.users.include?(user)
    %td= user.username
  .actions
    = f.submit 'Save'

我已经将一些调试打印语句放入项目控制器中,以查看它出错的地方。该控制器的创建部分是:

def create
  @project = Project.new(project_params)
  @project.users.each do |user|
    puts "=== User: #{user.username}"
  end

  respond_to do |format|
    if @project.save then
      # Print statement for debugging
      puts '=== Save success'
      on_save_success(format,
                      Project,
                      @project,
                      notice: 'Project was successfully created.')
    else
      # Print statement for debugging
      puts '=== Save fail'
      on_save_fail(format, @project.errors)
    end
  end
end

@project = Project.new(project_params)似乎成功,后续块正确显示我在表单上检查过的所有用户(实际上只有一个),但保存是失败。控制台输出是:

Started POST "/projects" for 127.0.0.1 at 2016-01-25 16:11:18 +0000
Processing by ProjectsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"S4Kawr8r7nVlpZdimAcvkER0lB70UQZ1zOoo8/82g4g=", "project"=>{"name"=>"Test", "base_year"=>"2000", "work_order"=>"", "client"=>"", "active"=>"0", "user_ids"=>["1"]}, "commit"=>"Save"}                                         
  User Load (0.5ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]                                        
=== User: alice                                                                                                                         
    (0.0ms)  begin transaction                                                                                                           
  User Exists (0.0ms)  SELECT  1 AS one FROM "users"  WHERE (LOWER("users"."username") = LOWER('alice') AND "users"."id" != 1) LIMIT 1  
  Project Exists (0.0ms)  SELECT  1 AS one FROM "projects"  WHERE "projects"."name" = 'Test' LIMIT 1                                    
    (1.0ms)  rollback transaction                                                                                                        
=== Save fail

我无法看到它对用户的反对意见 - 对它们没有任何验证。我究竟做错了什么?

编辑: 我无法解决如何添加调试器的问题,所以我只是将@ project.valid和@project_errors作为调试打印语句:

@project = Project.new(project_params)
puts "=== Project is #{@project.valid? ? "" : "not "} valid"
puts "=== Errors:"
puts @project.errors.full_messages.to_sentence

结果是:

Started POST "/projects" for 127.0.0.1 at 2016-01-29 16:06:13 +0000
Processing by ProjectsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"WAIMP+Z/g0bKFWhh/DUcroMooJoPTsUx3A9y6XZCJWk=", "project"=>{"name"=>"Test", "base_year"=>"2000", "work_order"=>"", "client"=>"", "active"=>"1", "user_ids"=>["1"]}, "commit"=>"Save"}
  User Load (0.0ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  User Exists (1.0ms)  SELECT  1 AS one FROM "users"  WHERE (LOWER("users"."username") = LOWER('alice') AND "users"."id" != 1) LIMIT 1
  Project Exists (1.0ms)  SELECT  1 AS one FROM "projects"  WHERE "projects"."name" = 'Test' LIMIT 1
=== Project is not  valid
=== Errors:
Users is invalid
  (0.0ms)  begin transaction
  CACHE (0.0ms)  SELECT  1 AS one FROM "users"  WHERE (LOWER("users"."username") = LOWER('alice') AND "users"."id" != 1) LIMIT 1

  CACHE (0.0ms)  SELECT  1 AS one FROM "projects"  WHERE "projects"."name" = 'Test' LIMIT 1
  (0.0ms)  rollback transaction
=== Save fail

3 个答案:

答案 0 :(得分:0)

我已经确认问题是Rails正在尝试验证用户条目,即使不需要验证也失败,因为我验证明文密码的长度,当我保存项目时不存在。因此,我正在关闭此问题,并发布a new question that is more specific to the issue as I now understand it

答案 1 :(得分:0)

面对相同的问题,我通过以下2项更改解决了该问题

  • 在您的check_box_tag之前添加<%= hidden_​​field_tag“ project [user_ids] []”,无%>行。

  • 确保您现有的用户有效,如果您添加无效用户,则将显示“用户无效”错误。

答案 2 :(得分:0)

在check_box_tag之前添加以下隐藏标签。

Social

hidden_​​field_tag允许用户取消选中所有复选框并成功删除所有关联。

或者您可以使用collection_check_boxes

`<%= hidden_field_tag("project[user_ids][]", nil) %>`