Rails: delete_all raises PG::NotNullViolation - ruby-on-rails

I am importing a CSV with a few hundred rows into my rails database.
Occasionally the user wants to force overwrite the data so I figured that it would be best to destroy all the data and start fresh.
something like:
account.catalog_listings.delete_all if should_refresh
CSV.foreach(file, options) do |row|
account.catalog_listings.create!({...rowstuff})
problem is that delete_all line is raising the PG error
ActiveRecord::StatementInvalid (PG::NotNullViolation: ERROR: null value in column "account_id" violates not-null constraint
DETAIL: Failing row contains (1, null, ... ... ).
: UPDATE "catalog_listings" SET "account_id" = NULL WHERE "catalog_listings"."account_id" = $1):
app/models/catalog_listing.rb:41:in `import_catalog_listings'
app/controllers/accounts_controller.rb:20:in `catalog'
I DO have a null: false on a couple foreign key fields but i cannot figure out why delete_all is trying to remove the foreign key instead of removing the whole record?
UPDATE - everything works when I change:
account.catalog_listings.delete_all if should_refresh
to:
account.catalog_listings.destroy_all if should_refresh
except that destroy goes through each item and deletes one-by-one:
SQL (0.1ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 957]]
SQL (0.1ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 958]]
SQL (0.1ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 959]]
SQL (0.1ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 960]]
SQL (0.1ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 961]]
SQL (0.1ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 962]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 963]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 964]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 965]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 966]]
SQL (0.3ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 967]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 968]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 969]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 970]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 971]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 972]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 973]]
SQL (0.3ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 974]]
SQL (0.2ms) DELETE FROM "catalog_listings" WHERE "catalog_listings"."id" = $1 [["id", 975]]
not cool... anyone know a better way to do this?

Try adding dependent: :destroy in the catalog_listings association on your Account model.
https://apidock.com/rails/ActiveRecord/Associations/CollectionProxy/delete_all

Related

Rails duplicate GET requests

