I have a class as follows:
class Player < ActiveRecord::Base
attr_accessor :name, :rating, :team_name
end
So I ran 'bundle exec rails console' and then
Player.create(:name => "Ben", :rating => 5, :team_name => "Brown")
However, this is what I get back:
(0.1ms) begin transaction
SQL (7.0ms) INSERT INTO "players" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Sat, 29 Mar 2014 04:24:31 UTC +00:00], ["updated_at", Sat, 29 Mar 2014 04:24:31 UTC +00:00]]
(0.9ms) commit transaction
=> #<Player id: 1, name: nil, rating: nil, team_name: nil, created_at: "2014-03-29 04:24:31", updated_at: "2014-03-29 04:24:31">
Why does the object that's created not have the properties I've assigned it? This strikes me as really strange...
Any guidance on this one?
Thanks,
Mariogs
Player.create(:name => "Ben", :rating => 5, :team_name => "Brown")
Here you are mass assigning attributes name, rating and team_name
So you need to tell Player model that above attributes can be mass assigned.
to allows mass assignment, you have to pass those attributes to attr_accessible method.
attr_accessor is used for creating getter and setter methods.
And getter and setter methods for database attributes are dynamically created, you don't need to create it.
so change
attr_accessor :name, :rating, :team_name
to
attr_accessible :name, :rating, :team_name
Related
I'm trying to model a system where each user may have many emails (at least one).
Following good normalization rules I created two migrations (some fields removed for brevity):
create_table :users do |t|
end
create_table :user_emails do |t|
t.integer :user_id, null: false
t.string :email, null: false
end
add_index :user_emails, :email, :unique => true
add_foreign_key :user_emails, :users, dependent: :delete
and the following rails models:
class User < ActiveRecord::Base
has_many :emails, class_name: 'UserEmail', dependent: :destroy
def self.find_by_email(email)
UserEmail.find_by(email: email).try(:user)
end
validate do
if emails.count < 1
errors.add(:emails, "is empty")
end
end
end
class UserEmail < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id, :email
validates_uniqueness_of :email
end
Now I am not able to create any of those. User cannot be created since it requires at least an UserEmail. At the same time, UserEmail cannot be created beforehand since it requires an user_id.
I believe I have tried any combination of #user.emails.build and #user.emails << e that I can think of.
How can I solve this really simple problem without renouncing to data consistency (one is saved and the other is not)?
P.s.: I tought that maybe relaxing the validations and using transactions may solve the problem consistently. However I've never used transactions in rails, so any help is really appreciated.
Thanks
validates_presence_of :user_id only check for any valid integer which might not be a valid user.
But if you use validates_presence_of :user and validates_presence_of :emails, they will check the user and emails association are valid and not blank.
Also, when you are trying to create the user and the associated email, you are able to do the following code on application level.
user = User.new(name: 'user name')
user.emails.build(email_address: 'email address') #build(email: '') for your case.
Then, you can save to database with
user.save!
Below is the code I tested with.
class User < ActiveRecord::Base
has_many :emails, class_name: 'UserEmail', dependent: :destroy
validates_presence_of :emails, :message => 'User should have at least one email address.'
end
class UserEmail < ActiveRecord::Base
belongs_to :user
validates_presence_of :user
validates_presence_of :email_address
end
[40] pry(main)> u = User.create!(name: 'doh')
(0.1ms) begin transaction
(0.0ms) rollback transaction
ActiveRecord::RecordInvalid: Validation failed: Emails User should have at least one email address.
[1] pry(main)> u = User.new(name: 'nice')
=> #<User id: nil, name: "nice", created_at: nil, updated_at: nil>
[2] pry(main)> u.emails.build(email_address: 'hello#world.blah')
=> #<UserEmail id: nil, user_id: nil, email_address: "hello#world.blah", created_at: nil, updated_at: nil>
[3] pry(main)> u.save!
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "users" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2014-07-26 10:14:52.184245"], ["name", "nice"], ["updated_at", "2014-07-26 10:14:52.184245"]]
SQL (0.2ms) INSERT INTO "user_emails" ("created_at", "email_address", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["created_at", "2014-07-26 10:14:52.189757"], ["email_address", "hello#world.blah"], ["updated_at", "2014-07-26 10:14:52.189757"], ["user_id", 5]]
(0.7ms) commit transaction
=> true
[4] pry(main)> u.emails.count
(0.2ms) SELECT COUNT(*) FROM "user_emails" WHERE "user_emails"."user_id" = ? [["user_id", 5]]
=> 1
[5] pry(main)> u.id
=> 5
[6] pry(main)> u.name
=> "nice"
[7] pry(main)> u.emails
=> [#<UserEmail id: 4, user_id: 5, email_address: "hello#world.blah", created_at: "2014-07-26 10:14:52", updated_at: "2014-07-26 10:14:52">]
[8] pry(main)> u
=> #<User id: 5, name: "nice", created_at: "2014-07-26 10:14:52", updated_at: "2014-07-26 10:14:52">
[19] pry(main)> UserEmail.create!(email_address: 'test#test.org')
(0.1ms) begin transaction
(0.1ms) rollback transaction
ActiveRecord::RecordInvalid: Validation failed: User can't be blank
Hope this helps.
I just realized that I adding a NOT NULL constraint on the storage layer is sufficient. I can remove the :user_id presence validation and have the same consistency behavior, with the exception that it now works.
However this looks quite hacky from the rails POW. Any better solution is still really appreciated...
I'm relatively new to Rails (4, Ruby 2.0) and currently have a has_many + belongs_to association that I'm working with. The association that I'm trying to model is a 'Day' having several 'Timeslots' and each Timeslot belonging to a Day.
My models look like this:
Day.rb
class Day < ActiveRecord::Base
has_many :timeslots
belongs_to :calendar
end
Timeslot.rb
class Timeslot < ActiveRecord::Base
has_many :events
belongs_to :day
validates_presence_of :start_time
validates_presence_of :end_time
validates_presence_of :day_id
end
Schemas for Day and Timeslot:
create_table "days", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "timeslots", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.time "start_time"
t.time "end_time"
t.integer "day_id"
end
The issue that I'm seeing is that I can't get a Timeslot object to save to the DB correctly:
In the console:
d = Day.new
=> #<Day id: nil, created_at: nil, updated_at: nil, date: nil>
d.id = 20140322
=> 20140322
d.save
(0.1ms) begin transaction
SQL (22.2ms) INSERT INTO "days" ("created_at", "date", "id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Sat, 22 Mar 2014 21:28:27 PDT -07:00], ["id", 20140322], ["updated_at", Sat, 22 Mar 2014 21:28:27 PDT -07:00]]
(6.8ms) commit transaction
=> true
Day.all
Day Load (0.3ms) SELECT "days".* FROM "days"
=> #<ActiveRecord::Relation [#<Day id: 20140322, created_at: nil, updated_at: nil>]>
t = Timeslot.new
=> #<Timeslot id: nil, created_at: nil, updated_at: nil, start_time: nil, end_time: nil, day_id: nil>
t.start_time = Time.now
2014-03-22 21:28:52 -0700
t.end_time = Time.now
2014-03-22 21:28:57 -0700
t.day_id = 20140322
=> 20140322
t.save
(0.1ms) begin transaction
SQL (0.8ms) INSERT INTO "timeslots" ("created_at", "day_id", "end_time", "start_time", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", Sat, 22 Mar 2014 21:29:06 PDT -07:00], ["day_id", 20140322], ["end_time", 2014-03-22 21:28:57 -0700], ["start_time", 2014-03-22 21:28:52 -0700], ["updated_at", Sat, 22 Mar 2014 21:29:06 PDT -07:00]]
(7.6ms) commit transaction
=> true
# Here's the problem
Day.all
Day Load (0.2ms) SELECT "days".* FROM "days"
=> #<ActiveRecord::Relation [#<Day id: 20140322, created_at: nil, updated_at: nil>]>
2.0.0-p247 :015 > Timeslot.all
Timeslot Load (0.2ms) SELECT "timeslots".* FROM "timeslots"
=> #<ActiveRecord::Relation [#<Timeslot id: 2, created_at: nil, updated_at: nil, start_time: nil, end_time: nil, day_id: 20140322>]>
a = Day.all.first
Day Load (0.2ms) SELECT "days".* FROM "days" ORDER BY "days"."id" ASC LIMIT 1
=> #<Day id: 20140322, created_at: nil, updated_at: nil>
a.timeslots
Timeslot Load (0.3ms) SELECT "timeslots".* FROM "timeslots" WHERE "timeslots"."day_id" = ? [["day_id", 20140322]]
=> #]>
The fact is that the Timeslot objects just don't seem be created properly. I'm not sure why this is happening.
There are several things I don't understand in your files, and I think you should use the ruby command line tools to generate your files to be sure not to do any mistake. For instance, your created_At and updated_at are not normal, they should be filled automatically and they are not.
I suggest you delete your files and launch those commands
rails g model day calendar:references
rails g model timeslot start_time:time end_time:time day:references
Then you add your association to the models
class Day < ActiveRecord::Base
has_many :timeslots
belongs_to :calendar
end
class Timeslot < ActiveRecord::Base
has_many :events
belongs_to :day
validates_presence_of :start_time
validates_presence_of :end_time
# validates_presence_of :day_id #Remove this line for now
end
And you can launch rake db:migrate and try again (by the way Day.all.first is the same as Day.first and instead of t.day_id = ..., try t.day = Day.first
Before saving my Payment model I would like to set an attribute for my other model Post .
My Payment belongs_to :user and my User has has_many :posts and has_many :payments
In my payment.rb I have an before_create :set_expiration_date callback.
How can I set the expiration date of my Post Model?
Is it something like this?
def set_expiration_date
self.User.Post.last.expiration = Date.today + self.duration.days
end
Would the before_create create even work since the payment has not been saved in the database yet and hence the association with the User?
Would it be simple if I care a 1-to-1 association Payment-Post?
I save my payment record the following way:
#Payment.rb
def save_with_payment
if valid?
Stripe::Charge.create(
:amount => 3000,
:currency => "usd",
:card => stripe_card_token,
:description => "Charge for test#example.com")
end
save!
end
The payment model does have a user_id attribute but it seems to always nil
SQL (1.1ms) INSERT INTO "payments" ("amount", "created_at", "transaction_number", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?) [["amount", nil], ["created_at", Sun, 23 Feb 2014 14:50:42 UTC +00:00], ["transaction_number", nil], ["updated_at", Sun, 23 Feb 2014 14:50:42 UTC +00:00], ["user_id", nil]]
Thank you
post = self.user.posts.last
post.expiration = Date.today + self.duration.days
post.save!
I have a User model which has many roles. Roles contains a user_id field, which I want to validate_presence_of
The issue is: if I assign a role to user upon create, the validation fails because no user_id is set. Now, I do want to validate that a user_id exists, but I need to save the user before checking that.
The code currently looks like this:
#user = User.new(params[:user])
#user.roles << Role.new(:name => 'Peon') unless #user.has_roles?
if #user.save
# ...
The only ways I can think of getting around the problem involves either disabling the validation, which I don't want to do, or double-saving to the DB, which isn't exactly efficient.
What's the standard way for handling this issue?
After researching a bit, this solution seems to be easiest. First, in your Role model, instead of validating user_id, validate user:
validates :user, :presence => true
Then, in your User model, add :inverse_of => :user to your has_many call:
has_many :roles, :inverse_of => :user
Then it works as expected:
irb(main):001:0> #user = User.new
=> #<User id: nil, created_at: nil, updated_at: nil>
irb(main):002:0> #user.roles << Role.new(:name => "blah")
=> [#<Role id: nil, user_id: nil, name: "blah", created_at: nil, updated_at: nil>]
irb(main):003:0> #user.roles[0].user
=> #<User id: nil, created_at: nil, updated_at: nil>
irb(main):004:0> #user.save
(0.1ms) begin transaction
SQL (3.3ms) INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Fri, 04 Jan 2013 02:29:33 UTC +00:00], ["updated_at", Fri, 04 Jan 2013 02:29:33 UTC +00:00]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
SQL (0.2ms) INSERT INTO "roles" ("created_at", "name", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["created_at", Fri, 04 Jan 2013 02:29:34 UTC +00:00], ["name", "blah"], ["updated_at", Fri, 04 Jan 2013 02:29:34 UTC +00:00], ["user_id", 3]]
(1.9ms) commit transaction
=> true
irb(main):005:0> #user.roles.first
=> #<Role id: 4, user_id: 3, name: "blah", created_at: "2013-01-04 02:29:34", updated_at: "2013-01-04 02:29:34">
Note, however, that this still produces two SQL transactions, one to save the user and one to save the role. I don't see how you can avoid that.
See also: How can you validate the presence of a belongs to association with Rails?
I think you can get around the validation problem if you change your code to look like this:
#user = User.new(params[:user])
#user.roles.new(:name => 'Peon') unless #user.has_roles?
if #user.save
# ...
If that doesn't work, you could try changing you validation to this:
class Role < ActiveRecord::Base
belongs_to :user
validates :user_id, :presence => true, :unless => Proc.new() {|r| r.user}
end
You must take a look at ActiveRecord's Callbacks. Probably you will use the before_validation to do it.
For anyone Googling for a solution to this problem for a has_many :through association, as of 5/December/2013 the :inverse_of option can't be used in conjunction with :through (source). Instead, you can use the approach suggested by #waldyr.ar. For example, if our models are set up as follows:
class User < ActiveRecord::Base
has_many :roles
has_many :tasks, through: roles
end
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :task
end
class Task < ActiveRecord::Base
has_many :roles
has_many :users, through: roles
end
We can modify our Role class as follows to validate the presence of both task and user before saving
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :task
before_save { validates_presence_of :user, :task }
end
Now if we create a new User and add a couple tasks like so:
>> u = User.new
>> 2.times { u.tasks << Task.new }
Running u.save will save the User and the Task, as well as transparently build and save a new Role whose foreign keys user_id and task_id are set appropriately. Validations will run for all models, and we can go on our merry way!
(I could probably think of a better title for this and open to suggestions)
I am trying to call this in my NotesController, testing it in rails console:
?> u = User.first
User Load (1.6ms) SELECT "users".* FROM "users" LIMIT 1
=> #<User id: 2, email: nil, password: nil, linkedin_url: nil, created_at: "2012-06-17 05:54:44", updated_at: "2012-06-17 05:54:44", liid: "7fB-pQGIQi">
>> c = u.contacts.first
Contact Load (2.0ms) SELECT "contacts".* FROM "contacts" WHERE "contacts"."user_id" = 2 LIMIT 1
=> #<Contact id: 8, user_id: 2, created_at: "2012-06-19 01:23:45", updated_at: "2012-06-19 01:23:45">
>> c.notes.create!(:body => "this is a note")
(0.5ms) BEGIN
SQL (23.3ms) INSERT INTO "notes" ("body", "contact_id", "created_at", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["body", "this is a note"], ["contact_id", 8], ["created_at", Thu, 21 Jun 2012 05:42:21 UTC +00:00], ["updated_at", Thu, 21 Jun 2012 05:42:21 UTC +00:00], ["user_id", nil]]
(1.7ms) COMMIT
=> #<Note id: 4, created_at: "2012-06-21 05:42:21", updated_at: "2012-06-21 05:42:21", body: "this is a note", user_id: nil, contact_id: 8>
The problem is in the last line where it says that the Note created has a "user_id: nil". I'm wondering what I'm missing that is not allowing the note to properly get the user_id from the contact user_id? I can think of a quick fix, to set the user_id on the note object, but it seems as though it could fetch it from the contact_id, am I wrong?
Here are my models in case this is helpful:
class Contact < ActiveRecord::Base
attr_accessible :user_id
belongs_to :user
has_many :notes
end
class User < ActiveRecord::Base
attr_accessible :password, :liid
has_many :contacts
has_many :notes, :through => :contacts
end
class Note < ActiveRecord::Base
attr_accessible :title, :body
belongs_to :contact
belongs_to :user
end
Thanks for the help!
Your Note also needs to belong to your User:
class Note < ActiveRecord::Base
attr_accessible :title, :body
belongs_to :contact
belongs_to :user
end
Rails will only automatically set the foreign_key of the parent object, and not the parent object's parent like you want. So you'll have to set that attribute manually:
c.notes.create!(:body => "this is a note", :user_id => c.user_id)