Rails在其他模型的方法中找到模型

时间:2015-08-12 17:53:11

标签: ruby-on-rails

我想为以下内容实现服务器端验证: 只要订阅数量不超过此发布的点数,就只能创建特定发布的订阅。

    class Cposting < ActiveRecord::Base
      belongs_to :user 
      has_many :subscriptions, 
                foreign_key: "post_id", 
                dependent: :destroy
...

    def spots_left #returns the number of places left in this class
      self.spots.to_i - Subscription.where(post_id: self.id).count.to_i
    end
...
end

在订阅模型中,我尝试调用spots_left方法来确定新订阅所属的Cposting是否还有剩余点。

class Subscription < ActiveRecord::Base
    belongs_to :subscriber, class_name: "User"
    belongs_to :post, class_name: "Cposting"

    ...
    validate :class_not_full



def class_not_full
    Cposting.find_by(id: self.post_id).spots_left > 0 
end
end

在Subscription模型上运行测试返回了nil错误

NoMethodError: undefined method `spots_left' for nil:NilClass

似乎我不能使用find_by,find或where方法指向此Cposting。

我想知道如何引用属于正在验证的订阅的Cposting,或者实现此验证的替代方法。

由于

编辑添加测试

require 'test_helper'

class SubscriptionTest < ActiveSupport::TestCase
  def setup
    @cposting = cpostings(:one) #has one spot
    @customer = users(:customer)
    @customer2 = users(:customer2)
    @subscription = Subscription.new(post_id: @cposting.id, subscriber_id: @customer.id)
end

...

  test "subscriptions cannot exceed spots" do
    @subscription.save
    assert @cposting.subscriptions.count == @cposting.spots 
    @subscription2 = Subscription.new(post_id: @cposting.id, subscriber_id: @customer2.id)
    assert_not @subscription2.valid?
  end

end

正在运行rake test TEST=test/models/subscription_test.rb

  1) Failure:
SubscriptionTest#test_subscriptions_cannot_exceed_spots [/~/test/models/subscription_test.rb:37]:
Expected true to be nil or false

5 runs, 7 assertions, 1 failures, 0 errors, 0 skips

编辑2添加创建方法

class SubscriptionsController < ApplicationController
    def create 
        @posting = Cposting.find(params[:post_id])  
        current_user.subscriptions.create(post_id: @posting.id)
        flash[:success] = "Subscribed!"
        redirect_to subscriptions_path
    end
end

2 个答案:

答案 0 :(得分:0)

利用rails关系。你不需要再次查询所有内容。

尝试以下方法:

class Cposting < ActiveRecord::Base
  belongs_to :user 
  has_many :subscriptions, 
            foreign_key: "post_id", 
            dependent: :destroy

  def spots_left
    self.spots - self.subscriptions.count # i assume that spots is an integer db field
  end
end

class Subscription < ActiveRecord::Base
  belongs_to :subscriber, class_name: "User"
  belongs_to :post, class_name: "Cposting"

  validate :class_not_full

  def class_not_full
    post.spots_left > 0 
  end
end

创建订阅:

@cposting.build_subscription(subscriber: @customer2) 

Rails为您提供了一系列可供选择的方法。你甚至不需要使用ID。只需使用关系。一般来说,当你坚持使用AR方法时,我发现Rails工作得更顺畅(在命名表时坚持使用rails约定是个好主意)  请仔细阅读this,它会为您提供很多帮助。

答案 1 :(得分:0)

如果测试没有通过,则通过添加nil检查和错误来修复nil错误。

    validate :class_not_full

def class_not_full #checks if there are spots left for the posting
    errors.add(:post, "posting is already full") unless !post.nil? && post.spots > post.subscriptions.count 
end