I have a Rails application sending double GET requests. I have found that the issue doesn't exist with POST requests.
The log shows I'm testing the website locally, but the issue persists on production(Heroku).
I found several reports of the same issue related to turbolinks, after that I proceeded to remove everything turbolinks related from my code. Issue still persisted. I also found some people with similar issues related to empty img tags, or url(). I haven't found anything like that either. I also tried deleting all html being rendered on certain pages to try and see if the error was there, including the partial renders, still no success. Now I am completely clueless.
Here's the log showing the double request:
Started GET "/" for 127.0.0.1 at 2019-07-04 18:38:30 -0300
Processing by HomeController#index as HTML
Cart Load (0.5ms) SELECT "carts".* FROM "carts" WHERE "carts"."id" IS NULL LIMIT $1 [["LIMIT", 1]]
Setting Load (0.5ms) SELECT "settings".* FROM "settings" WHERE "settings"."key" = $1 LIMIT $2 [["key", "featured_artist_slug"], ["LIMIT", 1]]
Artist Load (33.8ms) SELECT "users".* FROM "users" left outer join (select user_id from arts group by user_id) as arts on users.id = arts.user_id WHERE (arts.user_id is not null) AND "users"."slug" = $1 LIMIT $2 [["slug", "herisson-artes"], ["LIMIT", 1]]
Rendering home/index.html.erb within layouts/application
(0.5ms) SELECT COUNT(*) AS "size", MAX("banners"."updated_at") AS timestamp FROM "banners" WHERE "banners"."active" = $1 [["active", true]]
Admin::Banner Load (0.5ms) SELECT "banners".* FROM "banners" WHERE "banners"."active" = $1 ORDER BY "banners"."position" ASC [["active", true]]
Rendered home/index.html.erb within layouts/application (18.4ms)
Rendered shared/_google_tag_manager_head.html.erb (0.8ms)
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 56592], ["LIMIT", 1]]
Rendered shared/_google_analytics.html.erb (4.7ms)
Rendered layouts/application/_favicons.html.erb (2.6ms)
Rendered shared/_seo.html.erb (4.1ms)
Rendered layouts/application/_head.html.erb (20.4ms)
Rendered shared/_google_tag_manager_body.html.erb (1.1ms)
Rendered shared/_facebook_js_sdk.html (0.4ms)
(0.9ms) SELECT COUNT(*) AS "size", MAX("coupons"."updated_at") AS timestamp FROM "coupons"
Coupon Load (4.6ms) SELECT "coupons".* FROM "coupons"
Rendered shared/_smartbar.html.erb (219.3ms) [cache hit]
Rendered layouts/application/_navbar.html.erb (7.6ms) [cache hit]
Rendered shared/_alert.html.erb (0.9ms)
Rendered layouts/application/_footer.html.erb (3.4ms) [cache hit]
Art Load (0.6ms) SELECT "arts".* FROM "arts" WHERE "arts"."active" = $1 AND "arts"."user_id" = $2 LIMIT $3 [["active", true], ["user_id", 56592], ["LIMIT", 1]]
(1.2ms) SELECT COUNT(*) FROM "arts" WHERE "arts"."active" = $1 AND "arts"."user_id" = $2 [["active", true], ["user_id", 56592]]
Order Exists (0.5ms) SELECT 1 AS one FROM "orders" WHERE "orders"."deleted_at" IS NULL AND "orders"."user_id" = $1 LIMIT $2 [["user_id", 56592], ["LIMIT", 1]]
Rendered shared/_crisp.html.erb (12.2ms)
Completed 200 OK in 349ms (Views: 291.6ms | ActiveRecord: 44.3ms)
Started GET "/" for 127.0.0.1 at 2019-07-04 18:38:30 -0300
Processing by HomeController#index as HTML
Cart Load (0.5ms) SELECT "carts".* FROM "carts" WHERE "carts"."id" IS NULL LIMIT $1 [["LIMIT", 1]]
Setting Load (0.7ms) SELECT "settings".* FROM "settings" WHERE "settings"."key" = $1 LIMIT $2 [["key", "featured_artist_slug"], ["LIMIT", 1]]
Artist Load (140.1ms) SELECT "users".* FROM "users" left outer join (select user_id from arts group by user_id) as arts on users.id = arts.user_id WHERE (arts.user_id is not null) AND "users"."slug" = $1 LIMIT $2 [["slug", "herisson-artes"], ["LIMIT", 1]]
Rendering home/index.html.erb within layouts/application
(0.6ms) SELECT COUNT(*) AS "size", MAX("banners"."updated_at") AS timestamp FROM "banners" WHERE "banners"."active" = $1 [["active", true]]
Admin::Banner Load (1.2ms) SELECT "banners".* FROM "banners" WHERE "banners"."active" = $1 ORDER BY "banners"."position" ASC [["active", true]]
Rendered home/index.html.erb within layouts/application (26.7ms)
Rendered shared/_google_tag_manager_head.html.erb (0.7ms)
User Load (1.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 56592], ["LIMIT", 1]]
Rendered shared/_google_analytics.html.erb (6.8ms)
Rendered layouts/application/_favicons.html.erb (2.8ms)
Rendered shared/_seo.html.erb (5.6ms)
Rendered layouts/application/_head.html.erb (27.5ms)
Rendered shared/_google_tag_manager_body.html.erb (3.1ms)
Rendered shared/_facebook_js_sdk.html (0.7ms)
(1.8ms) SELECT COUNT(*) AS "size", MAX("coupons"."updated_at") AS timestamp FROM "coupons"
Coupon Load (20.7ms) SELECT "coupons".* FROM "coupons"
Rendered shared/_smartbar.html.erb (812.0ms) [cache hit]
Rendered layouts/application/_navbar.html.erb (13.8ms) [cache hit]
Rendered shared/_alert.html.erb (2.1ms)
Rendered layouts/application/_footer.html.erb (7.5ms) [cache hit]
Art Load (1.0ms) SELECT "arts".* FROM "arts" WHERE "arts"."active" = $1 AND "arts"."user_id" = $2 LIMIT $3 [["active", true], ["user_id", 56592], ["LIMIT", 1]]
(1.5ms) SELECT COUNT(*) FROM "arts" WHERE "arts"."active" = $1 AND "arts"."user_id" = $2 [["active", true], ["user_id", 56592]]
Order Exists (1.3ms) SELECT 1 AS one FROM "orders" WHERE "orders"."deleted_at" IS NULL AND "orders"."user_id" = $1 LIMIT $2 [["user_id", 56592], ["LIMIT", 1]]
Rendered shared/_crisp.html.erb (23.1ms)
Completed 200 OK in 1137ms (Views: 948.5ms | ActiveRecord: 170.5ms)
Started GET "/assets/favicons/favicon-359aff8efc45853aea627316f2ab1c7eb0242d855d0e5049152a699ac872092d.ico" for 127.0.0.1 at 2019-07-04 18:38:32 -0300
Started GET "/manifest.json" for 127.0.0.1 at 2019-07-04 18:38:32 -0300
Started GET "/assets/favicons/favicon-359aff8efc45853aea627316f2ab1c7eb0242d855d0e5049152a699ac872092d.ico" for 127.0.0.1 at 2019-07-04 18:38:32 -0300
Started GET "/manifest.json" for 127.0.0.1 at 2019-07-04 18:38:32 -0300
Started GET "/serviceworker.js" for 127.0.0.1 at 2019-07-04 18:38:34 -0300
Any tips are welcome. Thanks in advance.
So, by inspecting the Networking tab in my Browser's dev tools as suggested in the comments, I found out the problem was the Service Worker. It was duplicating the requests when the fetch event was fired. For anyone interested, here's the code that was duplicating the requests:
self.addEventListener('fetch', function(event) {
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
return;
}
event.respondWith(
checkResponse(event.request).catch(function() {
return returnFromCache(event.request)}
));
event.waitUntil(addToCache(event.request));
});
var checkResponse = function(request){
return new Promise(function(fulfill, reject) {
fetch(request).then(function(response){
if(response.status !== 404) {
fulfill(response)
} else {
reject()
}
}, reject)
});
};
var addToCache = function(request){
return caches.open('touts-offline').then(function (cache) {
return fetch(request).then(function (response) {
return cache.put(request, response);
});
});
};
var returnFromCache = function(request){
return caches.open('touts-offline').then(function (cache) {
return cache.match(request).then(function (matching) {
if(!matching || matching.status == 404) {
return cache.match('offline.html')
} else {
return matching
}
});
});
};
And here's the proper way of doing it.

