rails 中的外键验证

Foreign key validation in rails

我正在尝试设计一个具有一对多关系的数据库,其中 1 parent 可以有多个 children。数据库似乎工作正常。我正在尝试编写集成测试来检查无效数据并且工作正常,但是当我尝试将有效数据输入数据库时​​它失败了。它给了我一个与下面类似的错误。是我的代码做错了什么,还是我的测试有问题?

当我输出child.errors

.F........#<ActiveModel::Errors:0x007fe0fccb50b8 @base=#<Play id: nil, ad_id: 42, duration: 8, created_at: nil, updated_at: nil>, @messages={:ad=>["can't be blank"]}>
.#<ActiveModel::Errors:0x007fe0fa5756d8 @base=#<Play id: nil, ad_id: 2, duration: 8, created_at: nil, updated_at: nil>, @messages={:ad=>["can't be blank"]}>

综合测试

def setup
    Ad.create!(ad_id: 2, device_id: 1, user_id: 1, cost:300.20, title: 'my awesome ad')
    Ad.create!(ad_id: 3, device_id: 2, user_id: 3, cost:40, title: 'super')
    Ad.create!(ad_id: 1, device_id: 1, user_id: 2, cost:500, title: 'my awesome ad')
    Ad.create!(ad_id: 4, device_id: 2, user_id: 2, cost:500, title: 'super')
end
test 'log a valid play' do
  post '/plays',      
      play: {
             ad_id: 2,
             duration: 8
           }
         }
    assert_equal 201, response.status
  end

测试失败

  1) Failure:
    LogPlayTest#test_log_a_valid_play [/path/log_play_test.rb:18]:
    Expected: 201
    Actual: 422

Child迁移

class CreatePlays < ActiveRecord::Migration
  def change
    create_table :plays do |t|
      t.integer :ad_id
      t.integer :duration

      t.timestamps null: false
    end
    add_index :plays, :ad_id
  end
end

Parent迁移

class CreateAds < ActiveRecord::Migration
  def change
    create_table :ads do |t|
      t.integer :ad_id
      t.integer :device_id
      t.integer :user_id
      t.float :cost
      t.string :title

      t.timestamps null: false
    end
    add_index :ads, :ad_id
  end
end

Parent型号

class Ad < ActiveRecord::Base
  has_many :plays
end

Child型号

class Play < ActiveRecord::Base
  belongs_to :ad
  validates :ad, presence: true
end

Child 控制器

def create
    play = Play.new(play_params)
    if play.save
      p play.save 
      render nothing:true, status: 201, location: play
    else
      p play.errors #the output is shown above
      render json: play.errors, status: 422
    end
  end

我会说问题出在外键上。当您将测试广告放入数据库时​​,您给每个广告一个 ad_id:

Ad.create!(ad_id: 2...)

我不完全确定你在那里做什么,但如果那是主键,我建议遵循约定并使用 Rails 自动为你生成的 ID。例如:

Ad.first.id == 1

与广告相关的播放将此主键存储为外键:

Play.first.ad_id == 1

因此:

Play.ad.id == 1

您似乎在尝试使用自定义外键,但没有告诉 Rails 您在做什么,这就是找不到父广告的原因。您的两个选择是两个遵循约定或覆盖外键。在此处阅读有关覆盖的信息:http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference

澄清

如果您选择遵循惯例,只需从广告 table 中删除 ad_id 属性并改用 ID。您可以像这样建立关联以自动设置外键:

@ad.plays.new()

如果你想覆盖外键,试试:

class Play < ActiveRecord::Base
  belongs_to :ad, foreign_key: "ad_id"
end

阅读上面链接的文档中有关覆盖的更多信息。