usage of query where in controller - ruby-on-rails

I want to display events of specific season and championship, but I have all the events of a championship. Where I am not right?
resources :championships do
resources :seasons do
resources :events
end
end
class EventsController < ApplicationController
def index
#events = #championship.events.where(params[:season_id] == #season.id)
end
end
Started GET "/championships/2/seasons/2/events" for 127.0.0.1 at 2016-12-28 12:07:07 +0200
Processing by EventsController#index as HTML
Parameters: {"championship_id"=>"2", "season_id"=>"2"}
Championship Load (0.3ms) SELECT "championships".* FROM "championships" WHERE "championships"."id" = $1 LIMIT 1 [["id", 2]]
Season Load (0.3ms) SELECT "seasons".* FROM "seasons" WHERE "seasons"."id" = $1 LIMIT 1 [["id", 2]]

Here is your error:
#events = #championship.events.where(params[:season_id] == #season.id)
You give where either true or false
but you need to do this:
#events = #championship.events.where(:season_id => params[:season_id])

Related

Rails PUT/POST request fails on first try, works on next

I have a VueJS Frontend application with a Ruby On Rails API backend. I need to change information about a user in the database.
I have a PUT/POST request that removes the creditcard information. The basic rails code is below. The issue I am having is that I get the, very popular, error:
NoMethodError (undefined method []' for nil:NilClass):
But the catch is that it only happens on the first attempt. Subsequent attemps are successful. Can someone point me in the right direction here? Should you need additional informaiton, please don't hesitate to ask.
class AccountsController < ApplicationController
before_action :load_account, only: [:show, :update, :destroy, :formats, :charges, :destroy_card]
def destroy_card
#account.card_last_4 = nil
#account.card_type = nil
#account.card_exp_month = nil
#account.card_exp_year = nil
#account.plan_id = 1
#account.save!
render json: { account: #account }, status: 200
end
private
def load_account
if #current_user.admin?
#account = Account.find(params[:id])
else
#account = #current_user.accounts.find_by_id(params[:id]) unless #account.present?
end
end
end
Errors:
Started PUT "//accounts/20/destroy_card" for ::1 at 2020-08-17 20:24:47 -0400
Processing by AccountsController#destroy_card as */*
Parameters: {"id"=>20, "account_id"=>"20"}
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 10 LIMIT 1
↳ app/controllers/application_controller.rb:18:in `load_user'
AccountUser Load (0.2ms) SELECT `account_users`.* FROM `account_users` WHERE `account_users`.`user_id` = 10 AND `account_users`.`account_id` = 20 LIMIT 1
↳ app/controllers/application_controller.rb:28:in `load_user'
Account Load (0.1ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`id` = 20 LIMIT 1
↳ app/controllers/application_controller.rb:30:in `load_user'
CACHE Account Load (0.0ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`id` = 20 LIMIT 1 [["id", 20], ["LIMIT", 1]]
↳ app/controllers/accounts_controller.rb:97:in `load_account'
(0.1ms) BEGIN
↳ app/models/account.rb:148:in `assign_plan'
Plan Load (0.1ms) SELECT `plans`.* FROM `plans` WHERE `plans`.`id` = 1 LIMIT 1
↳ app/models/account.rb:148:in `assign_plan'
Account Update (0.2ms) UPDATE `accounts` SET `accounts`.`card_last_4` = NULL, `accounts`.`card_type` = NULL, `accounts`.`card_exp_month` = NULL, `accounts`.`card_exp_year` = NULL, `accounts`.`plan_id` = 1, `accounts`.`updated_at` = '2020-08-18 00:24:47' WHERE `accounts`.`id` = 20
↳ app/controllers/accounts_controller.rb:82:in `destroy_card'
Account Update (0.3ms) UPDATE `accounts` SET `accounts`.`plan_updated_at` = '2020-08-18 00:24:47' WHERE `accounts`.`id` = 20
↳ app/models/account.rb:60:in `stripe_subscribe'
(9.1ms) ROLLBACK
↳ app/controllers/accounts_controller.rb:82:in `destroy_card'
Completed 500 Internal Server Error in 1150ms (ActiveRecord: 10.4ms | Allocations: 22601)
NoMethodError (undefined method `[]' for nil:NilClass):
app/models/account.rb:71:in `stripe_subscribe'
app/controllers/accounts_controller.rb:82:in `destroy_card'
Code from account.rb
def stripe_subscribe
s = stripe_customer.subscriptions.first
self.plan_updated_at = Time.now
save!
if coupon.present? && plan.id != coupon.plan_id
self.coupon = nil
save!
end
if plan.price == 0
# Downgrading from a paid tier
if s.present?
meta = s.delete
self.changed_plan = Plan.find(previous_changes[:plan_id][0])
self.changed_plan_date = Time.at(meta['current_period_end']).to_datetime
save!
end
else
s.delete({ prorate: true, invoice_now: true }) if s.present?
params = { items: [{ plan: plan.short_name }] }
if coupon.present?
params[:coupon] = coupon.token
end
stripe_customer.subscriptions.create(params)
if Rails.env == 'production'
client = Slack::Web::Client.new
client.chat_postMessage(channel: '#somechannel', text: "some random message", as_user: true)
end
end
end

How to avoid destroy deleting earlier related object

If the user already has a favorite model after log in, then I want the products that were added to their anonymous bookmarks to be overwritten in the favorite that is attached to their user object.
I find session_favorite (anonymous favorite), #favorite, which belongs to the user. I am rewriting line_item.favorite with the ID of session_favorite to the user's ID favorite. I reset the session and destroy that anonymous favorite, which in theory has become empty, since now line_items has the user's favorite ID.
But, for some reason, when destroying a session_favorite, line_items are also deleted. Why?
module CurrentFavorite
def set_favorite
if user_signed_in?
set_user_favorite
else
if session[:favorite]
#favorite = Favorite.find(session[:favorite])
else
#favorite = Favorite.create
session[:favorite] = #favorite.id
end
end
end
def set_user_favorite
if session[:favorite]
if current_user.favorite.nil?
#favorite = Favorite.find(session[:favorite])
session[:favorite] = nil
#favorite.update(user: current_user)
else
#block with error
session_favorite = Favorite.find(session[:favorite])
#favorite = Favorite.find_by(user: current_user)
session_favorite.line_items.each do |line_item|
line_item.update(favorite: #favorite)
end
session[:favorite] = nil
session_favorite.destroy
#favorite
end
else
if current_user.favorite.nil?
#favorite = current_user.build_favorite
#favorite.save
else
#favorite = Favorite.find_by(user: current_user)
end
end
end
end
LineItem Update (0.7ms) UPDATE "line_items" SET "favorite_id" = ?, "updated_at" = ? WHERE "line_items"."id" = ? [["favorite_id", 1], ["updated_at", "2020-06-07 05:19:54.653064"], ["id", 13]]
↳ app/models/concerns/current_favorite.rb:31
(12.9ms) commit transaction
↳ app/models/concerns/current_favorite.rb:31
(0.0ms) begin transaction
↳ app/models/concerns/current_favorite.rb:31
Movie Load (0.1ms) SELECT "movies".* FROM "movies" WHERE "movies"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
↳ app/models/concerns/current_favorite.rb:31
LineItem Update (0.7ms) UPDATE "line_items" SET "favorite_id" = ?, "updated_at" = ? WHERE "line_items"."id" = ? [["favorite_id", 1], ["updated_at", "2020-06-07 05:19:54.674229"], ["id", 14]]
↳ app/models/concerns/current_favorite.rb:31
(3.6ms) commit transaction
↳ app/models/concerns/current_favorite.rb:31
(0.0ms) begin transaction
↳ app/models/concerns/current_favorite.rb:34
LineItem Destroy (0.5ms) DELETE FROM "line_items" WHERE "line_items"."id" = ? [["id", 13]]
↳ app/models/concerns/current_favorite.rb:34
LineItem Destroy (0.1ms) DELETE FROM "line_items" WHERE "line_items"."id" = ? [["id", 14]]
↳ app/models/concerns/current_favorite.rb:34
Favorite Destroy (0.2ms) DELETE FROM "favorites" WHERE "favorites"."id" = ? [["id", 10]]
↳ app/models/concerns/current_favorite.rb:34
session_favorite still has the set of line_items associated with it. For performance reasons, Rails does not automatically reload the line_items before destroying the session_favorite so it doesn't recognize that the line_items are now associated with a different favorite.
To prevent the line_items being destroyed, refresh the association before doing the destroy:
session_favorite.line_items.reload
session_favorite.destroy

N+1 in has_many :through

I ran into problem N + 1
in association :
class Category < ApplicationRecord
has_many :categories_designs, dependent: :destroy
has_many :designs, through: :categories_designs
has_many :templates, ->{ where(is_template: true) }, through: :categories_designs, class_name: 'Design', source: :design
def marked_designs_as_new?
designs.select(:mark_design_as_new_until).where("mark_design_as_new_until >= ?", Time.now.in_time_zone.beginning_of_day).exists?
end
end
And I want to use the marked_designs_as_new? method in the view.
- #categories.each do |category|
= category.title.titleize
- if category.marked_designs_as_new?
.design-type-marked
NEW
In my controller I call:
#categories = Category.includes(categories_designs: :design).visible
And I'm faced with the problem of N + 1.
Category Load (0.4ms) SELECT "categories".* FROM "categories" WHERE "categories"."hidden" = $1 ORDER BY "categories"."position" ASC LIMIT $2 OFFSET $3 [["hidden", false], ["LIMIT", 100], ["OFFSET", 0]]
CategoriesDesign Load (0.4ms) SELECT "categories_designs".* FROM "categories_designs" WHERE "categories_designs"."category_id" IN (1, 3, 4, 5, 6, 7, 8)
Design Load (0.5ms) SELECT "designs".* FROM "designs" WHERE "designs"."id" IN (1, 4, 3, 6)
(0.7ms) SELECT COUNT(*) FROM "designs" INNER JOIN "categories_designs" ON "designs"."id" = "categories_designs"."design_id" WHERE "categories_designs"."category_id" = $1 AND "designs"."is_template" = $2 [["category_id", 1], ["is_template", true]]
Design Exists (0.7ms) SELECT 1 AS one FROM "designs" INNER JOIN "categories_designs" ON "designs"."id" = "categories_designs"."design_id" WHERE "categories_designs"."category_id" = $1 AND (mark_design_as_new_until >= '2018-03-13 00:00:00') LIMIT $2 [["category_id", 1], ["LIMIT", 1]]
(0.5ms) SELECT COUNT(*) FROM "designs" INNER JOIN "categories_designs" ON "designs"."id" = "categories_designs"."design_id" WHERE "categories_designs"."category_id" = $1 AND "designs"."is_template" = $2 [["category_id", 3], ["is_template", true]]
............. etc.
why?
Ok, your .select(:mark_design_as_new_until) performs another query to the database. What you should do is use an array select method in the following way:
.select(&:mark_design_as_new_until)
This gives you an array of designs loaded in the memory on which you can perform .any? method to check your condition:
.select(&:mark_design_as_new_until).any? { |design| design.mark_design_as_new_until >= Time.now.in_time_zone.beginning_of_day }
And of course, include designs in your Category.
Category.includes(:designs, ...)
Did you try Category.includes([:categories_designs, :design]) Also, you can change the marked_designs_as_new? method as follows,
def marked_designs_as_new?
designs.select{ |x| x.marked_designs_as_new? }.any?
end
design.rb
class Design
def marked_designs_as_new?
mark_design_as_new_until >= Time.now.in_time_zone.beginning_of_day
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.

Rails association skip update associated object

class City<ActiveRecord::Base
has_one :template, class_name:'TmplLocation'
after_initialize :_init
private
def _init
self.template = TmplLocation.find(18) if !self.template
end
end
And that's what happens in console:
>Loc.first.template
City Load (29.8ms) SELECT `locations`.* FROM `locations` WHERE `locations`.`type` IN ('City') LIMIT 1
TmplLocation Load (0.2ms) SELECT `locations`.* FROM `locations` WHERE `locations`.`type` IN ('TmplLocation') AND `locations`.`location_id` = 23 LIMIT 1
TmplLocation Load (34.8ms) SELECT `locations`.* FROM `locations` WHERE `locations`.`type` IN ('TmplLocation') AND `locations`.`id` = ? LIMIT 1 [["id", 18]]
SQL (0.2ms) BEGIN
(0.7ms) UPDATE `locations` SET `location_id` = 23, `updated_at` = '2013-06-11 10:47:11' WHERE `locations`.`type` IN ('TmplLocation') AND `locations`.`id` = 18
(41.4ms) COMMIT
You see? It updates the TmplLocation so now it is constantly associated with this exact city.
I want only use the TmplLocation instance in this City
How to skip update stage??
You can try something like this
class City<ActiveRecord::Base
has_one :template, class_name:'TmplLocation', :conditions => { :id => 18 }
end
For more options see this
guides.rubyonrails.org

Resources