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
阅读上面链接的文档中有关覆盖的更多信息。
我正在尝试设计一个具有一对多关系的数据库,其中 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
阅读上面链接的文档中有关覆盖的更多信息。