build association and its size issue

I wonder id it is normal behaviour that when building an assosiation ‘build’ method , the returned size of association array changes despite the objet is nt saved yet ? Here is a short exampe to illustrate that:

class User < ActiveRecord::Base has_many :posts, dependent: :destroy end

class Post < ActiveRecord::Base belongs_to :user end

``

Nothing complex until now. When I play with that in the console: Loading development environment (Rails 4.2.0) irb(main):001:0> user = User.first User Load (0.0ms) SELECT “users”.* FROM “users” ORDER BY “users”.“id” ASC LIMIT 1 => #<User id: 1, name: “toto”, created_at: “2015-01-16 11:18:41”, updated_at: “2015-01-16 11:18:41”> irb(main):002:0> user.posts.size (0.0ms) SELECT COUNT(*) FROM “posts” WHERE “posts”.“user_id” = ? [[“user_id”, 1]] => 0 irb(main):003:0> post = user.posts.build(title: ‘ruby’) => #<Post id: nil, user_id: 1, title: “ruby”, created_at: nil, updated_at: nil> irb(main):004:0> user.posts.size => 1 irb(main):005:0>

``

As you see the size of user posts is changed by 1. Why ? The post is not still saved to the database. The user_id is nil. I had to apply some validation before adding a new post tp ths user, that’s why I wonder if it is normal adn how to display just the existing User posts without using ‘select’ for example. Rails version: 4.2.0, Ruby 2.1.5

Thank you.

You have not saved it to the database but you have added it to the users posts in memory. If you reload user it will revert to 0, and you will lose the built post. If you want to check what it is in the database then fetch the user to a new variable. user1 = User.first then user1.posts.size will be 0

Colin

So it is normal because AR does not make a new query to the DB and operates on the existing cached collection. What if instead of making the same query to find the same user:

user1 = User.first

``

I’ll do like that: user.posts(force_reload: true).size

``

it works as needed and it seems like force_reload is false by default. What do you think ?

I think just user.reload would do it. It is not something I have had to do often, why would one build an association and then want to throw it away again in the same action?

Colin

I wonder id it is normal behaviour that when building an assosiation

‘build’

method , the returned size of association array changes despite the

objet is

nt saved yet ? Here is a short exampe to illustrate that:

class User < ActiveRecord::Base

has_many :posts, dependent: :destroy

end

class Post < ActiveRecord::Base

belongs_to :user

end

Nothing complex until now. When I play with that in the console:

Loading development environment (Rails 4.2.0)

irb(main):001:0> user = User.first

User Load (0.0ms) SELECT “users”.* FROM “users” ORDER BY

“users”.“id”

ASC LIMIT 1

=> #<User id: 1, name: “toto”, created_at: “2015-01-16 11:18:41”,

updated_at: “2015-01-16 11:18:41”>

irb(main):002:0> user.posts.size

(0.0ms) SELECT COUNT(*) FROM “posts” WHERE “posts”.“user_id” = ?

[[“user_id”, 1]]

=> 0

irb(main):003:0> post = user.posts.build(title: ‘ruby’)

=> #<Post id: nil, user_id: 1, title: “ruby”, created_at: nil,

updated_at:

irb(main):004:0> user.posts.size

=> 1

irb(main):005:0>

As you see the size of user posts is changed by 1. Why ? The post is not

still saved to the database. The user_id is nil. I had to apply some

validation before adding a new post tp ths user, that’s why I wonder if

it

is normal adn how to display just the existing User posts without using

‘select’ for example.

You have not saved it to the database but you have added it to the

users posts in memory. If you reload user it will revert to 0, and

you will lose the built post. If you want to check what it is in the

database then fetch the user to a new variable.

user1 = User.first

then user1.posts.size will be 0

Colin

So it is normal because AR does not make a new query to the DB and operates

on the existing cached collection. What if instead of making the same query

to find the same user:

user1 = User.first

I’ll do like that:

user.posts(force_reload: true).size

it works as needed and it seems like force_reload is false by default. What

do you think ?

I think just user.reload would do it. It is not something I have had

to do often, why would one build an association and then want to throw

it away again in the same action?

Why do I want it - because I’d like to run a validation callback before saving an element of the collection. For example, an Account has_many Operations. So I create an Operation like that in the OperationsController:

def create @operation = @account.operations.new(operation_params) if @operation.save redirect_to @account, notice: t(:created_success, model: Operation.model_name.human) else render “new” end end

What I’d like to do is to check the sum to be withdrawn in the above operation so that the balance of the account not to be negative. That’s how I discovered the described behaviour.