Like after_add callback for has_and_belongs_to_many association is there any call back or work around to get after_add functionality for belongs_to association.
One work around is to use after/before save call backs and dirty object functionality.
belongs_to :video
after_save :after_save_task
def after_save_task
do_stuff if video_id_changed?
end
def do_stuff
### do stuff
end
But I cannot save(true) in do_stuff since its going into an infinite loop.
Looks like the feature for adding callbacks for has_on and belongs_to is not yet added. see this thread https://github.com/rails/rails/issues/586
One dirty solution for your specific problem would be to add some dirty attribute to suggest if update has already been made.
like this
belongs_to :video
after_save :after_save_task
attr_accessor :stuff_done
def after_save_task
do_stuff if video_id_changed? && !stuff_done
end
def do_stuff
stuff_done = true
### do stuff
## Saving record here would be fine.
end
Again this is truly a hack, there might exist some better solution for this.
How about overriding the setter method?
Rails guides has an exam0le of this here
an excerpt of which states
Overriding generated methods
Association methods are generated in a module that is included into
the model class, which allows you to easily override with your own
methods and call the original generated method with super. For
example:
class Car < ActiveRecord::Base belongs_to :owner belongs_to
:old_owner
def owner=(new_owner)
self.old_owner = self.owner
super
end
end
If your model class is Project, the module is named
Project::GeneratedAssociationMethods. The GeneratedAssociationMethods
module is included in the model class immediately after the
(anonymous) generated attributes methods module, meaning an
association will override the methods for an attribute with the same
name.
I appreciate this is an old question but I stumbled on it looking for a similar solution
Related
All of the references I've found either show me how to do it upon table creation, or are for a much earlier version of rails. Ideally, I'd like like the foreign_key to be named 'author_id' in the questions table to distinguish it from other users who may be leaving comments or answers later.
class Question < ApplicationRecord
belongs_to :user
end
class User < ApplicationRecord
has_many :questions
end
You can create a new empty migration file in your terminal via rails generate migration RenameUserFkOnQuestion. Open it up and build your migration. This is a handy guide if you're not sure on the name of something.
def change
change_table :questions do |t|
t.rename :user_id, :author_id
end
end
Run the migration and head over to your models. You'll need to update your relationships like so:
class Question
belongs_to :author, class_name: 'User'
end
class User
has_many :questions, inverse_of: :author
end
Then you should be good to go.
I have a parent User model, and each user has_one Contact which hold's the user's contact info.
One of the contact fields is the email. For various legacy reasons and reasons outside the context of this question, I'd love to be able to call #email directly as if it were a property of User
In other words I'd like to do this -
user.email
user.email = "foo#example.com"
Instead of this -
user.contact.email
user.contact.email = "foo#example.com"
I laid out my models as follows, overriding the child's getter/setter from the parent User -
class User < ActiveRecord::Base
has_one :contact
def email
contact.email
end
def email=(value)
contact.email = value
end
end
class Contact < ActiveRecord::Base
belongs_to :user
end
You may have already noticed the problem with the setter - when the User is saved, it doesn't save the child model.
What's the most robust to work around this?
Thanks!
Ok, found the solution for anyone curious - auto-saving
ActiveRecord::AutosaveAssociation is a module that helps with exactly this - saving child associations properly when the parent is saved.
Just need to include an autosave: true. Example -
has_one :contact, autosave: true
It also takes care of destroying records marked for deletion, and a few other fancy things.
I also did a quick check, and it smartly doesn't hit the DB unless it really needs to. That is, if you update the parent only it doesn't bother saving the child association with an other SQL UPDATE statement.
Check out the documentation here.
I have to populate a field in my db when a new user signs-up.But this field is not to be filled by the user, instead will be populated by the application in the db. Here is what i tried:
Added the extra field(investorId) in the migration file.
Overridden the devise controller:
def create
super
if #user.save
#user.investorId = #user.id + X---> some number
#user.save
end
end
Though it is working fine, but I want to know if there are better ways of doing it since I am doing it for the first time.
Thanks,
Sachin
If you need to generate a value before you create a record, you can use the before_create or after_create callbacks on the model.
User < ActiveRecord::Base
after_create :generate_investor_id
private
def generate_investor_id
reload
investor_id = "#{self.id}something"
update_attribute(:investor_id, investor_id)
end
end
Don't override your devise controller, there is no benefit of doing this. Simply put the below logic in your after_create callback -
User < ActiveRecord::Base
after_create :create_investor_id
private
def create_investor_id
self.update_column(:investor_id, id+some_number)
end
end
I have 2 models, product and a photos model as follows:
product.rb
class Product < ActiveRecord::Base
has_many :photos, dependent: :destroy
accepts_nested_attributes_for :photos, allow_destroy: true
end
photo.rb
class Photo < ActiveRecord::Base
belongs_to :product
validates :album, presence: true
mount_uploader :image, PhotoUploader
end
I'm using carrierwave & s3 for file uploads.
I can nest the forms so that users can upload photos at the same time that they create the product. In this scenario, the photo models are saved before the product model is saved, which leads to a photo file path of something like:
uploads/photos/images/photo_id
But the file structure would make more sense if it was something like:
uploads/product/id/photos/
However, product.id is nil at the time of photo creation.
With that in mind, a few questions:
Does the file structure really matter that much or am I over thinking it?
Should I do something instead where the product is saved first with basic info and then the files are attached on a separate form?
Is there something I'm overlooking like a way to trigger carrierwave to move the files after the product model is saved?
From a UI standpoint is it bad practice to have users go thru a multi-step process (in this case for creating a product)?
My solution to have a path such as uploads/products/id/photos/ is to configure the PhotoUploader as follows:
def store_dir
"uploads/products/#{model.product_id}/images"
end
Just be careful that there are no validations that would cause product_id to be non-existant at the time the store_dir method is called.
In my app, a Post has_many Comments and a Comment belongs_to Post (modeled via the has_one relationship in my serializers).
Since I get the Stack Level Too Deep error message whenever I attempt to model both of these relationships in my serializers, I am wondering if I should keep the has_many associations or the has_one association.
I also have an initializer that embeds :ids and sideloads the data.
Thanks for any help guys. I googled this I swear!
For those relationships, your serializers should look like this:
class PostSerializer < ActiveModel::Serializer
embed :ids
attributes :id # add other attributes here
has_many :comments
end
class CommentSerializer < ActiveModel::Serializer
embed :ids
attributes :id # add other attributes here
has_one :post
end
If you're getting a stack level too deep error, it's most likely from not embedding the ids. Without the ids being embedded it would attempt to embed the comments inside the post, and for every comment, again embed the post, which then embeds the comments again and so on in an infinite loop.
You should also make sure that you're using the DS.ActiveModelSerializer and DS.ActiveModelAdapter in your ember app.