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:
- Adding object to association on saved parent do save new child.
- Adding object to association on unsaved parent doesn't save (no foreign key value)
- 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
andClassName
are CamelCase (and also a kind of constant).variable_name
andattribute_name
aresnake_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
How to Perform Vector Addition in Ruby
Ruby 1.9 Doesn't Support Unicode Normalization Yet
Rake Aborted! Stack Level Too Deep
I = True and False in Ruby Is True
How to Best Handle Per-Model Database Connections with Activerecord
Running into Issues with Rvm During Ruby Install (1.9.2)
How to Understand Sender and Receiver in Ruby
Rails 3 Actionmail Openssl::Ssl::Sslerror
How to Override Gemfile for Local Development
What's a Rails Plugin, or Ruby Gem, to Automatically Fix English Grammar
Rails: Model Instance Method or Helper Method
How to Get a List of Gems That Are Installed That Have Native Extensions
How to Delete a Range of Values from an Array
How to Make Xcode Use the Correct Version of Ruby When Running a Script
Commonmarker Gem Cannot Be Installed (Needed for Jekyll) MACos