在has_many关系上添加金额约束,而不会使模型无法验证

时间:2013-09-26 22:36:24

标签: ruby-on-rails ruby validation relationship has-many

我正在构建一个应该显示作品列表的应用程序。每件作品都由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

我的问题

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

2 个答案:

答案 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