Rails Forms with Nested Attributes from Join Model many_to_many - ruby-on-rails

I GOT TWO PROBLEMS:
-I'm stuck with creating a project which includes nested attributes for :position
-I got it nearly working for editing the project details plus the :position attribute, but the fields_for :assigned_projects ads all the fields for all users who are assigned to the project.
I have 3 Models:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
attr_accessible :name, :controlled, :currency, :creator_id, :assigned_projects
accepts_nested_attributes_for :assigned_projects#, :allow_destroy => true
end
class AssignedProject < ActiveRecord::Base
belongs_to :user, class_name: "User"
belongs_to :project, class_name: "Project"
attr_accessible :project_id, :user_id, :position, :project, :user, :user_attributes
accepts_nested_attributes_for :user
end
Each User can create a Project and is the Projects.creator
Each Project has_many Users through the join model Assigned_Project
Each User can have a different position in the project, so I want to save the :position in the join model AssignedProject.
if a user creates a Project, he should be able to edit the project attributes PLUS the :position attribute of the new join model.
Now the Form field for New.Project and Edit.Project
/project/new.htm.erb
<%= form_for( setup_new_project(#project) ) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :assigned_projects do |ff| %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.submit "Add Project", class: "" %>
<% end %>
/project/edit.htm.erb
<%= form_for( setup_project(current_project) ) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :assigned_projects do |ff| %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.submit "Update Project", class: "" %>
<% end %>
And I have the following setup methods as described in this article:
http://www.sitepoint.com/complex-rails-forms-with-nested-attributes/
module FormHelper
def setup_project(project)
project.assigned_projects ||= AssignedProject.new
project
end
def setup_new_project(project)
project.assigned_project = AssignedProject.new
project
end
end
I hope the problem is clear enough.
for creating a new project the current error message is:
undefined method `assigned_project='
15: <%= render 'shared/user_sidebar_menu' %>
16:
17: <div class="span4 offset1">
18: <%= form_for( setup_new_project(#project) ) do |f| %>
19:
20: <%= render 'shared/error_messages', object: f.object %>
21:
UPDATE: added projects_controller.rb
projects_controller.rb
class ProjectsController < ApplicationController
def new
#project = Project.new
end
def create
#project = Project.new(params[:project])
#project.creator = current_user
if #project.save
current_user.assigned_projects.create(project: #project)
redirect_to current_user
else
render 'new'
end
end
end

Update setup_new_project method as below:
def setup_new_project(project)
project.assigned_projects.build ## Updated this line
project
end
Use project.assigned_projects(Notice plural) instead of project.assigned_project(Notice singular WRONG).
User and AssignedProject model are in a 1-M relationship. So, you get dynamic method assigned_projects=(Notice plural), you are getting error as you called assigned_project=(Notice singular) which does not exist.
UPDATE
undefined method each for <AssignedProject:0x007ff7aa55b528>
Use project.assigned_projects.build instead of project.assigned_project = AssignedProject.new.
UPDATE 2
You are approaching this incorrectly. The form helpers are totally not required. All you need to do is update the new and create actions as below:
def new
#project = Project.new
#project.assigned_projects.build
end
and update the form_for in both new and edit view's as below:
<%= form_for(#project) do |f| %>

Related

rails4 collection select with has_many through association and nested model forms

I have a rails4 app. At the moment my collection select only works if I select only one option. Below is my working code. I only have product form. Industry model is populated with seeds.rb. IndustryProduct is only use to connect the other 2 models.
I'd like to know what I have to change in the code to be able to choose more.
I saw some working examples with multiple: true option like (https://www.youtube.com/watch?v=ZNrNGTe2Zqk at 10:20) but in this case the UI is kinda ugly + couldn't pull it off with any of the sample codes. Is there an other solution like having more boxes with one option chosen instead of one box with multiple options?
models:
class Product < ActiveRecord::Base
belongs_to :user
has_many :industry_products
has_many :industries, through: :industry_products
has_many :product_features
accepts_nested_attributes_for :industry_products, allow_destroy: true
accepts_nested_attributes_for :product_features
validates_associated :industry_products
validates_associated :product_features
end
class Industry < ActiveRecord::Base
has_many :industry_products
has_many :products, through: :industry_products
accepts_nested_attributes_for :industry_products
end
class IndustryProduct < ActiveRecord::Base
belongs_to :product
belongs_to :industry
end
_form.html.erb
<%= form_for #product do |f| %>
<%= render 'layouts/error_messages', object: f.object %>
......
<%= f.fields_for :industry_products do |p| %>
<%= p.collection_select :industry_id, Industry.all, :id, :name %>
<% end %>
<%= f.fields_for :product_features do |p| %>
<%= p.text_field :feature, placeholder: "add a feature", class: "form-control" %>
<% end %>
<%= f.submit class: "btn btn-primary" %>
<% end %>
products controller
def new
#product = Product.new
#product.industry_products.build
#product.product_features.build
end
def create
#product = current_user.products.new(product_params)
if #product.save
redirect_to #product
else
render action: :new
end
end
......
def product_params
params.require(:product).permit(....., industry_products_attributes: [:id, :industry_id, :_destroy], industries_attributes: [:id, :name], product_features_attributes: [:feature])
end
Firstly, you could fix your first collection select by using it to set the industry_ids for the #product:
<%= form_for #product do |f| %>
<%= f.collection_select :industry_ids, Industry.all, :id, :name %>
<% end %>
This will allow you to set the collection_singular_ids method, which exists for all has_many associations.
You'd have to back it up in the params method:
#app/controllers/products_controller.rb
....
def product_params
params.require(:product).permit(.... industry_ids: [])
end
A lot more succinct than using nested attributes.
To get that "multiple" selection, you'll want to use the following:
<%= f.collection_select :industry_ids, Industry.all, :id, :name, {}, { multiple: true } %>
Tested & working
--
You may also want to look at collection_check_boxes:
<%= f.collection_check_boxes :industry_ids, Industry.all, :id, :name %>

Rails: belongs_to & accepts_nested_attributes_for, polymorphic

I'm working on my first Rails project, and I have the following model relationship:
class Profile < ActiveRecord::Base
belongs_to :identifiable, polymorphic: true
accepts_nested_attributes_for :students
class Student < ActiveRecord::Base
has_one :profile, as: :identifiable
attr_accessible :profile
The associated controllers are:
class StudentsController < ApplicationController
def new
#student = Student.new
end
def create
#student = Student.new(params[:student])
if #student.save
redirect_to root_path
else
render 'new'
end
end
end
And
class ProfilesController < ApplicationController
def new
#profile = Profile.new
end
def create
#profile = Profile.new(params[:profile])
#profile.save
end
end
What I'm trying to do is create a new Student with the following form, which is in students\new.html.erb:
<h1>Create a new Student Account</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#student) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.fields_for :profile, #profile do |builder| %>
<%= builder.label :name %>
<%= builder.text_field :name %>
<%= builder.label :email %>
<%= builder.text_field :email %>
<%= builder.label :password %>
<%= builder.password_field :password %>
<%= builder.label :password_confirmation, "Confirmation" %>
<%= builder.password_field :password_confirmation %>
<% end %>
</div>
</div>
<p><%= f.submit "Submit", class: "btn btn-large btn-primary" %></p>
<% end %>
I'm getting the following error message when I try to submit the form: No association found for name 'students'. Has it been defined yet? What am I doing wrong? Thanks in advance.
In order for a model to accept nested attributes for another model, the association to the other model needs to be declared. In Profile you have accepts_nested_attributes_for :students, but there is no corresponding association defined (e.g. has_many :students), which is why you're getting that particular error. In your case, this association wouldn't be correct, however.
Usually, if model A accepts nested attributes for model B, either A has_many B or A has_one B. In your case, you have A belongs_to B. A better design would be
class Profile < ActiveRecord::Base
belongs_to :identifiable, polymorphic: true
class Student < ActiveRecord::Base
attr_accessible :profile_attributes
has_one :profile, as: :identifiable
accepts_nested_attributes_for :profile
Should your student be singular? ie: accepts_nested_attributes_for :student
Edit: Also, your Student should accept nested attributes for a Profile, if the Student has_one profile, and the Student form contains the fields_for call (I think...)

Rails 3 Nested Model Form

I am having some issues with nested models in a form, using Rails 3.1rc4.
I presently have models that look like this:
class Sale < ActiveRecord::Base
attr_accessible :customer_id, :vehicle_id, :sale_date
belongs_to :customer
accepts_nested_attributes_for :customer
end
and
class Customer < ActiveRecord::Base
attr_accessible :dealership_id, :first_name, :last_name, :address1, :email
belongs_to :dealership
has_many :sales
has_many :vehicles, :through => :sales
end
I've obviously truncated these slightly, but all the important info is there.
I am attempting to set up a sale form that will also allow me to create a new customer, hence the accepts_nested_attributes_for :customer line in the sale model.
My form view looks like (again truncated, only the important part):
<%= form_for #sale, :html => {:class => 'fullform'} do |f| %>
<%= f.error_messages %>
<%= field_set_tag 'Customer Details' do %>
<% f.fields_for :customer do |builder| %>
<%= builder.label :first_name %><br>
<%= builder.text_field :first_name %>
<% end %>
<% end %>
<% end %>
The problem I am having is that neither the text field nor the label for :first_name are showing up when the form is rendered - there is no error message, it just doesn't appear.
I should mention that I have tried both with and without #sale.customer.build in the new method of my controller, but it seems to have had no effect.
Thanks!
Can anyone suggest what I am doing wrong?
EDIT: For the avoidance of doubt, my sales controller's new method looks like:
def new
#sale = Sale.new
#sale.customer.build
end
Add customer_attributes to your attr_accessible in the Sale model.
Another mistake; Replace:
<% f.fields_for :customer do |builder| %>
With:
<%= f.fields_for :customer do |builder| %>

Why does this user not have an invitation?

I have a form that creates a signed_user. This is the first line of the form:
<%= form_for(setup_user(#signed_user)) do |f| %>
The setup_user is in my application helper:
def setup_user(user)
user.tap do |u|
u.build_invitation
end
end
These are the model associations:
signed_user model:
has_one :invitation, :foreign_key => "sender_id"
invitation model:
belongs_to :sender, :class_name => 'SignedUser'
So why is a user being created without an invitation? I checked my console and the user's invitation is nil...
What you want is a nested form. All the details are available in the article but basically make sure you use accepts_nested_attributes_for in your SignedUser model.
class SignedUser < ActiveRecord::Base
...
has_one :invitation, :foreign_key => "sender_id"
accepts_nested_attributes_for :invitation, :allow_destroy => true
...
end
If you want your form to modify attributes from the Invitation model (in addition to attributes from the SignedUser), you'll also need to use fields_for in your form. For example:
<%= form_for setup_user(#signed_user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
// More user form fields
<%= f.fields_for :invitation do |cf| %>
<%= cf.label :event_name %>
<%= cf.text_field :event_name %>
// More invitation form fields
<% end %>
<%= submit_tag %>
<% end %>

how to check if parent exists in has many relation

I have 3 models:
User: has_many :comments
Video: has_many :comments
Comment: belongs_to :video, :user
videos/show.html.erb
<%= form_for(#comment, :method => :post ) do |f| %>
<%= f.text_area :body %>
<%= f.hidden_field :video_id, :value =>"4" %>
<%= f.submit "submit" %>
<% end %>
comments_controller
def create
#comment = Comment.new(params[:comment].merge(:user_id => current_user.id))
#comment.save
end
How can i check existence of Video before i create a comment?
This should be present as a validation in your Comment model.
class Comment < ActiveRecord::Base
validates_presence_of :video

Resources