Unpermitted parameters for nested form many-to-many relationship Rails 4 - ruby

I have a simple blog app, where I want to be able to create a Post and also create a new Tag for it in the same form, using a nested form.
Post and Tag have a many-to-many relationship, via a join table:
class PostTag < ActiveRecord::Base
belongs_to :post
belongs_to :tag
end
Here's the tag model:
class Tag < ActiveRecord::Base
has_many :post_tags
has_many :posts, :through => :post_tags
validates_presence_of :name
validates_uniqueness_of :name
end
Post model accepts nested attributes for tags:
class Post < ActiveRecord::Base
has_many :post_tags
has_many :tags, :through => :post_tags
accepts_nested_attributes_for :tags
validates_presence_of :name, :content
end
On the posts controller, I permit tags_attributes:
def post_params
params.require(:post).permit(:name, :content, :tag_ids => [], :tags_attributes => [:id, :name])
end
In my form for a new post, where I want to be able to either associate already existing tags (via checkboxes) or create a new one via a nested form using fields_for:
....
<div class="field">
<%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name %><br>
<%= f.fields_for [#post, Tag.new] do |tag_form| %>
<p>Add a new tag:</p><br>
<%= tag_form.label :name %>
<%= tag_form.text_field :name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My error is "Unpermitted parameters: tag":
Parameters: {"utf8"=>"✓", "authenticity_token"=>"dZnCgFxrvuoY4bIUMMxI7kTLEr/R32pUX55wwHZsS4Q=", "post"=>{"name"=>"post title", "content"=>"post content", "tag_ids"=>[""], "tag"=>{"name"=>"new tag"}}, "commit"=>"Create Post"}
Unpermitted parameters: tag

Change:
<%= f.fields_for [#post, Tag.new] do |tag_form| %>
to
<%= f.fields_for(:tags, Tag.new) do |tag_form| %>

Related

undefined method `students' for #<Period:0x007fb7fff47360>

I'm trying to add students I created in a school, to a teachers period (class). I'm not looking to create a new student in a period, but rather select students from a list I created in the school. I was able to list the students in a period, but cannot add them to the period. I'm unsure if I use the build method or something different. I'm currently getting an undefined method error and am just unsure which way to go from here.
<p id="notice"><%= notice %></p>
<p>
<strong>Class Name:</strong>
<%= #period.period %>
</p>
<%= link_to 'Delete Period', [#period.teacher, #period],
method: :delete,
data: { confirm: 'Are you sure?' } %>
<h2>Add a student to period</h2>
<%= form_for([#period, #period.students.build]) do |f| %>
<p>
<%= check_box_tag "school[student_ids][]", student.id %>
<%= link_to student.name, edit_school_student_path(#school, student) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
class Period < ActiveRecord::Base
belongs_to :teacher
has_many :students
end
class Student < ActiveRecord::Base
belongs_to :school
has_many :teachers, through: :periods
end
class Teacher < ActiveRecord::Base
belongs_to :school
has_many :periods
has_many :students, through: :periods
end
class School < ActiveRecord::Base
has_many :teachers, dependent: :destroy
has_many :students, dependent: :destroy
has_many :schoolads, dependent: :destroy
end

Nested form for has_many through association

I have two models, reservations and tables, with has_many through relationship between them with a join table and model collections, that has a separate attribute called :units_sold
my Reservation model:
class Reservation < ActiveRecord::Base
has_many :tables, through: :collections
has_many :collections
end
my table model:
class Table < ActiveRecord::Base
has_many :reservations, through: :collections
has_many :collections
end
and finally the collection model:
class Collection < ActiveRecord::Base
belongs_to :table
belongs_to :reservation
end
E.g:
A reservation in my name for 9 people (:units_sold) has 2 tables each with a capacity of 6 and I want 4 people in one table and 5 on the second table
What I have currently is this:
reservation_params
def reservation_params
params.require(:reservation).permit( :name, :total_units, table_ids: [],
collection_attributes: [ :units_sold],
table_attributes: [:capacity, :title])
end
and my form to submit a reservation:
<%= form_for [current_user, #account, #reservation] do |f| %>
<header>
<h1>Make your reservation</h1>
</header>
<%= f.text_field :name, placeholder: "Name" %>
<%= f.number_field :total_units, placeholder: "How many people..." %>
<%= f.fields_for :collections do |c|%>
<% #tables.each do |p| %>
<label for="<%= p.title %>"><%= p.title %></label>
<%= c.number_field :units_sold, placeholder: "People per table..." %>
<%= check_box_tag "reservation[table_ids][]", p.id %>
<% end %>
<% end %>
<%= f.submit "Save" %>
<% end %>
How should I do the reservation_params in the reservation controller? And nest the form to accept :units_sold for each table/reservation association
First, you should define the associations like :
has_many :collections
has_many :reservations, through: :collections
as in, first define collections and then reservations through collections. Similarly, update the associations in the Table model.
Then in the reservation model after defining the associations, add the following line:
accepts_nested_attributes_for :collections
In reservation_params, the key "collection_attributes" and "table_attributes" should be "collections_attributes" and "tables_attributes" respectively.

Nested attributes that do not belong to the current object using has_many :through

My end goal is to be able to add costumes to an agreement in the agreement view, regarless of whether or not they exist in the Costume database yet. My main difficulty is that a costume does not belong to an agreement, they exist independently but can be added to an agreement. If a new costume is added that isn't in the Costume database, it will add it to the database. Is there a way to do this? I can't find a tutorial about this anywhere. If I could get the controller from this post, I think that is all I need. I just need to create one costume every time the form is displayed. Thanks so much.
My models are as follows:
# app/models/agreement.rb
class Agreement < ActiveRecord::Base
has_and_belongs_to_many :costumes, join_table: :agreement_costumes
accepts_nested_attributes_for :costumes, :reject_if => :all_blank
end
# app/models/costume.rb
class Costume < ActiveRecord::Base
has_and_belongs_to_many :agreements, join_table: :agreement_costumes
end
# app/models/agreement_costume.rb
class AgreementCostume < ActiveRecord::Base
belongs_to :agreement
belongs_to :costume
end
My agreement controller is as follows:
class AgreementsController < ApplicationController
before_action :set_agreement, only: [:show, :edit, :update, :destroy]
# Some methods ommitted
# GET /agreements/new
def new
#agreement = Agreement.new
#agreement.costumes.build
#costumes = Costume.all
end
private
# Use callbacks to share common setup or constraints between actions.
def set_agreement
#agreement = Agreement.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def agreement_params
params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid, :title, :start, :end, :due, :financer, :employee, :costumes_attributes => [:cid, :description, :wd, :back, :photo])
end
:end
And finally, the agreement view
<%= form_for(#agreement) do |f| %>
<% if #agreement.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#agreement.errors.count, "error") %> prohibited this agreement from being saved:</h2>
<ul>
<% #agreement.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<!-- Agreement fields omitted -->
<%= f.fields_for :costumes do |c| %>
<div class="field">
<%= c.label :cid %><br>
<%= c.number_field :cid %>
</div>
<div class="field">
<%= c.label :description %><br>
<%= c.text_field :description %>
</div>
<!-- etc with costume fields -->
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You need accepts_nested_attributes_for in your Agreement controller if you want to create new costumes there.
class Agreement < ActiveRecord::Base
has_many :agreement_costumes
has_many :costumes, through: :agreement_costumes
accepts_nested_attributes_for :costumes, :reject_if => :all_blank, :allow_destroy => :false,
end
and then in the agreements#new action in your Agreements controller you build a costume entry
def new
#agreement = Agreement.new
#agreement.costumes.build
#costumes = Costumes.all
end
#agreement.costumes.build creates a blank instance of a costume, related to this agreement. You then access the params of that costume in the form using :costumes Don't forget to whitelist your nested params in your Agreements controller:
def agreement_params
params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid,
:title, :start, :end, :due, :financer, :employee, costumes_attributes[:name, :price, :id])
end
Now your form has to have a place to choose existing costumes from a list and/or add a new one.
<%= f.label :costumes, "Costumes" %>
<%= f.collection_select :costumes, :agreement_id, :id, :name, price, {}, {multiple: true} %>
<strong>Add a new costume</strong>
<%= f.fields_for :costumes do |c|
<%= c.label :name %>
<%= c.text_field :name %>
<br>
<%= c.label :price %>
<%= c.number_field :price %>
<br>
<% end %>
This should get you most of the way there. I've had to guess at some of your code, so this isn't going to be a cut and paste answer. You'll need to build what you can off of this and probably do a little more Googling here and there. If you wanted to have a popup form to add a costume to the list on the fly and then be able to choose it in the collection_select, you would have to turn on Turbo_links in your app, create a Javascript popup form. Then use AJAX to submit the form, save the costume to the database, run another .js.erb file that would then update the collection_select text list using a reload of just that list via your Javascript. It's actually probably easier than having a new costume form nested in this form.

Rails: Undefined method create for nil class

I am creating a sample project but when I am trying to create a new post getting an error "undefined method create for nil class"
My code is as follows.
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :post, dependent: :destroy
end
post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
posts_controller.rb
class PostsController < ApplicationController
def create
#user = current_user
if #user.post.blank?
#post = #user.post.create(params[:post].permit(:title, :text))
end
redirect_to user_root_path
end
end
new.html.erb
<%= form_for([current_user, current_user.build_post]) do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
But after trying so many times i have made some changes and it started working but i dont know what is the difference between both the codes.
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :posts, dependent: :destroy
end
post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
posts_controller.rb
class PostsController < ApplicationController
def create
#user = current_user
if #user.posts.blank?
#post = #user.posts.create(params[:post].permit(:title, :text))
end
redirect_to user_root_path
end
end
new.html.erb
<%= form_for([current_user, current_user.posts.build]) do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
my routes.rb is
UserBlog::Application.routes.draw do
devise_for :users, controllers: { registrations: "registrations" }
resources :users do
resources :posts
end
# You can have the root of your site routed with "root"
root 'home#index'
end
Please help me and tell me what is the difference between both the codes ?
The difference is in the helper methods that are added allowing you to build or create a new association object. The approach is slightly different for a has_one compared to a has_many association.
For a has_one association the method to create a new associated object would be user.create_post.
For a has_many association the method to create a new associated object would be user.posts.create.

Rails 3 I18n label translation for nested_attributes in has_many relationship

Using: Rails 3.0.3, Ruby 1.9.2
Here's the relationship:
class Person < ActiveRecord::Base
has_many :contact_methods
accepts_nested_attributes_for :contact_methods
end
class ContactMethod < ActiveRecord::Base
attr_accessible :info
belongs_to :person
end
Now when I try to customize the contact_method labels in I18n, it doesn't recognize it.
en:
helpers:
label:
person[contact_methods_attributes]:
info: 'Custom label here'
I have also tried:
person[contact_method_attributes]
This works just fine for 1-1 relationships, e.g.
person[wife_attributes]:
name: 'My wife'
but not person[wives_attributes]
Thanks in advance
I did this with :
en:
helpers:
label:
person[contact_methods_attributes][0]:
info: 'First custom label here'
person[contact_methods_attributes][1]:
info: 'Second custom label here'
Which is nice but not ideal when you have unlimited options.. I would just specify a custom translation key in the form builder :)
en:
helpers:
label:
person[contact_methods_attributes][any]:
info: 'Custom label here'
<% fields_for :contact_methods do |builder| %>
<%= builder.label :info, t("helpers.person[contact_methods_attributes][any].info") %>
<%= builder.text_field :info %>
<% end %>
EDIT :
Don't know if it's a new feature but seems to work like a charm doing this :
en:
helpers:
label:
person:
contact_methods:
info: 'Custom label here'
In my Rails 3.2.13 app the attribute labels are picked up automatically from the model whose attributes are embedded. Please note that I am nesting attributes of the belongs_to model, but it might also work the other way around.
My example from working code:
The models:
class User < ActiveRecord::Base
belongs_to :account
accepts_nested_attributes_for :account
# ...
end
class Account < ActiveRecord::Base
has_many :users
end
The view:
<h2><%= t(:sign_up) %></h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<%= f.fields_for :account do |account_form| %>
<div><%= account_form.label :subdomain %><br />
<%= account_form.text_field :subdomain %>.<%= request.host %> <span class="hint"></span></div>
<% end %>
translations_de.yml:
activerecord:
models:
account: Konto
user: Benutzer
attributes:
account:
name: Firmenname
subdomain: Subdomain
users: Benutzer
user:
# ... no sign of subdomain here ...
And the view is rendered with the subdomain label translated based on
activerecord.attributes.account.subdomain
Nice. :)
I'm not sure but it might require you to use the activerecord path instead of the helpers one.

Resources