I use simple_form. When using rails accepts_nested_attributes_for, the form fails validation complaining that the nested attributes validation failed because the id of the parent object can't be blank (validates_presence_of).
I know the parent object id is being submitted, because if I remove validation the form submits, and the child record is valid. So, the validations are looking at the params before the parent id is associated with the child...and therefor failing. This seems strange. Why is rails running validation at a stage in form submission before the parent of the nested attributes is correctly associated?
Is there a rails way for handling this scenario?
Just found the answer to this in the Rails docs. Here's their example, using inverse_of:
class Member < ActiveRecord::Base
has_many :posts, inverse_of: :member
accepts_nested_attributes_for :posts
end
class Post < ActiveRecord::Base
belongs_to :member, inverse_of: :posts
validates_presence_of :member
end
Related
I have this model:
class CompetitionEntry < ActiveRecord::Base
has_many :participants
has_one :address
has_many :music_programs
accepts_nested_attributes_for :address
accepts_nested_attributes_for :participants, :music_programs,
:allow_destroy => true,
:reject_if => :all_blank
end
and this one:
class Participant < ActiveRecord::Base
belongs_to :competition_entry
has_one :birthplace
validates :name, :surname, :instrument, presence: true
end
Now the problem is that, if I create a new competition entry, it goes through.
But if I fill ONE field, i.e name, then it comes up with an error!
Why is this happening? It should fail when all are empty!
When you use accepts_nested_attributes_for, you are able to create the participants records at the same time that the competition_entry record, considering that the hash passed to competition_entry.create contains participants_attributes. When you pass only name, it validates the participant to be created and fail because it has no surname and instrument. When you leave all field empty, the behaviour should be the same, but it isn't because you explicitly set :reject_if => :all_blank.
:reject_if => :all_blank states that the participant_attributes hash should be ignored if it is blank?, which happens when you don't fill any field. What is happening then is that a competition_entry is being created without trying to create a participant because the accepts_nested_attributes_for is just ignored.
I am unable to use model.update_attribute on an attribute that is mounted by a carrierwave uploader. The SQL statement wont accept the value and adds NULL to the placeholder. If I remove the mount_uploader statement from the model class it works as normal. I am troubleshooting things from the console and trying to add some attributes while seeding the DB and this is thwarting my efforts. Ideas?
Thanks.
Update:
Relevant code:
class Profile < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :sports
has_and_belongs_to_many :interests
has_and_belongs_to_many :minors
has_and_belongs_to_many :majors
has_and_belongs_to_many :events
has_and_belongs_to_many :groups
attr_accessible :description, :username, :avatar, :bio, :first_name, :last_name, :major, :minor, :graduation_date, :living_situation, :phone, :major_ids, :minor_ids, :sport_ids
mount_uploader :avatar, AvatarUploader
end
I am simply trying to rewrite the :avatar string from a db seed file and while testing from the rails console like so:
Profile.first.update_attribute(:avatar, 'foo')
Both work when I comment out the mount_uploader line.
Does adding the mount_uploader method freeze the string or make it immutable?
I found a solution to this.
My issue was that I was not able to alter the attribute mounted my the CarrierWave uploader from my seeds.rb file.
This works:
user.profile.update_column(:avatar, 'foobar/image.png')
class Upload < ActiveRecord::Base
has_many :comments
end
class Gallery < Upload
has_many :images
end
class MusicAlbum < Upload
has_many :audio_tracks
end
Should this work as expected? Will Gallery and MusicAlubm models inherit :comments association from their parent (Upload) and add their own?
Yes, the models are just classes, and when inherited they get all the methods from parent class. So, as both Gallery and MusicAlbum are descendants from Upload model, they will have the has_many :comments association, and both will get data from uploads db table (which needs to have a type column to support STI for this model)
A nice simple STI example can be found here
Given
User:
class User < ActiveRecord::Base
has_many :discussions
has_many :posts
end
Discussions:
class Discussion < ActiveRecord::Base
belongs_to :user
has_many :posts
end
Posts:
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
end
I am currently initializing Posts in the controller via
#post = current_user.posts.build(params[:post])
My question is, how do I set/save/edit the #post model such that the relationship between the post and the discussion is also set?
Save and edit discussions along with post
Existing Discussion
To associate the post you're building with an existing discussion, just merge the id into the post params
#post = current_user.posts.build(
params[:post].merge(
:discussion_id => existing_discussion.id
)
You will have to have a hidden input for discussion id in the form for #post so the association gets saved.
New Discussion
If you want to build a new discussion along with every post and manage its attributes via the form, use accepts_nested_attributes
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
accepts_nested_attributes_for :discussion
end
You then have to build the discussion in the controller with build_discussion after you built the post
#post.build_discussion
And in your form, you can include nested fields for discussions
form_for #post do |f|
f.fields_for :discussion do |df|
...etc
This will create a discussion along with the post. For more on nested attributes, watch this excellent railscast
Better Relations
Furthermore, you can use the :through option of the has_many association for a more consistent relational setup:
class User < ActiveRecord::Base
has_many :posts
has_many :discussions, :through => :posts, :source => :discussion
end
class Discussion < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
end
Like this, the relation of the user to the discussion is maintained only in the Post model, and not in two places.
If I have the following models setup:
class Member < ActiveRecord::Base
has_many :children
end
class Child < ActiveRecord::Base
belongs_to :member
has_many :photos
end
class Photo < ActiveRecord::Base
belongs_to :child
end
When a new Photo is created, what is the best way to ensure that it is associated with a child in the member's account?
I have login working properly, and a current_member helper method, which doesn't seem to be available in the models
So, from what I gather, the "Rails Way™" of doing this would be to put the conditions in the controllers.
ex:
unless current_member.children.collect { |child| child.id.to_s }.include?(#photo.child_id)
#photo.errors.add :child_id "this is not your child"
end