I have a one-to-one relationship between user and goal. I want to build a form that shows the goal of a user. The problem is that my code only works when the user already has a goal defined. The text field is not rendered when no goal is present.
<%= user_builder.fields_for :goal do |goal_builder| %>
<%= goal_builder.text_field :goal %>
<% end %>
Does Rails provide an easy way to do this?
This is how I would do it :
class User < ActiveRecord::Base
has_one :goal
accepts_nested_attributes_for :goal
after_initialize do
self.goal ||= self.build_goal()
end
end
You can do this very easily with accepts_nested_attributes_for.
In the view, as you had:
<%= user_builder.fields_for :goal do |goal_builder| %>
<%= goal_builder.text_field :goal %>
<% end %>
In the User model:
class User < ActiveRecord::Base
has_one :goal # or belongs_to, depending on how you set up your tables
accepts_nested_attributes_for :goal
end
See the documentation on nested attributes, and the form_for method for more information.
Related
I have two models.
Exemplary:
class Book < ActiveRecord::Base
has_many :pages, dependent: :destroy
accepts_nested_attributes_for :pages, allow_destroy: true
end
class Page < ActiveRecord::Base
belongs_to :book
validate_on_create :count_within_bounds
LIMIT = 200
private
def count_within_bounds
if self.book.pages.count >= LIMIT
errors.add_to_base("Number of pages cannot be greater than #{LIMIT}")
end
end
end
Now when update the book through a nested form everything is working just fine. I can edit let's say the title and add new pages. But if the page validation fails the other changes made to the book model are not getting saved either.
I understand that it's all being saved in one transaction but is there a way to persist the parent regardless without having to do it manually in two steps, i.e. saving the parent first without pages_attributes?
You can take away the validation and do something like:
<%= form_for(#book) do |f| %>
# book attribute stuff....
<% if #book.pages.count < 200 %>
<%= f.fields_for :pages, #book.pages.create do |ff| %>
<%= ff.text_field :attribute %><br>
<% end %>
<% end %>
<% end %>
Now only books with less than 200 pages will get a form that includes fields to add pages.
If you want to save the parent only and skip the validations for your Page subclass you can use the validate option in your Book model.
class Book < ActiveRecord::Base
has_many :pages, validate: false, dependent: :destroy
end
That title's a mouthful.
So I have something like this:
class Company < ActiveRecord::Base
has_many :company_partner_associations
has_many :partners, through: :company_partner_associations
end
class CompanyPartnerAssociation
belongs_to :company
belongs_to :partner
end
class Partner
has_many :company_partner_associations
has_many :companies, through: :company_partner_associations
end
And on a company form, i'm trying to make a list of all Partners with a checkbox next to them. If I check one, it creates association. If I uncheck it destroys.
<%= f.fields_for :company_partner_associations, Partner.all do |p| %>
<%= f.check_box :partner_id %>
<% end %>
fails because the object getting passed is a Partner, so getting undefined partner_id on Partner
I'm sure there's a nifty solution out there! Thank you!
Do this:
<%= f.collection_check_boxes :partner_ids, Partner.all, :id, :name %>
No fields_for.
This will have to be accompanied in the controller with the following params:
params.require(:company).permit(:company, :params, partner_ids: [])
This should set the partner_ids in your #company model.
With HABTM, you can declare associative data by populating the "collection_singular_ids" method; HMT has the same method appended with the has_many relation:
Although this will replace the current associated objects, it is much simpler than calling f.fields_for - especially for picking partners.
--
You can also use collection_check_boxes which is meant for this purpose :)
Not totally sure this is the problem here, but I think it could be that your controller is just not permitting the array of partner ids. So in your company_partner_params in your company controller needs to permit something like partner_attributes: [:id]. The syntax might not be perfectly correct there, but if that's something you're missing, you should look around for that.
This is what I think the form should look like:
<%= form_for #company do |f| %>
<%= f.fields_for :partners, Partner.all do |partner| %>
...
<% end %>
<% end %>
I am not sure why my checkboxes for a has_many through are not saving. This is a rather complex form (An online application for a language summer program) so in addition to regular applicant information, I am collecting attributes of a LanguageBackgroundAndInterest model using a fields_for. Part of these attributes are the books that the applicant used to learn the language. The code looks like this:
<%= f.fields_for :language_background_and_interest do |builder| %>
<div class="field">
<%= hidden_field_tag "language_background_and_interest[book_ids][]" %>
<% Book.all.each do |book| %>
<br><%= check_box_tag "language_background_and_interest[book_ids][]", book.id %>
<%= book.name.humanize %>
<% end %>
</div>
<% end %>
Both the language_background_and_interest and books are joined together using a has_many_through like so:
class LanguageBackgroundAndInterest < ActiveRecord::Base
attr_accessible :book_ids
has_many :language_background_books
has_many :books, through: :language_background_books
end
class Book < ActiveRecord::Base
attr_accessible :name, :publisher
has_many :language_background_books
has_many :language_background_and_interests, through: :language_background_books
end
# Join Table
class LanguageBackgroundBook < ActiveRecord::Base
attr_accessible :language_background_and_interest_id, :book_id
belongs_to :language_background_and_interest
belongs_to :book
end
I am not sure why the books from the checkboxes don't get assigned to the appropriate background model. Any ideas?
PS: Granted, this design is rather ambiguous (Why not make books belong to an applicant?), but I currently want to put up a prototype and I am also constrained by a dubious requirement. So I need this working.
I ended up reviewing my model design and simplified it. I also switched to Simple Form to ease up working with my complex forms.
I have the following setup in my rails app:
A user registers and he chooses from a set of check boxes for Music Styles.
The Music Styles are only 4 right now but should be extendable. I'd like to have a list of MusicStyles that I can extend and change easily.
My approach would be to create a model 'MusicStyles' and a model 'UserMusicStyles' and then use a has_many_through association similar to:
class User < ActiveRecord::Base
has_many :user_music_styles
has_many :music_styles, :through => :user_music_styles
end
class UserMusicStyle < ActiveRecord::Base
belongs_to :user
belongs_to :music_style
end
class MusicStyle < ActiveRecord::Base
has_many :music_styles
has_many :users, :through => :user_music_styles
end
Now, during registration I would do something like MusicStyle.all.each do |m| ... to display the checkboxes but how do I save it to the database correctly in the user controller?
Any help much appreciated!
You can do it like this:
<%= form_for #user do |f| %>
<!-- User stuff -->
...
<% MusicStyle.all.each do |m| %>
<%= check_box_tag('user[music_style_ids][]', m.id, #user.music_styles.include?(m)) %>
<% end %>
<%= f.submit 'Save' %>
<% end %>
I have these models:
class Order < ActiveRecord::Base
has_many :order_lines
has_many :prizes, :through => :order_lines
accepts_nested_attributes_for :order_lines
end
class Prize < ActiveRecord::Base
has_many :order_lines
end
class OrderLine < ActiveRecord::Base
belongs_to :order
belongs_to :prize
end
I would like a nested form in the order form that displays every prize with a text box next to it where the user can enter pieces (eg. the amount to order). When the form is submitted the create action should create the order_lines accordingly. I can't find a solution anywhere.
First of all in the Order model use accepts_nested_attributes_for :prizes, instead of what you have.
After that it's easy, just add a form in the view (there's no additional step controller)
<%= form_for #order do |order_form| %>
..
<%= order_form.fields_for :prizes do |prizes_form| %>
<%= prizes_form.text_field :piece %>
..
<% end %>
..
<% end %>
This is straight from the documentation... You should definitely check that first.