.save puts NULL in id field in Rails - ruby-on-rails

Here's the model file:
class ProfileTag < ActiveRecord::Base
def self.create_or_update(options = {})
id = options.delete(:id)
record = find_by_id(id) || new
record.id = id
record.attributes = options
puts "record.profile_id is"
puts record.profile_id
record.save!
record
end
end
This gives me the correct print out in my log. But it also says that there's a call to UPDATE that sets profile_id to NULL. Here's some of the output in the log file:
Processing ProfilesController#update (for 127.0.0.1 at 2010-05-28 18:20:54) [PUT]
Parameters: {"commit"=>"Save", "profile"=>{"id"=>"2", "password_confirmation"=>"", "username"=>"user2", "first_name"=>"user2_first", "password"=>"", "last_name"=>"user2_last"}, "authenticity_token"=>"...", "tag"=>"1", "id"=>"2"}
?[4;36;1mProfileTag Create (0.0ms)?[0m ?[0;1mINSERT INTO `profile_tags`
(`reputation_value`, `updated_at`, `tag_id`, `id`, `profile_id`, `created_at`) VALUES(0, '2010-05-29 01:20:54', 1, NULL, 4, '2010-05-29 01:20:54')?[0m
?[4;35;1mSQL (2.0ms)?[0m ?[0mCOMMIT?[0m
?[4;36;1mSQL (0.0ms)?[0m ?[0;1mBEGIN?[0m
?[4;35;1mSQL (0.0ms)?[0m ?[0mCOMMIT?[0m
?[4;36;1mProfileTag Load (0.0ms)?[0m ?[0;1mSELECT * FROM `profile_tags` WHERE (`profile_tags`.profile_id = 4) ?[0m
?[4;35;1mSQL (1.0ms)?[0m ?[0mBEGIN?[0m
?[4;36;1mProfileTag Update (0.0ms)?[0m ?[0;1mUPDATE `profile_tags` SET profile_id = NULL WHERE (profile_id = 4 AND id IN (35)) ?[0m
I'm not sure I understand why the INSERT puts the value into profile_id properly, but then it sets it to NULL on an UPDATE.
[Edit]
In ProfileController:
def update
#...stuff. Set tags array.
save_tags(tags) #These tags are correct. Verified by printouts before and after this call.
respond_to do |format|
if #profile.update_attributes(params[:profile])
flash[:notice] = 'Profile was successfully updated.'
#format.html { redirect_to(#profile) }
format.html { redirect_to :action=>'show' }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #profile.errors, :status => :unprocessable_entity }
end
end
end
def save_tags(tags)
profile = find_profile #finds the correct profile. And I confirm that it exists with a printout
tags.each do |t|
ProfileTags.create_or_update(:profile_id => profile.profile_id, :tag_id => t.id)
end
end
If you need more specifics, please let me know. I'm thinking that the save functionality does many things other than INSERTs into the database, but I don't know what I need to specify so that it will properly set profile_id.

Look at the line:
ProfileTags.create_or_update(:profile_id => profile.profile_id, :tag_id => t.id)
I believe you want to pass profile.id, and not profile.profile_id (which is probably null).

save! itself should't do this.
Maybe your problem is the name of the method. ActiveRecord::Base already have a method named create_or_update (see http://github.com/rails/rails/blob/2-3-stable/activerecord/lib/active_record/base.rb#L2913) which is called by save! - maybe replacing it causes this weird problem.
Try changing the name of your method to something else, it might help.

You aren't passing the id attribute to the create_or_update method in the first place, so you don't need to call it, just call create instead, like so:
def save_tags(tags)
profile = find_profile #finds the correct profile. And I confirm that it exists with a printout
tags.each do |t|
ProfileTag.create(:profile_id => profile.profile_id, :tag_id => t.id)
end
end

Related

Couldn't update nested attributes in Rails 4.2.6

I migrated my Rails from 3.2 to Rails 4.2.6. I am having 2 tables where report :has_many => icons. I added strong parameters for report and icon_attributes. The create functionality is working fine and when coming to update functionality, I am able to update reports but couldn't update icons, instead new icon is created every time it hits update action.
This is my code:
report.rb:
class Report < ActiveRecord::Base
has_many :icons, -> { order 'position_id ASC'}
accepts_nested_attributes_for :icons, :reject_if => lambda { |a| a[:icon].blank? }, :allow_destroy => true
end
icon.rb:
class Icon < ActiveRecord::Base
belongs_to :report
end
reports_controller:
def update
respond_to do |format|
if #report.update_attributes(report_params)
#report.save
format.html { redirect_to(user_reports_url, :notice => 'Report was successfully updated.') }
format.json { render :json => { :success => true, :report_id => #report.id, :report_title => #report.title, :icon_array => #report.icons, :redirect => report_url(#report.id) } }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #report.errors, :status => :unprocessable_entity }
end
end
end
private
def report_params
params.require(:report).permit(:title, :comments, :remarks,{:icons_attributes => [:id, :icon, :rotation, :top, :_destroy]})
end
I have seen the log by putting puts in the controller, the icons are inserting at #report.update_attributes(report_params) step and this is the log:
Processing by ReportsController#update as JSON Parameters:
{"utf8"=>"✓", "report"=>{"title"=>"title1", "comments"=>"This is a comment",
"icons_attributes"=>{"0"=>{"id"=>"", "icon"=>"market_indicator",
"rotation"=>"0", "top"=>"", "_destroy"=>"false"}, "id"=>"87"}
Report Load (0.3ms) SELECT "reports".* FROM "reports" WHERE
"reports"."deleted_at" IS NULL AND "reports"."id" = ? LIMIT 1 [["id",
87]]
SQL (1.6ms) INSERT INTO "icons" ("icon", "rotation", "top")
VALUES (?, ?, ?) [["icon", "market"], ["rotation", "0"], ["top", ""],
["left", ""]] (12.0ms) commit transaction
ActiveRecord::Associations::CollectionProxy
I have put log as:
def update
puts #report.icons.inspect
respond_to do |format|
.....
end
it resulted as:
Icon Load (0.9ms) SELECT "icons".* FROM "icons" WHERE "icons"."report_id" = ? ORDER BY position_id ASC [["report_id", 91]]
<ActiveRecord::Associations::CollectionProxy [#<Icon id: 204, report_id: 91, icon: "asking_price", rotation: "", top: "150", left: "165">]>
Your "icon_attributes" is not passing the id of the icon along.
"icons_attributes"=>{"0"=>{"id"=>"", "icon"=>"market_indicator", "rotation"=>"0", "top"=>"", "_destroy"=>"false"}, "id"=>"87"}
You'll notice the id is blank. Since the id is blank rails thinks it is a new record and thus creates a new icon. The error lies in how you have made your form.

Update value with first_or_create in rails

I have a table 'Likes' with columns business_id, user_id and liked(0,1) and a function 'change_like_status'.
Now on every function call, If the value is 1 then set it to 0 (or vice versa) and if record doesn't exists then create one with value 1.
The first_or_create method is working just fine but how can i toggle value of column 'liked' while using this method?
Here is my function:
def change_like_status
if current_user.present?
status = Like.where("business_id = ? AND user_id = ?",params['id'],current_user.id).first_or_create(:business_id => params['id'],:user_id => current_user.id,:liked => '1')
abort status.inspect
else
return render :json => {:status => false,:msg=>"You need to sign in before performing this action."}
end
end
In you controller, make the changes
def change_like_status
if current_user
status = Like.create_or_change_status(params[:id], current_user.id)
else
return render json: { status: false, msg: "You need to sign in before performing this action." }
end
end
In your model like.rb file, add a method
def self.create_or_change_status(business_id, user_id)
status = where(business_id: business_id, user_id: user_id).first
if status.nil?
status = create({business_id: business_id, user_id: user_id, liked: 1})
else
status.update_attributes(liked: !status.liked)
end
status
end
def change_like_status
if current_user
current_user.likes.find_by(business_id: params[:id]).switch_status!
else
return render json: { status: false, msg: "You need to sign in before performing this action." }
end
end
class Like
def switch_status!
self.update_column :liked, !liked
end
end
other approach should be something like that
class Like
def switch_status!
self.update_column :liked, !liked
end
end
class User
def likes id
likes_for_business id
end
def likes_for_business(id)
likes.find_by(business_id: id) || likes.create(:business_id: id, liked: true)
end
end
# controller
current_user.likes(params[:id]).switch_status!

Can't delete a embedded element with Mongoid

Still working on my Rails/MongoDB app, I have yet another problem.
This time, I can create embedded documents, but can't delete them, though I've been doing what has been said in another Stackoverflow topic (http://stackoverflow.com/questions/3693842/remove-an-embedded-document-in-mongoid)
Here goes my controller :
class FeedSubscribtionsController < ApplicationController
has_to_be_connected
def create
if session[:user_id] != params[:id]
#self = current_user
attributes = { :user => #self, :userId => params[:id], :feedId => params[:feed] }
subscribtion = FeedSubscribtion.create attributes
success = subscribtion.save
render json: { :success => success, :feed => params[:feed] }
end
end
def destroy
success = false
if session[:user_id] != params[:id]
#self = current_user
uid, fid = params[:id], params[:feed]
#feed = #self.feed_subscribtions.where :userId => uid, :feedId => fid
if #feed.count > 0
#self.feed_subscribtions.delete #feed.first.id.to_s
success = #feed.first.save
end
end
render json: { :success => success, :feed => params[:feed] }
end
end
The weirdest part is that everything seems to go well : success is equal to true in the rendered JSON object.
I also tried to replace "success = #feed.first.save" with "#self.save" : in that case, it returns false, but with no further explanations.
(I do know that for the logic behind this controller to be perfect, I should loop on the #feed array, and I will once it starts working ^^ it's just easier to debug that way)
So, is there any way I may find out why #ßelf.save fails, or why #feed.first.save doesn't fail but doesn't actually save either ?
Thanks.
Here is what you're doing as I see it
You are using FeedSubscriptionsController to delete the object with id if #feed.first, then you try and save #feed.first, but #feed.first points to an already deleted object, so it's failing to save it.

How can I update another Model using a Callback?

I'm working with a Model called Recover. Prior to creating the model I would like to save the boolean attribute, Combo.occupied = true using the Recover.combo_id attribute as a reference.
It appears my SQL is executing the query properly, but it is not saving this attribute. How can I save Combo.occupied = true?
recover.rb:
before_create :checkin
protected
def checkin
x = Combo.find_by_id(combo_id).occupied =
true
end
Rails Console:
Started POST "/recovers" for 127.0.0.1
at 2011-01-06 17:07:24 -0800
Processing by
RecoversController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"o1Iu3Y9/rVBOZPoDUgVP/tRfQ8GxbdWC40DbPq9YxUE=",
"recover"=>{"combo_id"=>"4",
"email"=>"jz#marin.edu"},
"commit"=>"Create Recover"} Recover
Load (0.2ms) SELECT "recovers"."id"
FROM "recovers" WHERE
("recovers"."email" =
'justin.zollars#marin.edu') LIMIT 1
Recover Load (0.1ms) SELECT
"recovers"."id" FROM "recovers" WHERE
("recovers"."combo_id" = 4) LIMIT 1
Combo Load (0.5ms) SELECT "combos".*
FROM "combos" WHERE ("combos"."id" =
4) LIMIT 1 AREL (0.5ms) INSERT INTO
"recovers" ("locker_number", "email",
"requests", "created_at",
"updated_at", "combo_id") VALUES
(NULL, 'justin.zollars#marin.edu',
NULL, '2011-01-07 01:07:24.287072',
'2011-01-07 01:07:24.287072', 4)
Redirected to
http://localhost:3000/recovers/14
Completed 302 Found in 119ms
RecoversController#create
def create
#recover = Recover.new(params[:recover])
respond_to do |format|
if #recover.save
format.html { redirect_to(#recover, :notice =>
'Recover was successfully created.') }
format.xml { render :xml => #recover, :status => :created,
:location => #recover }
else
format.html { render :action => "new" }
format.xml { render :xml => #recover.errors, :status =>
:unprocessable_entity }
end
end
end
You need to call save for the new value to be written to the database:
def checkin
combo = Combo.find_by_id(combo_id)
combo.occupied = true
combo.save!
end
This is easier if you use update_attribute. Also, if you have a belongs_to relationship, you can dispense with the find:
belongs_to :combo
def checkin
if combo # true unless combo_id is nil
combo.update_attribute(:occupied,true)
end
end
Note that update_attribute bypasses validation. If you need to validate, use update_attributes(:occupied=>true) instead.

How can I include a model association in a JSON response in Rails?

I've looked at similar posts but can't seem to quite figure it out.
I have the following function which works just fine. The Listing model has a foreign key called price_id which maps to the Price model and its price_range column. Price_id is returned as part of the message object in the JSON response.
How can I return the corresponding price_range value from the association instead of the price_id value (as part of the message obj, and keep the other attributes)?
def update
#listing = Listing.find(params[:listing][:id])
#if params were passed in for updating
if #listing.update_attributes(params[:listing])
#should we return the whole thing or just what's needed?
json_response = {
"success" => #listing.save, #save to DB and assign true/false based on success...
"message" => #listing.attributes #USE attributes to show output the content of the #message obj, and not another object called "message"
}
respond_to do |format|
#json response
format.html { render:json => json_response }
format.xml { render :xml => #listing }
#normal response. Consider leaving this for now?
#format.html { render :action => "detail" } #refresh this page, with new data in it. Consider trying to use redirect instead?
#format.xml { head :ok }
end
end #end if
end
add a method in your Listing model with the price_range and call it in serializable_hash
class Listing
def price_range
price.price_range
end
end
Like explain on comment you can use delegate instead this method :
class Listing
delegate :prince_range, :to => price
end
In you controller you can now do :
json_response = {
"success" => #listing.save, #save to DB and assign true/false based on success...
"message" => #listing.serializable_hash(:methods => [:price_range])
}
Based on what I read in this article, you should be able to do this:
class Listing
def as_json
super(:include => :price)
end
end
Then in your controller:
json_response = {
"success" => #listing.save,
"message" => #listing.as_json
}
If I understand correctly, you want to add #listing.price.price_range value to the "message" ?
If so, try this:
"message" => #listing.attributes[:price_range] = #listing.price.price_range

Resources