belongs_to / has_many not behaving as expected in Rails console - ruby-on-rails-3

models:
class Person < ActiveRecord::Base
attr_accessible :email, :first, :last, :uuid, :books
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :person
attr_accessor :blurb, :published, :title, :person
attr_accessible :person
end
I created a Person using the Rails 3.2.8 console like this:
person = Person.create!( {:first => "John", :last => "Doe"} )
and then created a Book
book = Book.create!( {:title => "Ruby for Dummies"} )
I then try to associate them like this:
person.books << book
When I query the person for books, I get an array with the book I created, but when I query the book for the person it belongs to, I get nil. I expected to get the person, given that all information was persisted (I see the SQL commands and I checked the database and the data is correct, i.e. the row in the book table points back to the person id it should.)
What am I missing?
thanks
Edit- Schema:
CREATE TABLE `persons` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`first` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`last` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`uuid` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`email` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
AUTO_INCREMENT=2;
CREATE TABLE `books` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`blurb` TEXT NULL COLLATE 'utf8_unicode_ci',
`published` TINYINT(1) NULL DEFAULT NULL,
`person_id` INT(11) NULL DEFAULT NULL,
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
AUTO_INCREMENT=3;

Assuming your migrations are correct, there is nothing wrong with your code which should produce the following results:
1.9.3p194 :001 > person = Person.create!( {:first => "John", :last => "Doe"} )
(0.1ms) begin transaction
SQL (145.9ms) INSERT INTO "people" ("book_id", "created_at", "email", "first", "last", "updated_at", "uuid") VALUES (?, ?, ?, ?, ?, ?, ?) [["book_id", nil], ["created_at", Mon, 03 Sep 2012 21:35:53 UTC +00:00], ["email", nil], ["first", "John"], ["last", "Doe"], ["updated_at", Mon, 03 Sep 2012 21:35:53 UTC +00:00], ["uuid", nil]]
(43.5ms) commit transaction
=> #<Person id: 1, email: nil, first: "John", last: "Doe", uuid: nil, book_id: nil, created_at: "2012-09-03 21:35:53", updated_at: "2012-09-03 21:35:53">
1.9.3p194 :005 > book = Book.create!( {:title => "Ruby for Dummies"} )
(0.1ms) begin transaction
SQL (1.4ms) INSERT INTO "books" ("blurb", "created_at", "person_id", "published", "title", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["blurb", nil], ["created_at", Mon, 03 Sep 2012 21:36:25 UTC +00:00], ["person_id", nil], ["published", nil], ["title", nil], ["updated_at", Mon, 03 Sep 2012 21:36:25 UTC +00:00]]
(4.4ms) commit transaction
=> #<Book id: 1, blurb: nil, published: nil, title: nil, person_id: nil, created_at: "2012-09-03 21:36:25", updated_at: "2012-09-03 21:36:25">
1.9.3p194 :006 > person.books << book
(0.1ms) begin transaction
(0.3ms) UPDATE "books" SET "person_id" = 1, "updated_at" = '2012-09-03 21:36:39.566387' WHERE "books"."id" = 1
(4.4ms) commit transaction
Book Load (0.3ms) SELECT "books".* FROM "books" WHERE "books"."person_id" = 1
=> [#<Book id: 1, blurb: nil, published: nil, title: nil, person_id: 1, created_at: "2012-09-03 21:36:25", updated_at: "2012-09-03 21:36:39">]
1.9.3p194 :007 > Book.all
Book Load (0.5ms) SELECT "books".* FROM "books"
=> [#<Book id: 1, blurb: nil, published: nil, title: nil, person_id: 1, created_at: "2012-09-03 21:36:25", updated_at: "2012-09-03 21:36:39">]
The problem is with your Book model, try the following:
class Book < ActiveRecord::Base
attr_accessible :person, :title, :person
belongs_to :person
end
Before:
1.9.3p194 :002 > Book.last
Book Load (0.1ms) SELECT "books".* FROM "books" ORDER BY "books"."id" DESC LIMIT 1
=> #<Book id: 4, blurb: nil, published: nil, title: nil, person_id: 1, created_at: "2012-09-03 21:55:33", updated_at: "2012-09-03 21:55:33">
1.9.3p194 :003 > Book.last.person
Book Load (0.2ms) SELECT "books".* FROM "books" ORDER BY "books"."id" DESC LIMIT 1
=> nil
After:
1.9.3p194 :001 > Book.last
Book Load (0.4ms) SELECT "books".* FROM "books" ORDER BY "books"."id" DESC LIMIT 1
=> #<Book id: 4, blurb: nil, published: nil, title: nil, person_id: 1, created_at: "2012-09-03 21:55:33", updated_at: "2012-09-03 21:55:33">
1.9.3p194 :002 > Book.last.person
Book Load (1.0ms) SELECT "books".* FROM "books" ORDER BY "books"."id" DESC LIMIT 1
Person Load (0.1ms) SELECT "people".* FROM "people" WHERE "people"."id" = 1 LIMIT 1
=> #<Person id: 1, email: nil, first: "John", last: "Doe", uuid: nil, created_at: "2012-09-03 21:48:02", updated_at: "2012-09-03 21:48:02">

Have you tried doing something like the following? If so, what does book.person give you?
person = Person.create!( {:first => "John", :last => "Doe"} )
book = Book.create!( {:title => "Ruby for Dummies"} )
book.person_id = person.id
person.books
book.person

Related

Unable to create record via nested attributes Rails API

I'm currently trying to create an Order instance. There is an association of the model Order with Items. The association is as follows. Order has many Items. I try following the documentation https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
class Order < ApplicationRecord
has_many :items
accepts_nested_attributes_for :items
end
class OrdersController < ApplicationController
##
private
def order_params
params.require(:order).permit(:description,
items_attributes: [:id, :quantity])
end
end
From the following post, it shows that the id has to be pass in the params. Rails 5 Api create new object from json with nested resource
params = {order: {description: "this is a test"}, items_attributes: [{id: 3, quantity: 3, description: 'within order -> item'}]}
=> {:order=>{:description=>"this is a test"}, :items_attributes=>[{:id=>3, :quantity=>3, :description=>"within order -> item"}]}
[7] pry(main)> order_test = Order.create!(params[:order])
(0.4ms) BEGIN
Order Create (62.9ms) INSERT INTO "orders" ("description", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["description", "this is a test"], ["created_at", "2019-05-30 23:31:39.409528"], ["updated_at", "2019-05-30 23:31:39.409528"]]
(4.6ms) COMMIT
=> #<Order:0x00007ff91556e4b8 id: 14, description: "this is a test", created_at: Thu, 30 May 2019 23:31:39 UTC +00:00, updated_at: Thu, 30 May 2019 23:31:39 UTC +00:00>
I create an order however when I check for the items it returns an empty array.
=> #<Order:0x00007ff9142da590 id: 14, description: "this is a test", created_at: Thu, 30 May 2019 23:31:39 UTC +00:00, updated_at: Thu, 30 May 2019 23:31:39 UTC +00:00>
[11] pry(main)> Order.last.items
Order Load (0.4ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."id" DESC LIMIT $1 [["LIMIT", 1]]
Item Load (0.3ms) SELECT "items".* FROM "items" WHERE "items"."order_id" = $1 [["order_id", 14]]
=> []
Here is the table for items:
class CreateItems < ActiveRecord::Migration[5.2]
def change
create_table :items do |t|
t.references :order, foreign_key: true
t.integer :quantity
t.string :description
t.timestamps
end
end
end
What is wrong?
The mistake was in the order the parameters were being passed.
{:order=>{:id=>22, :description=>"this is a test", :items_attributes=>[{:order_id=>22, :quantity=>3, :description=>"within order -> item"}]}}
This solved it:
order = Order.create!(params[:order])
<Order:0x00007ff918039ee0 id: 22, description: "this is a test", created_at: Fri, 31 May 2019 01:39:23 UTC +00:00, updated_at: Fri, 31 May 2019 01:39:23 UTC +00:00>
[39] pry(main)> order.items
Item Load (114.8ms) SELECT "items".* FROM "items" WHERE "items"."order_id" = $1 [["order_id", 22]]
=> [#<Item:0x00007ff9180394b8
id: 4,
order_id: 22,
quantity: 3,
description: "within order -> item",
created_at: Fri, 31 May 2019 01:39:23 UTC +00:00,
updated_at: Fri, 31 May 2019 01:39:23 UTC +00:00>]

scope for count children has_many relation

I have two models
# == Schema Information
#
# Table name: answers
#
# id :integer not null, primary key
# content :text
# question_id :integer
# accept :boolean
# created_at :datetime not null
# updated_at :datetime not null
# user_id :integer
#
class Answer < ActiveRecord::Base
attr_accessible :accept, :content, :question_id, :user
belongs_to :question
belongs_to :user
delegate :username, to: :user, allow_nil: true, prefix: 'owner'
end
and Question
# == Schema Information
#
# Table name: questions
#
# id :integer not null, primary key
# title :string(255)
# content :text default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
# user_id :integer
# viewed_count :integer default(0)
#
class Question < ActiveRecord::Base
validates_presence_of :title, :content, :user
attr_accessible :content, :title, :tag_list
acts_as_taggable
belongs_to :user, :counter_cache => true
has_many :answers
delegate :username, to: :user, allow_nil: true, prefix: 'owner'
scope :owner, joins(:user)
scope :without_answer, joins(:answers).
select('questions.id').
group('questions.id').
having('count(answers.id) = 0')
validate :validation_of_tag_list
def self.no_answer
Question.all.select{|question|question.answers.count == 0}
end
The scope without_answer and class method no_answer theoretically should be same. However, I run them in the console as below:
Loading development environment (Rails 3.2.13)
irb(main):001:0> Question.without_answer
Question Load (0.6ms) SELECT questions.id FROM `questions` INNER JOIN `answers` ON `answers`.`question_id` = `questions`.`id` GROUP BY questions.id HAVING count(answers.id) = 0
=> []
irb(main):002:0> Question.no_answer
Question Load (0.6ms) SELECT `questions`.* FROM `questions`
(0.5ms) SELECT COUNT(*) FROM `answers` WHERE `answers`.`question_id` = 1
(0.4ms) SELECT COUNT(*) FROM `answers` WHERE `answers`.`question_id` = 2
(0.4ms) SELECT COUNT(*) FROM `answers` WHERE `answers`.`question_id` = 16
(0.4ms) SELECT COUNT(*) FROM `answers` WHERE `answers`.`question_id` = 17
(0.3ms) SELECT COUNT(*) FROM `answers` WHERE `answers`.`question_id` = 34
=> [#<Question id: 2, title: "Here is the second", content: "here you go\r\n", created_at: "2013-04-20 00:34:15", updated_at: "2013-04-20 00:34:15", user_id: nil, viewed_count: 0>, #<Question id: 16, title: "my question", content: "Here is my question", created_at: "2013-04-21 02:02:47", updated_at: "2013-04-23 02:29:27", user_id: 1, viewed_count: 1>, #<Question id: 17, title: "Hello", content: "me", created_at: "2013-04-23 00:37:56", updated_at: "2013-04-23 00:37:56", user_id: nil, viewed_count: 0>, #<Question id: 34, title: "Question title", content: "question content", created_at: "2013-04-23 04:57:49", updated_at: "2013-04-23 04:57:49", user_id: 42, viewed_count: 0>]
why the scope dose not work as expect?
which way will be better to due with such situation or even better solution?
Your without_answer scope is very close, but needs an outer join like this:
scope :without_answer,
joins('LEFT OUTER JOIN answers ON answers.question_id = questions.id').
select('questions.id').
group('questions.id').
having('count(answers.id) = 0')
Then, you can get the count with length:
Question.without_answer.length
Note: if you want without_answer to be the same as no_answer (i.e. return actual Question objects), you would need to remove the select.
A simpler and faster way to count the unanswered questions is like this:
Question.joins('LEFT OUTER JOIN answers ON answers.question_id = questions.id').
where('answers.id' => nil).count
Also, this will return the same as no_answer as-is, simply use all instead of count.

Validation is passing when it should fail - Michael Hartl's RoR, 6.2.2

I am trying to add validation to my User model, but instead of failing, the data keeps passing. For name, I have no entry. See below:
1.9.3p286 :012 > user = User.new(name: "", email: "fail#fail.com")
=> #<User id: nil, name: "", email: "fail#fail.com", created_at: nil, updated_at: nil>
1.9.3p286 :013 > user.save
(0.1ms) SAVEPOINT active_record_1
SQL (1.5ms) INSERT INTO "users" ("created_at", "email", "name", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Sun, 23 Dec 2012 18:52:00 UTC +00:00], ["email", "fail#fail.com"], ["name", ""], ["updated_at", Sun, 23 Dec 2012 18:52:00 UTC +00:00]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> true
Here is what I have in app/models/user.rb:
class User < ActiveRecord::Base
attr_accessible(:name, :email)
validates(:name, presence: true)
end
This is supposed to fail. Any suggestions? Thank you!
It should work, I think u didn't reload a console after adding a validation

has_many through sql

Well my models are User, Exercise and Writing.
class User < ActiveRecord::Base
has_many :exercises, :through => :writings
has_many :writings
end
class Exercise < ActiveRecord::Base
has_many :users, :through => :writings
has_many :writings
end
class Writing < ActiveRecord::Base
belongs_to :user
belongs_to :exercise
attr_accessible :writing_date, :exercise_id
end
my db/schema.rb
ActiveRecord::Schema.define(:version => 20120517142448) do
create_table "exercises", :force => true do |t|
t.string "etitle"
t.text "ebody"
t.decimal "average", :precision => 3, :scale => 1
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_kind", :default => 0
end
create_table "writings", :force => true do |t|
t.integer "user_id"
t.integer "exercise_id"
t.date "writing_date"
t.datetime "created_at"
t.datetime "updated_at"
end
the join table is Writing. I try to show on exercise view (app/view/exercises/index.html.erb) for each exercise_id her writing_date. Each user_id has many exercise_id through Writing table. Each record on Writing table has writing_id, user_id, exercise_id and writing_date. In console for specifically exercise_id I made it like this:
1.9.2p290 :001 > #exercise = Exercise.find_by_id(9)
=> #<Exercise id: 9, etitle: "PLS51-2012-E01", ebody: "q", average: #<BigDecimal:53eeba8,'0.1E2',9(18)>, created_at: "2012-05-20 14:31:07", updated_at: "2012-05-20 14:34:27", askisi_file_name: nil, askisi_content_type: nil, askisi_file_size: nil, askisi_updated_at: nil>
1.9.2p290 :002 > #exercise.writings
=> [#<Writing id: 6, user_id: 1, exercise_id: 9, writing_date: "2012-06-29", created_at: "2012-05-20 14:36:45", updated_at: "2012-05-20 14:36:45">, #<Writing id: 12, user_id: 7, exercise_id: 9, writing_date: "2012-06-20", created_at: "2012-05-20 14:40:12", updated_at: "2012-05-20 14:40:12">, #<Writing id: 13, user_id: 7, exercise_id: 9, writing_date: "2012-05-02", created_at: "2012-05-20 15:41:54", updated_at: "2012-05-20 15:41:54">]
1.9.2p290 :003 > #exercise.writings.order("writing_date ASC")
=> [#<Writing id: 13, user_id: 7, exercise_id: 9, writing_date: "2012-05-02", created_at: "2012-05-20 15:41:54", updated_at: "2012-05-20 15:41:54">, #<Writing id: 12, user_id: 7, exercise_id: 9, writing_date: "2012-06-20", created_at: "2012-05-20 14:40:12", updated_at: "2012-05-20 14:40:12">, #<Writing id: 6, user_id: 1, exercise_id: 9, writing_date: "2012-06-29", created_at: "2012-05-20 14:36:45", updated_at: "2012-05-20 14:36:45">]
1.9.2p290 :004 > #exercise.writings.order("writing_date ASC").last
=> #<Writing id: 6, user_id: 1, exercise_id: 9, writing_date: "2012-06-29", created_at: "2012-05-20 14:36:45", updated_at: "2012-05-20 14:36:45">
Do I need scope?
Thanks in advance!
Finaly the solution for my problem that can help someone with the same situation is simple. File app/view/exercises/index.html.erb
Code:
<%= exercise.writings.last.try(:writing_date) %>

creating with a belongs_to has_many association

I'm getting the error unknown attribute: user_id durring execution of #user.posts.create in my specs
User Class
class User < ActiveRecord::Base
# new columns need to be added here to be writable through mass assignment
attr_accessible :username, :email, :password, :password_confirmation
has_many :posts, :dependent => :destroy
end
Post Class
class Post < ActiveRecord::Base
attr_accessible :title, :body
belongs_to :user
end
DB Schema
ActiveRecord::Schema.define(:version => 20111214045425) do
create_table "posts", :force => true do |t|
t.string "title"
t.string "body"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", :force => true do |t|
t.string "username"
t.string "email"
t.string "password_hash"
t.string "password_salt"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Any help? I've followed every guide I can find for using ActiveRecord. All I want to do is create a Post with an associated User.
You can use
#user = User.find(1)
#post = #user.posts.build(posts_attributes_as_hash)
#post.save
Or even
post = Post.new(posts_attributes)
#user = User.find(1)
#user.posts << post
Edit
To use create directly:
#user = User.find(1)
#post = #user.posts.create(posts_attributes_as_hash)
For more information have a look at has_many-association-reference especially at section called 4.3.1 Methods Added by has_many
New Edit:
I created a new project with your code and in rails console I tried the following commands
User.create(:username => "UserNamedTest", :email => "usernamedtest#somewhere.com")
SQL (13.6ms) INSERT INTO "users" ("created_at", "email", "password_hash", "password_salt", "updated_at", "username") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Wed, 14 Dec 2011 09:02:46 UTC +00:00], ["email", "usernamedtest#somewhere.com"], ["password_hash", nil], ["password_salt", nil], ["updated_at", Wed, 14 Dec 2011 01:03:26 UTC +00:00], ["username", "UserNamedTest"]]
=> #<User id: 2, username: "UserNamedTest", email: "usernamedtest#somewhere.com", password_hash: nil, password_salt: nil, created_at: "2011-12-14 09:02:46", updated_at: "2011-12-14 09:02:46">
user = User.find_by_username("UserNamedTest")
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."username" = 'UserNamedTest' LIMIT 1
=> #<User id: 2, username: "UserNamedTest", email: "usernamedtest#somewhere.com", password_hash: nil, password_salt: nil, created_at: "2011-12-14 09:02:46", updated_at: "2011-12-14 09:02:46">
new_post = user.posts.create(:title => "just a test", :body =>"body of article test")
SQL (0.5ms) INSERT INTO "posts" ("body", "created_at", "title", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?) [["body", "body of article test"], ["created_at", Wed, 14 Dec 2011 09:03:59 UTC +00:00], ["title", "just a test"], ["updated_at", Wed, 14 Dec 2011 09:03:59 UTC +00:00], ["user_id", 2]]
=> #<Post id: 2, title: "just a test", body: "body of article test", user_id: 2, created_at: "2011-12-14 09:03:59", updated_at: "2011-12-14 09:03:59">
irb(main):022:0> new_post.inspect
=> "#<Post id: 2, title: \"just a test\", body: \"body of article test\", user_id: 2, created_at: \"2011-12-14 09:03:59\", updated_at: \"2011-12-14 09:03:59\">"
From what I see the code is ok, the post get created with no errors
Discovered the issue after a dump of the test.sqlite3 schema. user_id was not defined as a column in the db. Blowing out the database and running rake spec migrates the database and fixes everything.