我正在构建一个应该显示作品列表的应用程序。每件作品都由Work
模型表示,可以有多张图片,由WorkPicture
模型表示。
work.rb
class Work < ActiveRecord::Base
belongs_to :category
has_many :pictures, class_name: "WorkPicture", dependent: :destroy
accepts_nested_attributes_for :pictures, allow_destroy: true
# Ensures that we have at least 1 picture
validates :pictures, length: { minimum: 1, message: "are not enough." }
validates :name, presence: true
validates :description, presence: true
validates :category_id, presence: true
end
我正在使用第validates :pictures, length: { minimum: 1, message: "are not enough." }
行来确保此模型的任何实例至少附有一张图片。
这是图片的模型(它使用附件图片的paperclip
宝石):
work_picture.rb
class WorkPicture < ActiveRecord::Base
belongs_to :work
CONVERT_OPTIONS = "-gravity north"
has_attached_file :picture, styles: { big: ["945x584#", :png], thumb: ["360x222#", :png] },
convert_options: { big: CONVERT_OPTIONS, thumb: CONVERT_OPTIONS }
validates_attachment :picture, presence: true,
content_type: { content_type: /image/ }
end
我无法测试这两种模型之间的关系。以下是我尝试使用factory_girl
gem设置它们的方法:
factories.rb
include ActionDispatch::TestProcess
FactoryGirl.define do
factory :work do
sequence(:name) { |n| "Project #{n}" }
description "A description"
category
after :build do |work, evaluator|
create(:work_picture, work: work) # Creating a picture for this work
end
end
factory :category do
sequence(:name) { |n| "Category #{n}" }
end
factory :work_picture do
picture { fixture_file_upload Rails.root.join("spec/fixtures/files/work.png"), "image/png" }
work
after :create do |work_picture, evaluator|
evaluator.picture.close
end
end
end
在:work_picture
工厂中,我只需添加预定义图片即可。
在:work
工厂中,我尝试在刚刚创建工作时为工作添加图片,但由于work_picture
需要有效的ID,因此无法完成此操作与作品正确关联,这是不可能的,因为我正在创作的作品尚未保存,因此没有ID。
我需要一种方法在构建图片时(保存之前)将图片关联到它,因为当工作保存时,它会验证图片的数量,并且因为它没有与任何图片相关联但是,验证不会通过,它仍然没有图片的ID。
如果你考虑一下,事实证明这有点悖论,因为图片需要一个带有id的工作与它相关联,但是一个工作需要一张图片才能被赋予一个id,所以没有这两个条件在它们成真之前依赖于另一个条件。
以下是我对Work
模型的规范:
work_spec.rb
require 'spec_helper'
describe Work do
let!(:work) { build(:work) }
subject { work }
it { should respond_to :name }
it { should respond_to :description }
it { should respond_to :category }
it { should respond_to :category_id }
it { should respond_to :pictures }
it { should be_valid } # Fails, because we have no picture
# ... Unrelated tests omitted
# This test is here to check if the factory_girl setup is working
describe "picture count" do
it "should be 1" do
expect(work.pictures.count).to eq 1 # Fails, because we are unable to add the picture
end
end
end
如何解决这个问题,以便模型上至少存在一张图片的要求仍然存在,但同时我可以在它与之关联之前创建(并保存)它的实例一张照片?
答案 0 :(得分:0)
尝试将work_picture.rb的第一行更改为
belongs_to :work, inverse_of: :pictures
答案 1 :(得分:0)
我想我找到了解决方案。在控制台中摆弄后,我发现,使用work.pictures.build
,我可以创建一个绑定到我的模型的虚拟work_picture
,即使它没有被保存。这使得可以将图片与其关联并在保存之前传递验证。一旦保存,一切正常。
以下是我所做的更改:
<强> factories.rb 强>
include ActionDispatch::TestProcess
FactoryGirl.define do
# ...
factory :work do
sequence(:name) { |n| "Project #{n}" }
description "A cool description"
category
after :build do |work|
work_picture = work.pictures.build # Builds a dummy picture
work_picture.update_attributes(attributes_for(:work_picture)) # Loads the picture file
end
end
# ...
end
我还调整了我的测试以验证一切正常:
<强> work_spec.rb 强>
require 'spec_helper'
describe Work do
# ...
# Checks if the factory_girl setup is working
describe "picture length" do
it "should be 1" do
expect(work.pictures.length).to eq 1
end
end
describe "picture count after save" do
before { work.save }
it "should be 1" do
expect(work.pictures.count).to eq 1
end
end
end