RAILS How can I build a nested form to change a has_many through associated object boolean attribute using checkboxes?

Multi tool use
RAILS How can I build a nested form to change a has_many through associated object boolean attribute using checkboxes?
I have 3 Models: User
, Profile
and Photo
.
User
Profile
Photo
Profile < ApplicationRecord
belongs_to :user
has_many :photos, through: :user
Photo < ApplicationRecord
belongs to :user
User < ApplicationRecord
has_one :profile
has_many :photos
I'd like to build a form for @profile
that also displays checkboxes for all of its associated photos.
@profile
When a photo is checked, I'd like the that photo's #featured_status
to be turned to be TRUE
. (it has a default value of 1 in my database).
featured_status
TRUE
Photo class has these methods
class Photo < ApplicationRecord
belongs_to :user
has_attached_file :image, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :image, content_type: /Aimage/.*z/
validates :image, :title, :description, presence: true
FEATURED_STATUS = { not_featured: 0, featured: 1 }
def featured?
self.featured_status == FEATURED_STATUS[:featured]
end
def not_featured?
self.featured_status == FEATURED_STATUS[:not_featured]
end
def myPhoto
ActionController::Base.helpers.image_tag("#{image.url(:thumb)}")
end
end
How can I build this form?
I've tried different variations of using fields_for
, collection_check_boxes, check_boxes and I can't seem to capture the information correctly.
fields_for
At the moment this is my form.
<%= simple_form_for @profile do |f| %>
<%= f.input :bio, input_html: { class: 'form-control'} %>
<section class="main">
<label>Featured Profile Photos</label>
<% @profile.photos.each do |photo| %>
<%= form_for ([@profile, photo]) do |f| %>
<%= f.check_box :featured_status, :checked => (true if photo.featured?) %>
<%= f.submit %>
<% end %>
<label><%= photo.myPhoto %></label>
<% end %>
</section>
<%= f.button :submit %>
When the form renders, there are multiple "update" buttons - one for each photo. I'm also not able to submit the @profile.bio changes at the same time as updating a photo's featured_status.
Ideally, I'd like to have each of those photo update buttons to be hidden
and just have one submit button that updates the Profile bio:text and renders the @profile.
At the same time, I'd like the photo.featured_status to be turned to true/false as soon as the checkbox is marked. (Maybe using Javascript?)
Any suggestions are really appreciated.
Hi yes! Thats the same association. The User Model has_many photos and has_one profile.
– Kim
Jun 30 at 22:30
Why have you declared that
Photo has_one profile
? Photo is already the child of profile.. It can't really be both the parent and the child. (well it can but you will see that later, especially for Comment models where comment can be the parent of another comment but also a child of a comment, but it needs specific wording)– Maxence
Jun 30 at 22:34
Photo has_one profile
It seems something is wrong with your code if
User
is the join table and you are using the has_many :through
Association.– iGian
Jun 30 at 22:35
User
has_many :through
@iGian is correct: in the edgeguide
Appointments
are the children of both Patients
and Physicians
. In your app design. one Photo
can probably belong to a Profile
and a User
, though User
already belong to Profile
. Users
and profiles
are not 2 distinct entities like in the guide but are already related in your design. Please explain what are Users and Profiles, their difference and we will try to tell you the right relationships– Maxence
Jun 30 at 22:45
Appointments
Patients
Physicians
Photo
Profile
User
User
Profile
Users
profiles
2 Answers
2
as per your last comments I would design my relationship like this :
User
has_one :profile
has_many :photos
Profile
belongs_to :user
Photo
belongs to :user
Now you can decide where you want to put the photo display on/off editing. Either in Users Edit action, alongside other User data, or even Profile data. Or maybe by creating a specific action in the Photo controller (the edit action is usually for editing a single instance, not all instances for a specific user, which should amount to a few)
Then if you have actual code and views, you can go further.
Thanks Maxence, I've added some additional code in my original question above. Please let me know if you have any suggestions. I appreciate you taking the time! The profile.photos relationship is to fulfill a project requirement for school. I needed a nested form
– Kim
Jul 1 at 0:20
app/models/profile.rb:
class Profile < ApplicationRecord
has_one :user
has_many :photos, through: :user
accepts_nested_attributes_for :photos
end
app/controllers/profiles_controller.rb:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:update] # etc...
def update
if @profile.update(profile_params)
# success, do something; i.e:
# redirect_to @profile, notice: 'Profile has been updated'
else
# validation errors, do something; i.e:
# render :edit
end
end
private
def set_profile
@profile = Profile.find(params[:id])
end
def profile_params
params.require(:profile).permit(:bio, photos_attributes: [:id, :featured_status]) # etc...
end
end
app/views/profiles/_form.html.erb
<%= simple_form_for @profile do |f| %>
<%= f.input :bio, input_html: { class: 'form-control'} %>
<section class="main">
<label>Featured Profile Photos</label>
<%= f.simple_fields_for :photos do |ff| %>
<%= ff.check_box :featured_status %>
<label><%= ff.object.myPhoto %></label>
<% end %>
</section>
<%= f.button :submit %>
<% end %>
Similar with the other comments from Maxence and iGian, I would advise something like below:
... instead of:
... for the sole reason being that intuitively speaking, I think a User (someone who logs in) can have one or many Profiles, of which then a Profile should belong to a User. Currently you have a Profile has_one User, which is always gonna work either way where the foreign_key is gonna be (either User or Profile is ok), but what I usually do to know where the foreign_key is placed is is to take into account a possible future change: (i.e. has_one becomes has_many), and therefore now that I established this, it makes more sense for a User to have many Profiles, instead of a Profile to have many Users. Of course, if you intentionally want it this way and aware of your schema implementations, then it's always also a correct way to do it (depending on your prerequisites) :)
simple_fields_for
I just noticed that you are using Profile has_one User, but at the same time User has_one Profile. Only one of them should be has_one, while the other should be belongs_to, and not both has_one each other. I do not know where you put the foreign_key, i.e. do you have a
user_id
column in profiles table? (then it should be Profile belongs_to :user), or... do yo have a profile_id
column in users table? (then it should be User belongs_to :profile. Now, my solution above assumes that you have a profile_id
column in users table.– Jay-Ar Polidario
Jul 1 at 1:34
user_id
profile_id
profile_id
My mistake! Yes I've implemented Profile belongs to one user. I've been staring at my code a while so my brain is melting. My foreign key is on the Profile model.
– Kim
Jul 1 at 2:54
I see, since it's Profile belongs_to user, I think my solution above would still work because I just looked at this SO post, because seems like nested attributes also work for belongs_to at Rails >= 4. If not let me know, I'll try to test this out myself when I get the time.
– Jay-Ar Polidario
Jul 1 at 3:34
Hi Jay! Thank you a million times for these recommendations, I've been trying to work on this one feature for a few days now. I've implemented your recommendations for the form along with the profile controller to a T. The form seems perfect for what I wanted to be rendered. However, on submit, I am getting an error and the photo changes are not being saved.
– Kim
Jul 1 at 4:24
The full message errors are:
photos.user must exist, photos.image can't be blank, photos.title can't be blank
. These are my singular photo validations. I'm not positive, but it seems that the form is passing a whole collection of photos to the method (photos.user instead of photo.user) rather than an individual one...? Do you know how I can fix this? Does it have something to do with my Profile Photo relationship?– Kim
Jul 1 at 4:24
photos.user must exist, photos.image can't be blank, photos.title can't be blank
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Can you give more details about the association between models? One is missing. Are you referring to something similar to edgeguides.rubyonrails.org/…?
– iGian
Jun 30 at 21:59