问题
Ok, this has me wildly stumped. I have a model PhoneNumbers which has an integer column sent_messages_count. I use background tasks to use this column as a counter cache, however, this column's inaccuracies appears to be more fundamental.
Here's what I'd like to do:
phone_number.sent_messages_count = phone_number.sent_messages.count
Note that I'm setting the table column to the count of an association. Underscore vs. period.
See my console session... (I've done some clean-up to make it more terse).
# Find the phone number
[1] pry(main)> pn = PhoneNumber.find 38
PhoneNumber Load (1.0ms) SELECT "phone_numbers".* FROM "phone_numbers" WHERE "phone_numbers"."id" = $1 LIMIT 1 [["id", 38]]
=> #<PhoneNumber:0x007f97902f6d70
id: 38,
updated_at: Thu, 07 Apr 2016 12:34:17 CDT -05:00,
sent_messages_count: 12>
# Change the field to a new value
[2] pry(main)> pn.sent_messages_count = 100000
=> 100000
# Save it! It succeeds!
[3] pry(main)> pn.save
(0.2ms) BEGIN
SQL (1.0ms) UPDATE "phone_numbers" SET "sent_messages_count" = $1, "updated_at" = $2 WHERE "phone_numbers"."id" = $3 [["sent_messages_count", 100000], ["updated_at", "2016-04-07 17:37:41.826069"], ["id", 38]]
(14.2ms) COMMIT
=> true
# Reload it, and the saved value persists
[4] pry(main)> pn = PhoneNumber.find 38
PhoneNumber Load (6.2ms) SELECT "phone_numbers".* FROM "phone_numbers" WHERE "phone_numbers"."id" = $1 LIMIT 1 [["id", 38]]
=> #<PhoneNumber:0x007f9790dc47e8
id: 38,
updated_at: Thu, 07 Apr 2016 12:37:41 CDT -05:00,
sent_messages_count: 100000>
# Now, let's get the association count. It's 49.
[5] pry(main)> pn.sent_messages.count
(1.6ms) SELECT COUNT(*) FROM "messages" WHERE "messages"."from_id" = $1 [["from_id", 38]]
=> 49
# Setting column to association count
[6] pry(main)> pn.sent_messages_count = pn.sent_messages.count
(0.6ms) SELECT COUNT(*) FROM "messages" WHERE "messages"."from_id" = $1 [["from_id", 38]]
=> 49
# Look, the attribute has the counted value
[7] pry(main)> pn.sent_messages_count
=> 49
# And the in-memory instance had that value (I know, it's redundant)
[8] pry(main)> pn
=> #<PhoneNumber:0x007f9790dc47e8
id: 38,
updated_at: Thu, 07 Apr 2016 12:37:41 CDT -05:00,
sent_messages_count: 49>
# Yep, we've changed!
[9] pry(main)> pn.changed?
=> true
# And we're valid!
[10] pry(main)> pn.valid?
PhoneNumber Exists (0.8ms) SELECT 1 AS one FROM "phone_numbers" WHERE ("phone_numbers"."number" = 'REDACTED' AND "phone_numbers"."id" != 38 AND "phone_numbers"."account_id" = 2) LIMIT 1
=> true
# And we're saving! ... but why is only updated_at changing?
[11] pry(main)> pn.save
(0.2ms) BEGIN
SQL (0.7ms) UPDATE "phone_numbers" SET "updated_at" = $1 WHERE "phone_numbers"."id" = $2 [["updated_at", "2016-04-07 17:39:07.200473"], ["id", 38]]
(5.8ms) COMMIT
=> true
# But our in-memory pn thinks it's 49
[12] pry(main)> pn
=> #<PhoneNumber:0x007f9790dc47e8
id: 38,
updated_at: Thu, 07 Apr 2016 12:39:07 CDT -05:00,
sent_messages_count: 49>
# But reloading makes me terribly sad. :(
[13] pry(main)> pn = PhoneNumber.find 38
PhoneNumber Load (6.1ms) SELECT "phone_numbers".* FROM "phone_numbers" WHERE "phone_numbers"."id" = $1 LIMIT 1 [["id", 38]]
=> #<PhoneNumber:0x007f97912dc820
id: 38,
updated_at: Thu, 07 Apr 2016 12:39:07 CDT -05:00,
sent_messages_count: 100000>
What's unclear to me is why setting the attribute to a calculated integer, instead of a literal integer is causing the column to not be saved.
Wildness and sadness. Anyone able to help me out?
Update
Including both the model and my schema. I'm omitting some class methods so this doesn't become the longest question ever.
models/phone_number.rb
include ActionView::Helpers::NumberHelper
class PhoneNumber < ActiveRecord::Base
has_many :callable_phone_numbers
has_many :students,
through: :callable_phone_numbers,
source: :callable,
source_type: 'Student', inverse_of: :phone_numbers
has_many :teachers,
through: :callable_phone_numbers,
source: :callable,
source_type: 'Teacher'
belongs_to :account
has_many :sent_messages,
class_name: 'Message',
foreign_key: 'from_id',
inverse_of: :from_phone_number
has_many :received_messages,
class_name: 'Message',
foreign_key: 'to_id',
inverse_of: :to_phone_number
phony_normalize :number, default_country_code: 'US'
validates_plausible_phone :number, presence: true
validates :label, presence: true
validates :account_id, presence: true
validates :number, uniqueness: { scope: :account_id, message: "should only happen once per account" }, unless: :skip_uniqueness_validation
attr_accessor :skip_uniqueness_validation
# ... a bunch of scopes ...
def messages
Message.where("from_id = ? OR to_id = ?", id, id)
end
# ... some other class methods ...
def most_recent_sent_message
sent_messages.order("created_at DESC").take
end
def most_recent_received_message
received_messages.order("created_at DESC").take
end
def update_caches!
self.last_sent_message_at = self.most_recent_sent_message.try(:created_at)
self.last_received_message_at = self.most_recent_received_message.try(:created_at)
self.sent_messages_count = self.sent_messages.count
self.received_messages_count = self.received_messages.count
self.save
end
end
schema.rb
create_table "phone_numbers", force: :cascade do |t|
t.string "number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "label"
t.boolean "assigned"
t.integer "account_id"
t.integer "sent_messages_count"
t.integer "received_messages_count"
t.datetime "last_sent_message_at"
t.datetime "last_received_message_at"
t.string "carrier_name"
t.string "carrier_type"
t.datetime "carrier_updated_at"
t.string "home_language", default: "en"
t.boolean "disconnected"
end
来源:https://stackoverflow.com/questions/36483809/rails-model-fails-to-save-cached-column-counts