I'm trying to do the following in my Rails 3 model:
require 'securerandom'
class Contest < ActiveRecord::Base
attr_accessor :key
before_create :generate_key
private
def generate_key
self.key = SecureRandom.hex(3)
end
end
However when I create a Contest, all the fields in my table appear to be correct except the key which remains nil in my DB.
More Info:
In my rails server log, I see the following when I create a contest through my "create contest form"
SQL (0.5ms) INSERT INTO "contests" ("category", "created_at", "description", "key", "price", "status", "time", "title", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["category", "camisas"], ["created_at", Wed, 15 Feb 2012 18:57:16 UTC +00:00], ["description", "test_description"], ["key", nil], ["price", 111], ["status", "In process"], ["time", "2sem"], ["title", "test_contest"], ["updated_at", Wed, 15 Feb 2012 18:57:16 UTC +00:00], ["user_id", 5]]
Note the ["key", nil]
But that should be correct right?, I would guess that the key will be added by the Contest.rb :before_create callback?
Maybe I'm miss-using SecureRandom?
Why are you using attr_accessor? This method is actually going to be defining both a setter and getter method for a key, keeping track of a virtual attribute.
Perhaps you meant to use attr_accessible, but even then that is unnecessary, because you're setting the attribute directly.
Really, you don't need either attr_accessor :key or attr_accessible :key in your model at all if you're just setting it like this. Remove the attr_accessor and it will work.
I think you should write attr_accessible <list accessible params> for example:
attr_accessible :key, :category
Related
I wonder if anyone can help me to understand about the difference between build and create in Has Many Through (HMT) relationship?
From my understanding after reading through stackoverflow and google, create is essentially build + save. However, it seems like create does more than just build + save. It also somehow save into the join table of the HMT relationship as shown below.
I created 3 models: user, wiki, and collaborator.
class User < ActiveRecord::Base
has_many :wikis, through: :collaborators
has_many :collaborators
end
class Wiki < ActiveRecord::Base
has_many :users, through: :collaborators
has_many :collaborators
end
class Collaborator < ActiveRecord::Base
belongs_to :user
belongs_to :wiki
end
Then I tested the build and create behavior in rails console:
$rails c
Loading development environment (Rails 4.0.10)
> user = User.first #create instance user
User Load SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1, name: "Jayzz55">
> (user.wikis.build(title:"hello",body:"world")).save #build and save wiki
SQL INSERT INTO "wikis" ("body", "created_at", "title", "updated_at") VALUES(?,?,?,?) [["body","world"], ["ccreated_at, Sat, 08 Nov 2014 01:39:36 UTC +00:00], ["title","hello"], ["updated_at",Sat, 08 Nov 2014 01:39:36 UTC +00:00]]
=> true
>wiki1 = Wiki.first #create instance wiki called wiki1
=> #<Wiki id:1, title:"hello",body:"world">
>user.wikis
=> #<Wiki id:1, title:"hello",body:"world">
>user.wikis.count
=> 0
>wiki1.users
=> []
>Collaborator.all
=> []
> user.wikis.create(title:"second title",body:"another one")
begin transaction
SQL INSERT INTO "wikis" ("body", "created_at", "title", "updated_at") VALUES (?, ?, ?, ?)[0m [["body", "another one"], ["created_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["title", "second title"], ["updated_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00]]
SQL INSERT INTO "collaborators" ("created_at", "updated_at", "user_id", "wiki_id") VALUES (?, ?, ?, ?) [["created_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["updated_at", Sat, 08 Nov 2014 01:59:39 UTC +00:00], ["user_id", 1], ["wiki_id", 2]]
=> #<Wiki id:2, title:"second title",body:"another one>
>user.wikis
=> [#<Wiki id: 1, title:"hello",body:"world">, #<Wiki id:2, title:"second title",body:"another one>]
>user.wikis.count
=> 1
>wiki2.users
=>#<User id: 1, name: "Jayzz55">
>Collaborator.all
Collaborator Load SELECT "collaborators".* FROM "collaborators"
=> #<Collaborator id:`, user_id:1, wiki_id: 2>
Case wiki1 : build and then save it
The data is not saved into the join table. calling user.wikis does return the object wiki, but running user.wikis.count return 0. Furthermore, running wiki1.users doesn't return the object user. Checking the join table Collaborator returns empty array.
Case wiki2 : create
The data is saved into the join table. calling user.wikis does return the object wiki. Running user.wikis.count return 1. Furthermore, running wiki1.users does return the object user. Checking the join table Collaborator, it does show the relationship clearly mapped.
Seems like Create is not simply just build + new. I'm curious about this behavior and hopefully someone can share their knowledge on this.
I believe that if you had in your first case instead written:
user.wikis.build(title:"hello",body:"world")
user.save
... you would find that ActiveRecord does the "full" save that create also does. The way you've written it, you're asking ActiveRecord to save the newly created Wiki instance, but not the association. So it doesn't create the record in the join table that's required.
Build + Save is equivalent to Create in any relationship
I have such problem. I'm using google_maps_for_rails gem and it is can't find location correctly. I run all instructions from documentation.
Here is my application.erb:
<%= yield :scripts%>
My model:
attr_accessible :address, :latitude, :longtitude
acts_as_gmappable
def gmaps4rails_address
self.address
end
but when I create location I get object with nil longtitude. Here is log:
INSERT INTO "locations" ("address", "created_at", "latitude", "longtitude", "name",
"updated_at") VALUES (?, ?, ?, ?, ?, ?) [["address", "Lviv"], ["created_at", Thu, 07
Feb 2013 10:56:45 UTC +00:00], ["latitude", 49.839683], ["longtitude", nil], ["name",
nil], ["updated_at", Thu, 07 Feb 2013 10:56:45 UTC +00:00]]
Can someone help me ?
See your code:
attr_accessible :address, :latitude, :longtitude
Typo: longtitude
Note that you can personalize the fields according to my doc:
acts_as_gmappable :lat => "latitude", :lng => "longtitude"
I am using Simple_form with Decent_exposure, Strong_parameters I have the following setup and I can post to households but nothing gets posted to neighbors
model
class Household < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :neighbors
accepts_nested_attributes_for :neighbors
view
= simple_nested_form_for household do |f|
= f.input :household_name
= f.simple_fields_for :neighbor, Neighbor.new do |neighbor_form|
= neighbor_form.input :first_name
= neighbor_form.input :middle_name
= neighbor_form.input :last_name
= neighbor_form.input :address
= f.button :submit
Based on the following from my log it looks like the form is working but it is not saving to neighbors the nested model - I have tried both simple_fields_for :neighbor do and simple_fields_for :neighbor, Neighbor.new do hoping that creating a new neighbor would help but it doesn't.
Parameters:{"utf8"=>"✓",
authenticity_token"=>"cVTteqPFa0JMoFi/ys0wAmNIQghubADv5lbPBr6hyq0=",
"household"=> {"household_name"=>"Deew", "neighbor"=>{"first_name"=>"Bill",
"middle_name"=>"", "last_name"=>"Ew", "street"=>"we"}}, "commit"=>"Create Household"}
(0.1ms) begin transactionSQL (0.7ms) INSERT INTO "households" ("created_at",
"household_name", "name", "updated_at") VALUES (?, ?, ?, ?)
[["created_at", Sun, 03 Feb 2013 03:02:56 UTC +00:00], ["household_name", "Deew"],
["name", nil], ["updated_at", Sun, 03 Feb 2013 03:02:56 UTC +00:00]](0.8ms)
commit transaction
Make sure that you include neighbors_attributes in the allowable parameters.
I want a validation to run before a record gets updated. I know of before_update but I pretty much copy and pasted the first codesnippet out of the api docs.
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
My stripped down model looked then like
class User < ActiveRecord::Base
attr_accessible :email
validates :email, :presence => true
before_save(:on => :update) do
puts "******** before_save on => :update ********"
# do something
end
end
if I go into the console and do create a new entry this callback is being executed on a SQL insert call.
irb(main):001:0> User.new(:email => "test#test.com").save
(0.1ms) begin transaction
******** before_save on => :update ********
SQL (29.1ms) INSERT INTO "users" ("created_at", "email", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", Fri, 30 Mar 2012 00:26:33 UTC +00:00], ["email", "test#test.com"], ["first_name", nil], ["last_name", nil], ["updated_at", Fri, 30 Mar 2012 00:26:33 UTC +00:00]]
(433.1ms) commit transaction
=> true
irb(main):002:0>
I would have expected to see this only on an update call. Can anybody sheed some light on this?
[EDIT]
I just changed the callback into a function call with no change in the outcome. The callback is still executed on create.
class User < ActiveRecord::Base
attr_accessible :email
validates :email, :presence => true
before_save :my_before_update, :on => :update
private
def my_before_update
puts "******** before_save on => :update ********"
# do something
end
end
The output is the same.
Loading development environment (Rails 3.2.2)
irb(main):001:0> User.new(:email => "test#test.com").save
(0.1ms) begin transaction
******** before_save on => :update ********
SQL (28.2ms) INSERT INTO "users" ("created_at", "email", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", Fri, 30 Mar 2012 02:28:45 UTC +00:00], ["email", "test#test.com"], ["first_name", nil], ["last_name", nil], ["updated_at", Fri, 30 Mar 2012 02:28:45 UTC +00:00]]
(131.2ms) commit transaction
=> true
The ActiveRecord::Callbacks don't support an :on option...
From the Rails codebase, the only place that mentions handling an :on option is in the validations module code in ActiveModel::Validations.
If you look through the ActiveRecord::Callbacks code, you'll see that there's no mention of :on, nor does the ActiveRecord::Callbacks module include any of the ActiveModel::Validations module that will handle that option. There is an include for ActiveModel::Validations::Callbacks, but that will just provide the definitions for the before_ and after_ validations methods. However, the before_validation and after_validation callbacks will handle the :on option as seen here in their definitions.
I'm pretty sure this is one of those areas that the Rails API has changed across versions. I do recall there being a way to pass :on as an option to before_save, just as I recall when you had to define an after_initialize method (it wasn't available as a callback).
The current way is cleaner and more explicit.
If you do find that the current docs reference before_save(:on => :update), check out the new docrails Github repository where you can fork, change, and commit changes to the docs to be included (no pull requests necessary, or accepted).
After a little more research, looks like you're right, it looks like you can pass :on => :update to before_save
Maybe the issue comes from the block notation, try calling a function like this:
before_save :run_this_before_update, :on => :update
def run_this_before_update
puts "******** before_save on => :update ********"
# do something
end
Looks like a major reason to use this is the order in which Rails runs the callbacks, check out this most excellent article from pivotallabs http://pivotallabs.com/users/danny/blog/articles/1767-activerecord-callbacks-autosave-before-this-and-that-etc-
I initialize an AdUnit object au with
au = AdUnit.new( cp )
where cp is equal to:
{:name=>"second56", :description=>nil, :target_window=>"BLANK", :explicitly_targeted=>false, :ad_unit_sizes_attributes=>[{:height=>90, :width=>728, :is_aspect_ratio=>false, :environment_type=>"BROWSER"}], :dfp_id=>"22319511", :parent_id_dfp=>"22261791"}
and the resulting object au is
#<AdUnit id: nil, dfp_id: "22319511", parent_id_dfp: "22261791", parent_id_bulk: nil, name: "second56", description: nil, target_window: "BLANK", explicitly_targeted: false, created_at: nil, updated_at: nil>
At this point, au.valid? and au.new_record? both return true.
If I do au.save (or au.save! for that matter) the result is nil and nothing is saved to the database. but if I do
aud = au.dup
aud.save
the result is true and the record is saved.
I can save my object with the duplicate workaround, but this looks really weird to me. can anybody give any ideas as to why is this happening? Below the SQL fragments from the 2 save statements from Rails console.
Thanks in advance all Rails gurus.
Returning nil and not saving:
SQL (1.0ms) INSERT INTO "ad_units" ("created_at", "description", "dfp_id", "explicitly_targeted", "name", "parent_id_bulk", "parent_id_dfp", "target_window", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) [["created_at", Wed, 04 Jan 2012 17:28:37 UTC +00:00], ["description", nil], ["dfp_id", "22400631"], ["explicitly_targeted", false], ["name", "zapiFirstLevel366"], ["parent_id_bulk", nil], ["parent_id_dfp", "1166751"], ["target_window", "BLANK"], ["updated_at", Wed, 04 Jan 2012 17:28:37 UTC +00:00]]
=> nil
Returning true and saving:
SQL (0.9ms) INSERT INTO "ad_units" ("created_at", "description", "dfp_id", "explicitly_targeted", "name", "parent_id_bulk", "parent_id_dfp", "target_window", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) [["created_at", Wed, 04 Jan 2012 17:29:58 UTC +00:00], ["description", nil], ["dfp_id", "22400631"], ["explicitly_targeted", false], ["name", "zapiFirstLevel366"], ["parent_id_bulk", nil], ["parent_id_dfp", "1166751"], ["target_window", "BLANK"], ["updated_at", Wed, 04 Jan 2012 17:29:58 UTC +00:00]]
=> true
OK, this was caused by the usage of accepts_nested_attributes_for when AdUnit has a has_and_belongs_to_many relationship with AdUnitSize. So I got to learn that these 2 do not work together nicely.
The only thing is, it might be good to have a warning somewhere, instead of having to find out about it from .save returning nil on a valid? = true object instead of true or false.
Using save! raises an exception if the record is invalid. It doesn't return anything.
If you need a return value use only save.
Basically if save! didn't raise, the record is saved successfuly. If you don't see it, then you're probably loading it wrong.