Ok, trying to learn rails commands on associations relationships in console
So if I have User that has_many posts, and Posts that belongs_to user ...
That means all this should work, correct? And these are all the the methods added automagically for this particular relationship, correct?
1. user.posts
2. user.posts=(posts)
3. user.posts << post
4. user.posts.delete(post)
5. user.posts.empty?
6. user.posts.size
7. user.post_ids
8. user.posts.clear
9. user.posts.find
10. user.posts.build(attributes={})
11. user.posts.create(attributes={})
user is: user = User.create(:name => "Michael")
result is: #<User id: 3, name: "Michael", created_at: "2016-06-21 16:52:22", updated_at: "2016-06-21 16:52:22">
&
post is: post = Post.create(:body => "body of txt")
result is: #<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">
1. For user.posts, I get this:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">, #<Post id: nil, user_id: 3, body: "txt of body2", created_at: nil, updated_at: nil>]>
2. For user.posts=(posts), I get this:
NameError: undefined local variable or method posts' for main:Object and a whole bunch more of rbenv stuff
3. For user.posts << post, I get this:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">, #<Post id: nil, user_id: 3, body: "txt of body2", created_at: nil, updated_at: nil>, #<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">]>
5. For user.posts.empty?, I get this:
false
6. For user.posts.size, I get this:
3
7. For user.post_ids, I get this:
[3, nil, 3]
9. For user.posts.find, I get this:
ActiveRecord::RecordNotFound: Couldn't find Post without an ID
10. For user.posts.build(attributes={:body => "random body of txt"}), I get this:
#<Post id: nil, user_id: 3, body: "random body of txt", created_at: nil, updated_at: nil>
Interesting thing about the one above is that when I do Post.all, that doesn't show up! But if I do user.posts, it does show up in that ...
11. For user.posts.create(attributes={}), I get this:
=> #<Post id: 4, user_id: 3, body: "body of text via create method", created_at: "2016-06-21 17:49:50", updated_at: "2016-06-21 17:49:50">
4. For user.posts.delete(post), I get this:
(0.3ms) begin transaction
SQL (0.6ms) DELETE FROM "posts" WHERE "posts"."id" = ? [["id", 3]]
(1.8ms) commit transaction
=> [#<Post id: 3, user_id: 3, body: "body of txt", created_at: "2016-06-21 16:53:51", updated_at: "2016-06-21 16:58:38">]
8. For user.posts.clear, I get this:
DELETE FROM "posts" WHERE "posts"."user_id" = ? [["user_id", 3]]
Which means now, when I run this: user.posts, I get this:
#<ActiveRecord::Associations::CollectionProxy []>
So, as you can see from above #2 doesn't work ... as well #9. Why not?
Thanks in advance for your help!
EDIT in response to answer (easier for clarification)
ok, I did posts = user.posts which resulted:
#<ActiveRecord::Associations::CollectionProxy [#<Post id: 5, user_id: 3, body: "body of txt", created_at: "2016-06-21 18:00:57", updated_at: "2016-06-21 18:01:11">, #<Post id: 6, user_id: 3, body: "body of txt2", created_at: "2016-06-21 18:22:02", updated_at: "2016-06-21 18:22:36">]>
then I did user.posts=(posts) which resulted the same answer as above.
But when I ran posts = Post.where(user: user), I got this:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: posts.user: SELECT "posts".* FROM "posts" WHERE "posts"."user" = 3
Also, when I ran user.posts = posts, I got this:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: posts.user: SELECT "posts".* FROM "posts" WHERE "posts"."user" = 3
In regards to user.posts.find(Post.last.id), it worked if I did this:
user.posts.find(Post.last) but if I did something like this:
user.posts.find(Post.last.6), I got this:
SyntaxError: (irb):121: no .<digit> floating literal anymore; put 0 before dotuser.posts.find(Post.last.6)
For #2
You need to define what "posts" is.
If you are trying to "set" posts
posts = user.posts
If you want to do a comparison on the two you would do:
posts = Post.where(user: user)
user.posts == posts #should be true
For #9
You need to pass something into your find method:
user.posts.find(Post.last) #this returns an object if "user" created Post.last
Hope this helps!
Edit:
You can't do user.posts=(posts) because you set data with an array, hash, string, integer, etc.
An equals comparison operator should have 2 ='s. That was my mistake and I have corrected the post.
Related
I have an endpoint the returns all the comments on a blog. I would like to have the name of the user that made the comment. Is there a way to bring it all together when hitting that comments endpoint or do I have to make another query for each comment?
def comments
#comments = #blog.comments
render json: { comments: #comments }
end
This is what doing #blog.comments returns
[#<Comment id: 1, content: "This is a very good post", created_at: "2020-09-11 01:55:56", updated_at: "2020-09-11 01:55:56", blog_id: 3, user_id: 1>, #<Comment id: 2, content: "I agree with all of this", created_at: "2020-09-11 01:55:56", updated_at: "2020-09-11 01:55:56", blog_id: 3, user_id: 1>, #<Comment id: 3, content: "I don't agree with all of this", created_at: "2020-09-11 01:55:56", updated_at: "2020-09-11 01:55:56", blog_id: 3, user_id: 1>]>
Assuming you have a users table with a field users.name, and a belongs_to :user relationship on your model comment:
Just replace the line
#comments = #blog.comments
with
#comments = #blog.comments.select('comments.*, users.name').joins(:user)
Had a question on subtracting queries from similar ActiveRecord collections.
Let say I have one query that is as follows:
all_users = User.all
users_with_adequate_reviews = User.joins(:reviews).select("users.id, count(*) as num_reviews").group(:id).having("num_reviews > 5")
if I do all_users - users_with_adequate_reviews, I get what I would expect from which is users with fewer than review count of 5. How does ActiveRecord relation subtraction know to remove the similar records even though i only select a few attributes from users (primarily the id). Was looking to see documentation on this but couldn't find it anywhere
WHERE IS SUBTRACTION METHOD DEFINED ?
Subtraction on ActiveRecord relation is defined on ActiveRecord::Delegation module.
If you're digging that source code, you can see that method is delegated from Array class.
So we need to dig Array's subtraction to understand how ActiveRecord relation's subtraction works.
HOW DOES ARRAY SUBTRACTION WORK ?
This is taken from documentation about Array subtraction / difference.
Array Difference
Returns a new array that is a copy of the original array, removing any
items that also appear in other_ary. The order is preserved from the
original array.
It compares elements using their hash and eql? methods for efficiency.
It means subtraction evaluates two methods : hash && eql? from each object to perform task.
HOW DO THOSE METHODS WORK ON ACTIVE RECORD OBJECT ?
The code below is taken from ActiveRecord::Core module.
def ==(comparison_object)
super ||
comparison_object.instance_of?(self.class) &&
!id.nil? &&
comparison_object.id == id
end
alias :eql? :==
def hash
if id
self.class.hash ^ id.hash
else
super
end
end
You can see both hash & eql? only evaluates class and id.
It means all_users - users_with_adequate_reviews will exclude some objects ONLY IF there are any objects from both elements that have same object's id and object's class.
ANOTHER SAMPLE
irb(main):001:0> users = User.all
User Load (26.4ms) SELECT `users`.* FROM `users` LIMIT 11
=> #<ActiveRecord::Relation [
#<User id: 1, name: "Bob", created_at: "2020-06-09 13:03:45", updated_at: "2020-06-09 13:03:45">,
#<User id: 2, name: "Danny", created_at: "2020-06-09 13:04:14", updated_at: "2020-06-09 13:04:14">,
#<User id: 3, name: "Alan", created_at: "2020-06-09 13:05:30", updated_at: "2020-06-09 13:05:30">,
#<User id: 4, name: "Joe", created_at: "2020-06-09 13:07:00", updated_at: "2020-06-09 13:07:00">]>
irb(main):002:0> users_with_multiple_emails = User.joins(:user_emails).select("users.id, users.name, count(*) as num_emails").group(:id).having("num_emails > 1")
User Load (2.8ms) SELECT users.id, users.name, count(*) as num_emails FROM `users` INNER JOIN `user_emails` ON `user_emails`.`user_id` = `users`.`id` GROUP BY `users`.`id` HAVING (num_emails > 1) LIMIT 11
=> #<ActiveRecord::Relation [#<User id: 1, name: "Bob">]>
irb(main):003:0> users - users_with_multiple_emails
=> [
#<User id: 2, name: "Danny", created_at: "2020-06-09 13:04:14", updated_at: "2020-06-09 13:04:14">,
#<User id: 3, name: "Alan", created_at: "2020-06-09 13:05:30", updated_at: "2020-06-09 13:05:30">,
#<User id: 4, name: "Joe", created_at: "2020-06-09 13:07:00", updated_at: "2020-06-09 13:07:00">]
As you can see all users - users_with_multiple_emails excludes first object (Bob).
Why ? It's because Bob from both elements have same id and class (id: 1, class: User)
Subtraction returns different result if it's like this
irb(main):001:0> users = User.all
User Load (26.4ms) SELECT `users`.* FROM `users` LIMIT 11
=> #<ActiveRecord::Relation [
#<User id: 1, name: "Bob", created_at: "2020-06-09 13:03:45", updated_at: "2020-06-09 13:03:45">,
#<User id: 2, name: "Danny", created_at: "2020-06-09 13:04:14", updated_at: "2020-06-09 13:04:14">,
#<User id: 3, name: "Alan", created_at: "2020-06-09 13:05:30", updated_at: "2020-06-09 13:05:30">,
#<User id: 4, name: "Joe", created_at: "2020-06-09 13:07:00", updated_at: "2020-06-09 13:07:00">]>
irb(main):002:0> users_with_multiple_emails = User.joins(:user_emails).select("users.name, count(*) as num_emails").group(:id).having("num_emails > 1")
User Load (2.3ms) SELECT users.name, count(*) as num_emails FROM `users` INNER JOIN `user_emails` ON `user_emails`.`user_id` = `users`.`id` GROUP BY `users`.`id` HAVING (num_emails > 1) LIMIT 11
=> #<ActiveRecord::Relation [#<User id: nil, name: "Bob">]>
irb(main):003:0> users - users_with_multiple_emails
=> [
#<User id: 1, name: "Bob", created_at: "2020-06-09 13:03:45", updated_at: "2020-06-09 13:03:45">,
#<User id: 2, name: "Danny", created_at: "2020-06-09 13:04:14", updated_at: "2020-06-09 13:04:14">,
#<User id: 3, name: "Alan", created_at: "2020-06-09 13:05:30", updated_at: "2020-06-09 13:05:30">,
#<User id: 4, name: "Joe", created_at: "2020-06-09 13:07:00", updated_at: "2020-06-09 13:07:00">]
This time users_with_multiple_emails only select name & num_emails.
As you can see all users - users_with_multiple_emails doesn't exclude Bob.
Why ? It's because Bob from both elements have different id.
Bob's id from users : 1
Bob's id from users_with_multiple_emails : nil
I'm using acts_as_commentable_with_threading gem with my Rails 4.0 application.
I have Notification controller:
class Notification < ActiveRecord::Base
acts_as_commentable
# ......
end
Now using build_from to create a comment is not working:
>> n = Notification.last
Notification Load (2.3ms) SELECT "notifications".* FROM "notifications" ORDER BY "notifications"."id" DESC LIMIT 1
=> #<Notification id: 134, owner_id: 223, sender_id: 247, n_type: "SAVED_SEARCH", url: nil, content: "2219", read: false, created_at: "2015-01-02 19:52:54", updated_at: "2015-01-02 19:52:54", archived: false, short_url: "http://www.some_url.com">
>> u = User.last
User Load (1.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
=> #<User id: 252, email: "my#email.com", encrypted_password: "$2a$1...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, first_name: "eqbalosss", last_name: nil, created_at: "2015-01-01 20:16:52", updated_at: "2015-01-01 20:16:52", provider: nil, uid: nil>
>> Comment.build_from(n, u.id, "test")
=> #<Comment id: nil, commentable_id: 134, commentable_type: "Notification", title: nil, body: "test", subject: nil, user_id: 252, parent_id: nil, lft: nil, rgt: nil, created_at: nil, updated_at: nil>
>> Comment.count
(1.0ms) SELECT COUNT(*) FROM "comments"
=> 0
I checked out the documentation and I have no clue what am I doing wrong? do I still have association between Comment and Notification? I assume that the gem would do that already.
Any help would be appreciated.
When you use build_from when are not actually saving the comment into the database; instead you're just building it based on your User model.
So, when you perform Comment.count you're querying your database and since the comment wasn't saved, it returns zero results.
You have to call either comment.save or comment.save! after building it in order to persist it to the database.
I hope it helps
I have a problem with using has_many through on a concern and from a polymorphic association.
So I have 3 models and concern here:
https://gist.github.com/andreorvalho/5c2f0e3800fbb126df85
my problem is when I create a competition and do:
competition.content_permission_sources I get all the correct values, but when I do
competition. permission_sources I get an empty collection proxy, even though the content_permission_source has a permission_source:
2.1.1 :022 > c.content_permission_sources
=> #<ActiveRecord::Associations::CollectionProxy [#<ContentPermissionSource id: 1, permissible_id: 1, permissible_type: "Competition", permission_source_id: 5, is_forced: nil, created_at: "2014-05-26 19:08:40", updated_at: "2014-05-26 19:08:40">, #<ContentPermissionSource id: 2, permissible_id: 1, permissible_type: "Competition", permission_source_id: 4, is_forced: nil, created_at: "2014-05-26 19:08:40", updated_at: "2014-05-26 19:08:40">, #<ContentPermissionSource id: 3, permissible_id: 1, permissible_type: "Competition", permission_source_id: 6, is_forced: nil, created_at: "2014-05-26 19:08:40", updated_at: "2014-05-26 19:08:40">]>
2.1.1 :023 > c.permission_sources
=> #<ActiveRecord::Associations::CollectionProxy []>
2.1.1 :024 > c.content_permission_sources.first.permission_source
=> #<PermissionSource id: 5, collectable_id: 1, collectable_type: "Site", source_code: 690089, source_name: "fallback", permission_type: "newsletter", country_code: "dk", created_at: "2014-05-26 19:03:01", updated_at: "2014-05-26 19:03:01">
Anybody has an idea of what I am doing wrong and how can I make sure I can access it correctly?
In my model I have :
#models/friend.rb
scope :approved_friend, where(:approved => true)
And the Rails console outputs :
User.find(2).friends
=> [#<Friend id: 18, user_id: 2, approved: true, created_at: "2013-04-23 09:18:59", updated_at: "2013-04-23 09:18:59", friend_id: 1>]
User.find(2).friends.approved_friend
=> []
Notice that approved is true in the output ...
Where it gets crazy is here :
User.find(1).friends.approved_friend
=> [#<Friend id: 19, user_id: 1, approved: true, created_at: "2013-04-23 09:19:36", updated_at: "2013-04-23 09:19:36", friend_id: 2>]
Am-I missing something ?
EDIT :
On one hand you have this query :
SELECT "friends".* FROM "friends" WHERE "friends"."user_id" = 2
=> [#<Friend id: 18, user_id: 2, approved: true, created_at: "2013-04-23 09:18:59", updated_at: "2013-04-23 09:18:59", friend_id: 1>]
On the other hand, you've got this (query through scope) :
SELECT "friends".* FROM "friends" WHERE "friends"."user_id" = 2 AND "friends"."approved" = 't'
=> []
Since the :status field is in the Friend model, you might have to change the scope to this
scope :approved_friend, where('friends.approved' => true).includes(:friend)