I have two callbacks that aren't working in my model. They don't raises any error messages either.
The first callback is: after_update :state_closed and I want to use this to close the ticket when I select ticket state from the view 'solved' or 'canceled'. So, I want it in this case is to be closed
The second callback is after_create :assign_state and I want to use this to say the ticket is assigned or its not assigned so if the employee_id is blank thats mean the ticket is not assigned to any employee yet. If employee_id is not black so this ticket is assigned
Here is my ticket.rb
class Ticket < ActiveRecord::Base
before_save :default_values
after_update :state_closed
after_create :assign_state
attr_accessible :description, :title, :employee_department_id, :user_id, :first_name,
:last_name , :email, :state_id, :employee_id, :ticket_state, :assign_state
belongs_to :employee_department
belongs_to :user
belongs_to :state
belongs_to :employee
has_many :replies
def default_values
self.state_id = 3 if self.state_id.nil?
end
def to_label
ticket_state.to_s
end
def state_closed
if self.ticket_state == "Solved" || self.ticket_state == "Canceled"
self.ticket_state = "Closed"
self.save
end
end
def assign_state
if self.employee_id.nil?
self.assign_state = "Un-assigned"
else
self.assign_state = "Assigned"
end
end
Ticket.all.each do |ticket|
if ticket.ticket_state.blank?
ticket.ticket_state = 'open'
end
ticket.save
end
...
This is from server logs when i choose "solved" for example it is updated to "solved" if my callbacks are working then in this case it should change from solved to closed but that's not happening
Started PUT "/tickets/1" for 127.0.0.1 at 2013-09-14 21:46:54 +0200
Processing by TicketsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"LZRTSjq9EWqgG6ub3xpd7fioWNtY1SSzy5XQA8ZNep0=", "ticket"=>{"ticket_state"=>"solved"}, "id"=>"1"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
State Load (0.1ms) SELECT "states".* FROM "states"
Ticket Load (0.1ms) SELECT "tickets".* FROM "tickets" WHERE "tickets"."id" = ? LIMIT 1 [["id", "1"]]
(0.0ms) begin transaction
(0.2ms) UPDATE "tickets" SET "ticket_state" = 'solved', "updated_at" = '2013-09-14 19:46:54.926307' WHERE "tickets"."id" = 1
(95.2ms) commit transaction
Redirected to http://localhost:3000/tickets
Completed 302 Found in 100ms (ActiveRecord: 96.0ms)
The issues with each are as follows:
state_closed is not being called because of capitalization issue: "solved" does not equal "Solved". Change the capitalizations to match, or compare the strings when they're both downcased.
assign_state is probably being called, but not persisting because you never actually save the model once it's changed. Try saving after you update on create.
Related
Why does EVERY single belongs_to association need to be selected first just to call update!? This seems a bit ridiculous, and I don't remember this being like that before, maybe I was using a deprecated method or something.
I know there is update_attribute which doesn't have this problem, but I want to update multiple attributes at once using a bang method.
I have some records with 5 associations, and just to update one or two columns it automatically does...
SELECT * FROM a
SELECT * FROM b
SELECT * FROM c
SELECT * FROM d
SELECT * FROM e
UPDATE f
I also am not using any validations at all, nor validates_associated
Model:
class Lead < ApplicationRecord
belongs_to :organization
belongs_to :vendor
belongs_to :prospect
belongs_to :visit
belongs_to :session
has_one :result
enum status: [
'PENDING',
'COMPLETE',
]
end
and
lead = Lead.first
lead.update!(status: 1)
[8] pry(main)> s.update!(slug: 'x')
TRANSACTION (0.2ms) BEGIN
Campaign Load (0.5ms) SELECT "campaigns".* FROM "campaigns" WHERE "campaigns"."id" = $1 LIMIT $2 [["id", "76dfe777-f563-43b1-900b-a2c40ea7d072"], ["LIMIT", 1]]
Sequence Update (0.6ms) UPDATE "sequences" SET "slug" = $1, "updated_at" = $2 WHERE "sequences"."id" = $3 [["slug", "x"], ["updated_at", "2023-01-27 01:49:32.472637"], ["id", "19c41977-71c6-4c60-a6b6-7af6ce090d80"]]
TRANSACTION (0.7ms) COMMIT => true
All columns look like this:
t.references :prospect, null: false, foreign_key: true, type: :uuid
belongs_to associations are required by default, which automatically adds presence validation.
:required
When set to true, the association will also have its
presence validated. This will validate the association itself, not the
id. You can use :inverse_of to avoid an extra query during validation.
NOTE: required is set to true by default and is deprecated. If you
don't want to have association presence validated, use optional: true.
https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to
I don't know what :inverse_of is supposed to do in this situation, doesn't seem to do anything.
Let's say you have this model:
class Post < ApplicationRecord
belongs_to :user
end
belongs_to adds a presence validator:
>> Post.validators
=> [#<ActiveRecord::Validations::PresenceValidator:0x00007f1fa2e96c40 #attributes=[:user], #options={:message=>:required}>]
In short, it does this to validate user: post.user.blank?, which loads the association.
You can set association as optional:
class Post < ApplicationRecord
belongs_to :user, optional: true
end
>> Post.validators
=> []
and add your own validations if you want:
class Post < ApplicationRecord
belongs_to :user, optional: true
validates :user, presence: true, on: :create
validates :user_id, presence: true, on: :update
end
Which only sort of works and breaks in some situations.
It will probably be best to handle it outside of the model with a custom validator and just skip model validations.
To update multiple attributes:
post = Post.first
post.assign_attributes(title: "name")
post.save!(validate: false)
There is also a config:
https://guides.rubyonrails.org/configuring.html#config-active-record-belongs-to-required-by-default
I have four models in Sport, Gold, Silver, and Bronze with a one to one relationship between sport and the other three. Every instance of sport must have an instance of Gold, Silver and Bronze; and three are to be created using a callback function in the Sports model. These callback function is one throwing up the error that i have been able to trace as the rails error stack produced is just a single line.
My Code
MODELS
Sport
class Sport < ActiveRecord::Base
validates :sportname, presence: true,
uniqueness: { case_sensitive: false }
has_one :gold, inverse_of: :sport, :dependent => :destroy
has_one :silver, inverse_of: :sport, :dependent => :destroy
has_one :bronze, inverse_of: :sport, :dependent => :destroy
accepts_nested_attributes_for :gold
accepts_nested_attributes_for :silver
accepts_nested_attributes_for :bronze
after_validation :build_default_medals, on: :create
def build_default_medals
self.build_gold
self.build_silver
self.build_bronze
end
end
Gold
class Gold < ActiveRecord::Base
belongs_to :sport #, inverse_of: :gold
validates_associated :sport, presence: true
belongs_to :team, inverse_of: :golds, counter_cache: true
validates_associated :team, :if => :create, allow_nil: true
accepts_nested_attributes_for :team
accepts_nested_attributes_for :sport
end
Silver
class Silver < ActiveRecord::Base
belongs_to :sport #, inverse_of: :silver
validates_associated :sport, presence: true
belongs_to :team, inverse_of: :silvers, counter_cache: true
validates_associated :team, :if => :create, allow_nil: true
accepts_nested_attributes_for :team
accepts_nested_attributes_for :sport
end
Bronze
class Bronze < ActiveRecord::Base
belongs_to :sport #, inverse_of: :bronze
validates_associated :sport, presence: true
belongs_to :team, inverse_of: :bronzes, counter_cache: true
validates_associated :team, :if => :create, allow_nil: true
accepts_nested_attributes_for :team
accepts_nested_attributes_for :sport
end
The callback after_validation :build_default_medals, on: :create is where the error is, producing this in my log
Started POST "/admin/sports" for 127.0.0.1 at 2016-07-14 14:52:22 -0400
Processing by Admin::SportsController#create as HTML
Parameters:{"utf8"=>"√",authenticity_token"=>"jss1O4bSJd3hcqxuSpu/KAxaowB7d
g5pLZw55oGDf1M=", "sport"=>{"sportname"=>"boxing"}, "commit"=>"Create Sport"}
User Load (1.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 (0.0ms) BEGIN
Sport Exists (1.0ms) SELECT 1 AS one FROM `sports` WHERE `sports`.`sportname` = 'boxing' LIMIT 1
SQL (1.0ms) INSERT INTO `sports` (`created_at`, `sportname`, `updated_at`) VALUES ('2016-07-14 18:52:22', 'boxing', '2016-07-14 18:52:22')
SQL (15.1ms) UPDATE `sports` SET `created_at` = '2016-07-14 18:52:22', `id` = 13, `sportname` = 'boxing', `updated_at` = '2016-07-14 18:52:22' WHERE `sports`.`id` = 13
SQL (1.0ms) INSERT INTO `golds` (`created_at`, `sport_id`, `updated_at`) VALUES ('2016-07-14 18:52:22', 13, '2016-07-14 18:52:22')
SQL (1.0ms) UPDATE `sports` SET `created_at` = '2016-07-14 18:52:22', `id` =13, `sportname` = 'boxing', `updated_at` = '2016-07-14 18:52:22' WHERE `sports`.`id` = 13
SQL (1.0ms) INSERT INTO `silvers` (`created_at`, `sport_id`, `updated_at`) VALUES ('2016-07-14 18:52:22', 13, '2016-07-14 18:52:22')
SQL (1.0ms) UPDATE `sports` SET `created_at` = '2016-07-14 18:52:22', `id` =13, `sportname` = 'boxing', `updated_at` = '2016-07-14 18:52:22' WHERE `sports`.`id` = 13
SQL (1.0ms) INSERT INTO `bronzes` (`created_at`, `sport_id`, `updated_at`) VALUES ('2016-07-14 18:52:22', 13, '2016-07-14 18:52:22') (72.2ms)
ROLLBACK
Completed 500 Internal Server Error in 213ms
SystemStackError (stack level too deep): actionpack (4.1.8) lib/action_dispatch/middleware/reloader.rb:79
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8/lib/action_dispatch/middleware/templates/rescues/_source.erb (2.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (4.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (7.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (145.1ms)
I am curious as to why the callback triggers update after inserting and what exactly is triggering the ROLLBACK and infinite loop for Stack Level Too Deep. Any insight is appreciated. Thanks.
EDIT
Using after_create :build_default_medals in place of after_validation :build_default_medals, on: :creation produces
Started POST "/admin/sports" for 127.0.0.1 at 2016-07-14 16:19:24 -0400
Processing by Admin::SportsController#create as HTML
Parameters: {"utf8"=>"√","authenticity_token"=>"3LrwB6+nD9PJ9EwxEgIGhN3rVHP3UPLOHUz9MXWRJ4Y=", "sport"=>{"sportname"=>"dancing"}, "commit"=>"Create Sport"}
User Load (1.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 (0.0ms) BEGIN
Sport Exists (1.0ms) SELECT 1 AS one FROM `sports` WHERE `sports`.`sportname` = 'dancing' LIMIT 1
SQL (1.0ms) INSERT INTO `sports` (`created_at`, `sportname`,`updated_at`) VALUES ('2016-07-14 20:19:24', 'dancing', '2016-07-14 20:19:24')
Gold Load (2.0ms) SELECT `golds`.* FROM `golds` WHERE `golds`.`sport_id` =8 LIMIT 1
Silver Load (1.0ms) SELECT `silvers`.* FROM `silvers` WHERE `silvers`.`sport_id` = 8 LIMIT 1
Bronze Load (1.0ms) SELECT `bronzes`.* FROM `bronzes` WHERE `bronzes`.`sport_id` = 8 LIMIT 1
(45.5ms) COMMIT
EDIT FOR undefined method 'create' for #<Gold:0x63bf820>
def build_default_medals
#Gold.create(sport: self, team: nil)
#Silver.create(sport: self, team: nil)
#Bronze.create(sport: self, team: nil)
self.create_gold(team: nil)
self.create_silver(team: nil)
self.create_bronze(team: nil)
end
replace
after_validation :build_default_medals, on: :create
with
after_create :build_default_medals
You are trying to build associated models while sport is still a new record and not a persisted one. validates_associated :sport, presence: true causes a circular dependency and will give error.
I reproduced and tested with this change. It will work fine.
For saved object, use self.create_gold instead of self.build_gold, like this
def build_default_medals
self.create_gold
self.create_silver
self.create_bronze
end
Also remove accepts_nested_attributes_for :sport from Gold, Silver and Bronze model. Refer - https://github.com/rails/rails/issues/7809
I have 2 models: User and Collective, along with their join table, Membership. When a user creates a new collective, I want to simultaneously create a new membership between the two. Right now I have my create function partially working, in that the collective 'new' form adds a collective record to my db but no membership record is added.
User Model
class User < ActiveRecord::Base
has_many :collectives, :through => :memberships
has_many :memberships
end
Collective Model
class Collective < ActiveRecord::Base
has_many :users, :through => :memberships
has_many :memberships
end
Membership Model
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :collective
validates :user_id, presence: true
validates :collective_id, presence: true
end
Collective Controller
def new
#collective = Collective.new if user_signed_in?
end
def create
#collective = Collective.new(collective_params)
if #collective.save
current_user.memberships.create(collective_id: #collective)
flash[:success] = "Collective created!"
redirect_to collective_url(#collective)
else
render 'new'
end
end
private
def collective_params
params.require(:collective).permit(:name, :description, :location, :num_of_users, :num_of_projects)
end
end
Log
Started POST "/collectives" for 127.0.0.1 at 2014-11-06 10:49:39 -0500
Processing by CollectivesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Dh/rG1N6ulrJSIiEaAgudnaltjxnKwCw5sdUQxG9qnE=", "collective"=>{"name"=>"Honda Civic", "location"=>"Cars", "description"=>"Blaahhhhh"}, "commit"=>"Create Collective"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "collectives" ("created_at", "description", "location", "name", "num_of_projects", "num_of_users", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["created_at", "2014-11-06 15:49:40.003338"], ["description", "Blaahhhhh"], ["location", "Cars"], ["name", "Honda Civic"], ["num_of_projects", 0], ["num_of_users", 1], ["updated_at", "2014-11-06 15:49:40.003338"]]
(145.3ms) commit transaction
(0.0ms) begin transaction
(0.1ms) commit transaction
Redirected to http://localhost:3000/collectives/7
Completed 302 Found in 208ms (ActiveRecord: 146.7ms)
It seems like the line current_user.memberships.create(collective_id: #collective) is just being ignored for some reason given that the redirect happens fine. Help would be much appreciated. Thanks in advance!
I would say the problem is with passing instance object instead of id. It should be:
if #collective.save
current_user.memberships.create(collective_id: #collective.id)
flash[:success] = "Collective created!"
redirect_to collective_url(#collective)
else
render 'new'
end
Since create method only returns true/false (false in this case because of validation) and does not raise any exception, it gets redirected into show action.
Unable to update state attribute of the Product model using accepts_nested_attributes. This is what JSON I post looks like:
{"auth_token"=>"x", "product"=>{"caption"=>"x","state_attributes"=>{"id"=>"1"}}, "id"=>"x"}
All I need to do is to change referenced state. Thoughts?
State Model
class State < ActiveRecord::Base
has_many :products
end
Product Model
class Product < ActiveRecord::Base
attr_accessible :state, :state_attributes
belongs_to :state
accepts_nested_attributes_for :state, :allow_destroy => false
end
Products Controller Update Action
def update
product = Product.find_by_id( params[:id])
if product && product.update_attributes(params[:product])
respond_with(product, status: :updated, location: product)
else
respond_with(product.errors, status: :unprocessable_entity)
end
end
Transaction Log
Started PUT "/products/1170.json" for 127.0.0.1 at 2014-01-13 16:21:52 -0700
Processing by ProductsController#update as JSON
Parameters: {"auth_token"=>"x", "product"=>{"caption"=>"x", "state_attributes"=>{"id"=>"3"}}}, "id"=>"1170"}
WARNING: Can't verify CSRF token authenticity
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."authentication_token" = 'x' LIMIT 1
(0.1ms) BEGIN
(0.3ms) UPDATE "users" SET "last_sign_in_at" = '2014-01-13 23:20:46.614453', "current_sign_in_at" = '2014-01-13 23:21:52.251028', "sign_in_count" = 954, "updated_at" = '2014-01-13 23:21:52.251753' WHERE "users"."id" = 536
(6.5ms) COMMIT
Product Load (0.5ms) SELECT "products".* FROM "products" WHERE "products"."user_id" = 536 AND "products"."id" = 1170 LIMIT 1
(0.1ms) BEGIN
State Load (0.2ms) SELECT "states".* FROM "states" WHERE "states"."id" = 2 LIMIT 1
(0.5ms) ROLLBACK
Completed 404 Not Found in 15ms
ActiveRecord::RecordNotFound (Couldn't find State with ID=3 for Product with ID=1170):
app/controllers/products_controller.rb:81:in `update'
Rendered /Users/me/.rvm/gems/ruby-2.0.0-p353/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
Rendered /Users/me/.rvm/gems/ruby-2.0.0-p353/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.8ms)
Rendered /Users/me/.rvm/gems/ruby-2.0.0-p353/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (5.8ms)
As #Sergio Aristizábal pointed out, I was trying to reset associated state instead of simply updating the id
You are trying to reset the id of the associated State. I guess you just want to associate a state to product, so you should just set the state_id attribute.
With that, I updated Product model
class Product < ActiveRecord::Base
attr_accessible :state_id, :state_attributes
belongs_to :state
end
Passed state id in the json
{"auth_token"=>"x", "product"=>{"caption"=>"x","state_id"=>"1"}, "id"=>"x"}
and everything worked like a charm. Thanks #Sergio
I am trying to create a relation_level, with a score attribute, for each interest_question/feed pair. I am doing this with an accepts_nested_attributes_for :relation_levels in the feed form. Everything renders as it should, and when the form is submitted, a feed is created, but no relation_levels are created.
I've also tried adding the feed_id as a hidden field in the form.
(using rails 4 and haml)
app/models/interest_question.rb
class InterestQuestion < ActiveRecord::Base
has_many :relation_levels, dependent: :destroy
has_many :interest_answers, dependent: :destroy
end
app/models/relation_level.rb
class RelationLevel < ActiveRecord::Base
belongs_to :feed
belongs_to :interest_question
end
app/models/feed.rb
class Feed < ActiveRecord::Base
has_many :relation_levels, dependent: :destroy
has_many :interest_questions, through: :relation_levels
accepts_nested_attributes_for :relation_levels
end
app/views/feeds/_form.html.haml
=form_for(#feed) do |f|
...other fields
...other fields
-#interest_questions.each do |iq|
=f.fields_for #feed.relation_levels.build(interest_question_id: iq.id) do |rl|
=rl.label iq.question_text
=rl.range_field :score, max: 100, min: 0, default: 0
=f.submit
app/controllers/feeds_controller.rb
class FeedsController < ApplicationController
def new
#feed = Feed.new
#sources = Source.all
#interest_questions = InterestQuestion.all
end
def create
#feed = Feed.new(feed_params)
if #feed.save
redirect_to '/feeds', notice: 'Feed created.'
else
render action: 'new'
end
end
...
private
def feed_params
params.require(:feed).permit(..., relation_levels_attributes:
[:interest_question_id, :score, :feed_id])
end
Server output:
Started POST "/feeds" for 127.0.0.1 at 2013-12-30 15:00:58 -0600
Processing by FeedsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"/+TOOdpZZk85YvVlxkRIpLNfPfVVtGUTlKPb9Ctkvh8=", "feed"=>{"url"=>"lkas6df.com", "source_id"=>"1", "section"=>"kasl6d6fa", "area_importance"=>"", "is_local_news"=>"0", "relation_level"=>{"score"=>"17"}}, "commit"=>"Create Feed"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '6fcabf7c7b1376250b1ffa589ff4f2279854d066' LIMIT 1
Unpermitted parameters: relation_level
(0.1ms) begin transaction
Source Load (0.1ms) SELECT "sources".* FROM "sources" WHERE "sources"."id" = ? ORDER BY "sources"."id" ASC LIMIT 1 [["id", 1]]
Feed Exists (0.2ms) SELECT 1 AS one FROM "feeds" WHERE "feeds"."url" = 'lkas6df.com' LIMIT 1
SQL (1.7ms) INSERT INTO "feeds" ("created_at", "section", "source_id", "updated_at", "url") VALUES (?, ?, ?, ?, ?) [["created_at", Mon, 30 Dec 2013 21:00:58 UTC +00:00], ["section", "kasl6d6fa"], ["source_id", 1], ["updated_at", Mon, 30 Dec 2013 21:00:58 UTC +00:00], ["url", "lkas6df.com"]]
(170.8ms) commit transaction
Redirected to http://localhost:3000/feeds
I've been stuck on this for a while, thanks in advance for any help :)
This should be a comment, but it's too big:
Maybe this could be your issue?
=f.fields_for #feed.relation_levels.build(interest_question_id: iq.id) do |rl|
This builds an ActiveRecord object on the fly, which IMO is bad practice. I'd build the object in the controller, and then call the object in the f.fields_for, like this:
#app/controllers/feeds_controller.rb
def new
#feed = Feed.new
#sources = Source.all
#interest_questions = InterestQuestion.all
#interest_questions.count.times do
#feed.relation_levels.build
end
end
You can then call:
=f.fields_for :relation_levels do |rl|
Much cleaner, and will likely help with your debugging!