How can I use a scope in a join query?

I want to use a scope of a joined table.
The goal is to write a scope for autors that have reports with a specific stat_id (for example 15)
Rails 5.2.3
class Author < ApplicationRecord
belongs_to :report
class Report < ApplicationRecord
has_many :authors
scope :with_stat, ->(s) {
where(stat_id: s)
}
This works fine:
Autor.joins(:report).where(reports: {stat_id: 15})
If the scope is more complex. How can I use the scope from class Report?
This doesn't work:
Autor.joins(:report).where(reports: {with_stat(15)})
What is the correct syntax?
That scope will not give you the correct query.
What you want is Author.joins(:report).where(reports: { stat_id: 1 }). Which gives a single query:
Author Load (1.0ms) SELECT "authors".* FROM "authors" INNER JOIN "reports" ON "reports"."id" = "authors"."report_id" WHERE "reports"."stat_id" = $1 LIMIT $2
This is what happens if you use the scope instead:
irb(main):004:0> Author.joins(:report).where(Report.with_stat(1))
Report Load (1.6ms) SELECT "reports".* FROM "reports" WHERE "reports"."stat_id" = $1 [["stat_id", 1]]
Author Load (0.6ms) SELECT "authors".* FROM "authors" INNER JOIN "reports" ON "reports"."id" = "authors"."report_id" LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
irb(main):005:0> Author.joins(:report).where(report: Report.with_stat(1))
Author Load (2.1ms) SELECT "authors".* FROM "authors" INNER JOIN "reports" ON "reports"."id" = "authors"."report_id" WHERE "authors"."report_id" IN (SELECT "reports"."id" FROM "reports" WHERE "reports"."stat_id" = $1) LIMIT $2 [["stat_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
The later uses a subquery which should give the same result but should be less effective.
What you can do is place the scope on the other side of the association:
class Author < ApplicationRecord
belongs_to :report
scope :with_stat, ->(s){
joins(:report).where(reports: {stat_id: s})
}
end
irb(main):010:0> Author.joins(:report).where(reports: { stat_id: 1 })
Author Load (1.1ms) SELECT "authors".* FROM "authors" INNER JOIN "reports" ON "reports"."id" = "authors"."report_id" WHERE "reports"."stat_id" = $1 LIMIT $2 [["stat_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Relation []>

Rails where clause is not working for some of the values

I am working on Easy Redmine PMS and I have written where query in rails Like:
Tracker.where(name: "+ Permits Required")
but it returns less values whereas when I have tried detect query for getting same data Like:
Tracker.all.detect{|i| i.name == "+ Permits Required"
And it is returning all the values. Also I noticed that when fetching the data directly in database then I get the value as Request Available whereas rails console returns Permits Required. I have tried this issue with postgresql and mysql both the databases and the issue is same. Here are the queries:
2.1.1 :001 > #tra = Tracker.find_by_name("+ Camps & Finished Food Store")
Tracker Load (32.8ms) SELECT "trackers".* FROM "trackers" WHERE "trackers"."name" = $1 LIMIT 1 [["name", "+ Camps & Finished Food Store"]]
=> nil
2.1.1 :002 > #tra = Tracker.all.detect{|i| i.name == ("+ Camps & Finished Food Store")}
Tracker Load (0.9ms) SELECT "trackers".* FROM "trackers"
AnonymousUser Load (27.4ms) SELECT "users".* FROM "users" WHERE "users"."type" IN ('AnonymousUser') ORDER BY "users"."id" ASC LIMIT 1
EasyTranslation Load (28.6ms) SELECT "easy_translations".* FROM "easy_translations" WHERE "easy_translations"."entity_id" = $1 AND "easy_translations"."entity_type" = $2 AND "easy_translations"."entity_column" = $3 AND "easy_translations"."lang" = $4 ORDER BY "easy_translations"."id" ASC LIMIT 1 [["entity_id", 3], ["entity_type", "Tracker"], ["entity_column", "name"], ["lang", "en"]]
EasyTranslation Load (1.5ms) SELECT "easy_translations".* FROM "easy_translations" WHERE "easy_translations"."entity_id" = $1 AND "easy_translations"."entity_type" = $2 AND "easy_translations"."entity_column" = $3 AND "easy_translations"."lang" = $4 ORDER BY "easy_translations"."id" ASC LIMIT 1 [["entity_id", 4], ["entity_type", "Tracker"], ["entity_column", "name"], ["lang", "en"]]
EasyTranslation Load (0.7ms) SELECT "easy_translations".* FROM "easy_translations" WHERE "easy_translations"."entity_id" = $1 AND "easy_translations"."entity_type" = $2 AND "easy_translations"."entity_column" = $3 AND "easy_translations"."lang" = $4 ORDER BY "easy_translations"."id" ASC LIMIT 1 [["entity_id", 5], ["entity_type", "Tracker"], ["entity_column", "name"], ["lang", "en"]]
2.1.1 :003 > #tra = Tracker.where(name: "+ Camps & Finished Food Store")
Tracker Load (0.8ms) SELECT "trackers".* FROM "trackers" WHERE "trackers"."name" = $1 [["name", "+ Camps & Finished Food Store"]]
Tracker.where(name: "Permits Required")

Spree currency convertor is not working for AED

I have included following gem in my gem file
gem 'spree_multi_currency', github: 'spree/spree_multi_currency', branch: '2-3-stable'
in my application following currencies are already present:
SGD,USD,EUR,AUD,GBP,PHP,THB,MYR
and these are converting price properly. But my requirement is to add AED currency to so I have added that also SGD,USD,EUR,AUD,GBP,PHP,THB,MYR,AED from backend
Now I automatically got this option in my header now when i click on AED it gives me following error
Started GET "/assets/world-globe.png" for 127.0.0.1 at 2016-01-26 10:26:08 +0100
Started POST "/currency/set" for 127.0.0.1 at 2016-01-26 10:26:11
+0100 Processing by Spree::CurrencyController#set as JSON Parameters: {"currency"=>"AED"} Spree::Country Load (1.1ms) SELECT "spree_countries".* FROM "spree_countries" WHERE "spree_countries"."name" = 'N/A' LIMIT 1 Spree::User Load (1.2ms) SELECT "spree_users".* FROM "spree_users" WHERE "spree_users"."id" = 1 ORDER BY "spree_users"."id" ASC LIMIT 1 Spree::Order Load (1.2ms) SELECT "spree_orders".* FROM "spree_orders" WHERE "spree_orders"."completed_at" IS NULL AND "spree_orders"."currency" = 'USD' AND "spree_orders"."guest_token" = 'ZtR5IUlQUC40ueZBlo21Pg' AND "spree_orders"."user_id" = 1 LIMIT 1 Spree::Adjustment Load (1.4ms) SELECT "spree_adjustments".* FROM "spree_adjustments" WHERE "spree_adjustments"."adjustable_type" = 'Spree::Order' AND "spree_adjustments"."adjustable_id" IN (6084) ORDER BY spree_adjustments.created_at ASC Spree::Order Load (0.9ms) SELECT "spree_orders".* FROM "spree_orders" WHERE "spree_orders"."user_id" = $1 AND "spree_orders"."completed_at" IS NULL AND (id != 6084) [["user_id", 1]] (0.9ms) BEGIN Spree::Order Exists (1.7ms) SELECT 1 AS one FROM "spree_orders" WHERE ("spree_orders"."number" = 'R501407003' AND "spree_orders"."id" != 6084) LIMIT 1 Spree::LineItem Load (1.3ms) SELECT "spree_line_items".* FROM "spree_line_items" WHERE "spree_line_items"."order_id" = $1 AND (currency != 'AED') ORDER BY created_at ASC [["order_id", 6084]] Spree::Variant Load (1.3ms) SELECT "spree_variants".* FROM "spree_variants" WHERE "spree_variants"."id" = $1 LIMIT 1 [["id", 2]] Spree::Price Load (1.3ms) SELECT "spree_prices".* FROM "spree_prices" WHERE "spree_prices"."deleted_at" IS NULL AND "spree_prices"."variant_id" = $1 AND "spree_prices"."currency" = 'AED' ORDER BY "spree_prices"."id" ASC LIMIT 1 [["variant_id", 2]] Spree::Product Load (1.3ms) SELECT "spree_products".* FROM "spree_products" WHERE "spree_products"."id" = $1 LIMIT 1 [["id", 2]] Spree::Product::Translation Load (1.3ms) SELECT "spree_product_translations".* FROM "spree_product_translations" WHERE "spree_product_translations"."spree_product_id" = $1 [["spree_product_id", 2]] (1.5ms) ROLLBACK Completed 500 Internal Server Error in 56ms
RuntimeError - no AED price found for 28 Day Ultimate Teatox (28 Day): () Users/TopFormInvestment/.rvm/gems/ruby-2.1.4#skinnymint/bundler/gems/spree-cfe7e96539b6/core/app/models/spree/order/currency_updater.rb:34:in `update_line_item_price!' () Users/TopFormInvestment/.rvm/gems/ruby-2.1.4#skinnymint/bundler/gems/spree-cfe7e96539b6/core/app/models/spree/order/currency_updater.rb:18:in `block in update_line_item_currencies!'
Please guide me how to solve this error. As I am new in spree
Go to the Products in admin panel. There you can see the price tab, click the tab and then you change the prices there itself.
You did not set an AED price for the variant 28 Day Ultimate Teatox. In update_line_items_price! it has a local variable price which returns the variants price in the newly set currency. If the price isn't present it will raise the RuntimeError you are getting.
def update_line_item_currencies!
line_items.where('currency != ?', currency).each do |line_item|
update_line_item_price!(line_item)
end
end
# Returns the price object from given item
def price_from_line_item(line_item)
line_item.variant.prices.where(currency: currency).first
end
# Updates price from given line item
def update_line_item_price!(line_item)
price = price_from_line_item(line_item)
if price
line_item.update_attributes!(currency: price.currency, price: price.amount)
else
raise RuntimeError, "no #{currency} price found for #{line_item.product.name} (#{line_item.variant.sku})"
end
end

Rails infinite loop while updating other record's value during `before_save`

I have this model in Rails (trimmed to the relevant parts)
class Session < ActiveRecord::Base
belongs_to :user
before_save :invalidate_existing_sessions
def invalidate_existing_sessions
Session.where(user_id: user.id, current: true).each { |sess| sess.update_attributes(current: false) }
end
end
However, when a record is created and about to be saved, the server goes into an infinite loop.
Here are the server logs
Processing by V1::SessionsController#create as */*
Parameters: {"email"=>"user#example.com", "password"=>"[FILTERED]", "session"=>{}}
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT 1 [["email", "user#example.com"]]
(0.2ms) BEGIN
Session Load (0.7ms) SELECT "sessions".* FROM "sessions" WHERE "sessions"."user_id" = $1 AND "sessions"."current" = $2 [["user_id", 1
], ["current", true]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
CACHE (0.0ms) SELECT "sessions".* FROM "sessions" WHERE "sessions"."user_id" = $1 AND "sessions"."current" = $2 [["user_id", 1], ["cu
rrent", true]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
CACHE (0.0ms) SELECT "sessions".* FROM "sessions" WHERE "sessions"."user_id" = $1 AND "sessions"."current" = $2 [["user_id", 1], ["cu
rrent", true]]
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
CACHE (0.0ms) SELECT "sessions".* FROM "sessions" WHERE "sessions"."user_id" = $1 AND "sessions"."current" = $2 [["user_id", 1], ["cu
rrent", true]]
A bit later, this is what the log turns into
app/models/session.rb:12:in `invalidate_existing_sessions'
app/models/session.rb:12:in `block in invalidate_existing_sessions'
app/models/session.rb:12:in `invalidate_existing_sessions'
app/models/session.rb:12:in `block in invalidate_existing_sessions'
app/models/session.rb:12:in `invalidate_existing_sessions'
app/models/session.rb:12:in `block in invalidate_existing_sessions'
app/models/session.rb:12:in `invalidate_existing_sessions'
Any ideas? I'm using Rails 5 alpha.
It's because your before_save method does this...
sess.update_attributes(current: false)
Since update_attributes calls before_save you are (as you say) in an infinite loop.
So you need to skip the callbacks
class Session < ActiveRecord::Base
attr_accessor :skip_callbacks
before_save :invalidate_existing_sessions, unless: :skip_callbacks
def invalidate_existing_sessions
Session.where(user_id: user.id, current: true).each do |sess|
sess.skip_callbacks = true
sess.update_attributes(current: false)
end
end
Even though all of the above answers worked for me, this is what I found simplest and I ended up using.
def invalidate_existing_sessions
Session.where(user_id: user.id, current: true).each { |sess| sess.update_column(:current, false) }
end
Turns out update_column doesn't call any callbacks, but as an disadvantage it doesn't update updated_at if you're using timestamps in your model.
You're running update_attributes in before_save, that means you're saving before save. That's why it goes into an infinite loop.

Resources