1

我正在构建一个应该显示作品列表的应用程序。每件作品都由Work模型表示,可以有多张图片,由WorkPicture模型表示。

工作.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_girlgem 设置它们的方法:

工厂.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 才能将其与作品正确关联,而这是不可能的,因为我的作品m created 尚未保存,因此没有 id。

我需要一种在构建图片时(在保存之前)将图片与其关联的方法,因为当保存作品时,它会验证它拥有的图片数量,并且由于它尚未与任何图片相关联,所以验证不会通过,它仍然没有图片的 id。

仔细一想,结果有点悖论,因为图片需要一个有id的作品才能关联到它,但是一个作品需要一张图片才能被分配一个id,所以两者都不是条件要靠对方的存在才能成真。

这是我的模型规格Work

工作规范.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

我的问题

我该如何解决这个问题,以便模型上至少存在一张图片的要求保持不变,但同时我将能够在它与图片关联之前创建(并保存)它的一个实例?

4

2 回答 2

0

我想我找到了解决办法。在控制台中摆弄之后,我发现使用work.pictures.build,我可以创建一个work_picture绑定到我的模型的虚拟模型,即使它没有被保存。这使得可以将图片与其关联并在保存之前通过验证。保存后,一切正常。

以下是我所做的更改:

工厂.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

我还调整了我的测试以验证一切正常:

工作规范.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
于 2013-09-27T13:00:05.253 回答
0

尝试将 work_picture.rb 的第一行更改为

belongs_to :work, inverse_of: :pictures
于 2013-09-26T22:49:28.697 回答