在ActiveRecord方法中无意中第二次调用数据库

时间:2014-12-22 16:12:56

标签: ruby-on-rails activerecord callback

我试图实现一个功能,用户可以使用订阅代码注册订阅。这是我的代码:

 class SubscriptionsController < ApplicationController

    def create
      @subscription = current_user.subscriptions.build(subscription_params)



      respond_to do |format|
        if @subscription.save
          format.html { redirect_to @subscription, notice: 'Subscription was successfully created.' }
          format.json { render :show, status: :created, location: @subscription }
        else
          format.html { render :new }
          format.json { render json: @subscription.errors, status: :unprocessable_entity }
        end
      end
    end
  end



 class Subscription < ActiveRecord::Base
  belongs_to :user
  belongs_to :topic
  has_one :subscription_code
  before_validation :register_subscription
  after_save :register_expiry_date
  validates_presence_of :submitted_code, :topic_id, :subscription_code_id

  attr_accessor :submitted_code

  private

    def register_subscription

      registered_code_id = SubscriptionCode.register_subscription_code(self.submitted_code)

      unless registered_code_id == nil
        self.subscription_code_id = registered_code_id
      else
        errors.add(:submitted_code, "Invalid Code")
        return false
      end
    end

    def register_expiry_date
      self.expiry_date ||= self.created_at + 1.year
      self.save
    end
end



class SubscriptionCode < ActiveRecord::Base
  belongs_to :subscription
  attr_accessor :number_of_codes

def self.register_subscription_code(submitted_code)
    matched_code = find_by(code: submitted_code, used: false)
    matched_code.update(used: true, date_used: DateTime.now)
    return matched_code.id
  end 
end

我得到一个no方法错误,这是我的日志。

SubscriptionsController处理#create as HTML   参数

{"utf8"=>"✓", "authenticity_token"=>"iV5cOOi+IgyVUuq0rR5yIdCwYDCrRHGf4Hvw25kGso0=", "subscription"=>{"topic_id"=>"1", "submitted_code"=>"89aaa22e40958b40092f"}, "commit"=>"Create Subscription"}
  User Load (0.3ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = $1 LIMIT 1  [["id", 3]]

   (0.2ms)  BEGIN

  SubscriptionCode Load (0.7ms)  SELECT  "subscription_codes".* FROM "subscription_codes"  WHERE "subscription_codes"."code" = '89aaa22e40958b40092f' AND "subscription_codes"."used" = 'f' LIMIT 1

  SQL (35.8ms)  UPDATE "subscription_codes" SET "date_used" = $1, "updated_at" = $2, "used" = $3 WHERE "subscription_codes"."id" = 7  [["date_used", "2014-12-22 15:31:45.258082"], ["updated_at", "2014-12-22 15:31:45.280165"], ["used", "t"]]

  SQL (1.4ms)  INSERT INTO "subscriptions" ("created_at", "subscription_code_id", "topic_id", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["created_at", "2014-12-22 15:31:45.318757"], ["subscription_code_id", 7], ["topic_id", 1], ["updated_at", "2014-12-22 15:31:45.318757"], ["user_id", 3]]

  SubscriptionCode Load (0.6ms)  SELECT  "subscription_codes".* FROM "subscription_codes"  WHERE "subscription_codes"."code" = '89aaa22e40958b40092f' AND "subscription_codes"."used" = 'f' LIMIT 1
   (0.3ms)  ROLLBACK

Completed 500 Internal Server Error in 129ms

NoMethodError - undefined method `update' for nil:NilClass:
  app/models/subscription_code.rb:17:in `register_subscription_code'

1 个答案:

答案 0 :(得分:0)

一些观察结果:

您将在每次保存时调用:register_subscription,无论是创建还是更新。 尝试:

before_validation :register_subscription, on: :create

此外,如果:register_subscription_code无法找到代码,则会抛出undefined method update错误。尝试:

def self.register_subscription_code(submitted_code)
  matched_code = find_by(code: submitted_code, used: false)
  return nil unless matched_code
  matched_code.update(used: true, date_used: DateTime.now)
  matched_code.id # returns are implied at the ends of methods
end 

此方法将返回nil,错误将附加到Subscription。

最后一点,这是一个小问题,但在Rails中nil是假的,因此unless registered_code_id == nil代替if registered_code_id更容易阅读submitted_code

最后一点,您已经在:register_subscription上进行了验证,因此您无需在def register_subscription self.subscription_code_id = SubscriptionCode.register_subscription_code(self.submitted_code) end 中对其进行预验证。它只需要看起来像这样:

submitted_code

好的,最后一点,您不需要在attr_accessor上验证,这是{{1}}。验证用于保持数据库中的数据一致,而不是虚拟属性。