Rails/Activerecord: Save Changes to a Model's Associated Collections

Rails/ActiveRecord: save changes to a model's associated collections

just do a rental.dvd.save after you increment the value or in the above case you could use

rental.dvd.increment!(:copies)

which will also automatically save, note the '!' on increment!

ActiveRecord save all changes to has_many childs on parent save

I think you need to add

accepts_nested_attributes_for :licenses

in Client model. This would help you to save the child objects

as mentioned in comment. Please refer this link

http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

When will ActiveRecord save associations?

Unfortunately there are no such thing like associations_to_save. However there are some rules saying what is being saved when. You can find those here: http://guides.rubyonrails.org/association_basics.html. Points: 4.1.5 (belongs_to), 4.2.5 (has_one), 4.3.4 (has_many) and 4.4.4 (habtm).

UPDATE:

In case of has_many association, the child is saved on saving the parent if child.new_record? returns true (child was not yet saved to db), or the foreign_key column needs to be updated. This is why:

  1. Adding object to association on saved parent do save new child.
  2. Adding object to association on unsaved parent doesn't save (no foreign key value)
  3. If unsaved parent is being saved and has some child objects in association cache, those objects are saved to update foreign_key.

Save collection of updated records all at once

Firstly +1 to @andrea for Transactions - cool stuff I didn't know

But easiest way here to go is to use accepts_nested_attributes_for method for model.

Lets make an example. We have got two models: Post title:string and Comment body:string post:references

lets look into models:

class Post < ActiveRecord::Base
has_many :comments
validates :title, :presence => true
accepts_nested_attributes_for :comments # this is our hero
end

class Comment < ActiveRecord::Base
belongs_to :post
validates :body, :presence => true
end

You see: we have got some validations here. So let's go to rails console to do some tests:

post = Post.new
post.save
#=> false
post.errors
#=> #<OrderedHash {:title=>["can't be blank"]}>
post.title = "My post title"
# now the interesting: adding association
# one comment is ok and second with __empty__ body
post.comments_attributes = [{:body => "My cooment"}, {:body => nil}]
post.save
#=> false
post.errors
#=> #<OrderedHash {:"comments.body"=>["can't be blank"]}>
# Cool! everything works fine
# let's now cleean our comments and add some new valid
post.comments.destroy_all
post.comments_attributes = [{:body => "first comment"}, {:body => "second comment"}]
post.save
#=> true

Great! All works fine.

Now lets do the same things with update:

post = Post.last
post.comments.count # We have got already two comments with ID:1 and ID:2
#=> 2
# Lets change first comment's body
post.comments_attributes = [{:id => 1, :body => "Changed body"}] # second comment isn't changed
post.save
#=> true
# Now let's check validation
post.comments_attributes => [{:id => 1, :body => nil}]
post.save
#=> false
post.errors
#=> #<OrderedHash {:"comments.body"=>["can't be blank"]}>

This works!

SO how can you use it. In your models the same way, and in views like common forms but with fields_for tag for association. Also you can use very deep nesting for your association with validations nd it will work perfect.

Rails: How do I save an object to a model after calling Model.new?

1. Don't override the initialize method on ActiveRecord::Model.

ActiveRecord models already take a hash of attributes:

User.new(name: 'Max', awesome: true)

If the attribute exists it will be set on the model instance. The initializer does a lot of work for and you should not be clobbering it lightly.

If you really really need to do something in the initializer make sure you call super and keep the interface the same as one would expect for a model.

def initalize(hash = {}, &block)
super
# do something custom here
end

But in most cases you can use callbacks or custom attribute setters instead.

2. Follow the ruby conventions.

Ruby and Rails for that matter has pretty simple and strong conventions for naming:

  • CONSTANTS are all caps. Ruby enforces this.
  • ModuleName and ClassName are CamelCase (and also a kind of constant).
  • variable_name and attribute_name are snake_case. Ruby does not care - but the community does. Disregard this and you'll never be able to sit with the cool kids.

3. .update and .assign_attributes

There are a few ways to change a model instance but it's important to note the difference between changing the model instance in memory and committing the changes to the database.

@order = Order.new

# change a single attribute.
@order.total = 999

# Update multiple attributes at once
@order.assign_attributes(
total: 669,
shipping_method: 'FEDEX'
)

# So far we have only updated the instance in memory.
@order.save # Commit the changes to the database

# We can also update the database straight away.

@order.update_attribute(:total, 999) # one attribute at a time

# or with a hash.
@order.update(
total: 669,
shipping_method: 'FEDEX'
)

# Using related objects works the same:
@order.update(
customer: @customer
)

# Note that this will also save the related object to the database.
@order.update(
customer: Customer.new
)

4. Use Service Objects to create model instances from API calls.

This limits the amount of contact points between the external api and your application. And they are are really easy to test.

# Import a message from SomeApi
class MessageImportService

def initialize(client = MyApi)
@client = client
end

# @return [Array]
def call
data = @client.call_some_api_method || []
data.map do |raw_msg|
Message.new(
body1 : raw_msg['bodies'][0]['content'],
sender_name : raw_msg['addresses']['from'][0]['email'],
sender_actual_aame : raw_msg['addresses']['from'][0]['name'],
)
end
end
end

From your controller you would do:

@messages = MessageImportService.new.call

https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services

How to update value of a Model's attribute

here is the solution

item = Item.find(1)
item.popularity = 1
item.save

Rails - How to refresh an association after a save

You can use item.reload that will refetch the model from the database and next time you call an association, it will refetch it.



Related Topics



Leave a reply



Submit