Insert data into multiple tables from one controller/view [Rails 4] - ruby-on-rails

I have more curious questions for all you amazing people!
I am creating a forum and when you create a topic, you are also creating the first post at the same time.
I need to assign variables to certain fields.
Example: :user_id => current_user.id,
I don't have the param settings correct, so many of the fields are NULL when stored in the database.
Models
class Topic < ActiveRecord::Base
belongs_to :forum
has_many :posts, :dependent => :destroy
belongs_to :user
accepts_nested_attributes_for :posts
end
class Post < ActiveRecord::Base
belongs_to :topic
belongs_to :user
end
Topics Controller
# GET /topics/new
def new
#topic = Topic.new
#topic.posts.build
end
def create
#topic = Topic.new(topic_params)
if #topic.save
##topic.responses = Post.new(params[:responses])
flash[:success] = "Topic Posted"
redirect_to "/forums/#{#topic.forum_id}"
else
render :new
end
end
def topic_params
# last_post_at = (:last_post_at => Time.now)
params.require(:topic).permit(
:name,
:description,
[:last_poster_id => current_user.id],
[:last_post_at => Time.now],
[:user_id => current_user.id],
:forum_id,
posts_attributes: [:id, :content, :topic_id, :user_id => current.user.id] )
end
Post Controller
# GET /posts/new
def new
#post = Post.new
end
def create
#post = Post.new(
:content => params[:post][:content],
:topic_id => params[:post][:topic_id],
:user_id => current_user.id)
if #post.save
#topic = Topic.find(#post.topic_id)
#topic.update_attributes(
:last_poster_id => current_user.id,
:last_post_at => Time.now)
flash[:notice] = "Successfully created post."
redirect_to "/topics/#{#post.topic_id}"
else
render :action => 'new'
end
end
_form for View/Topic
<%= form_for(#topic) do |f| %>
<% if params[:forum] %>
<input type="hidden"
id="topic_forum_id"
name="topic[forum_id]"
value="<%= params[:forum] %>" />
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :posts do |p| %>
<%= p.label :content %><br />
<%= p.text_area :content %>
<% end %>
<%= f.submit :class => "btn btn-primary" %>
<% end %>

You'll likely be looking for a function called:
accepts_nested_attributes_for
You put this into the model you're working with (in your case Post) and it will pass paeans for the nested model through to the corresponding controller
There is a good RailsCast about this and I've gr some experience with it too. If you want me to post working live code, let me know (I'm on my iPhone)
Live Code
Models
#app/models/image_post.rb
belongs_to :post, :class_name => 'Post'
belongs_to :image, :class_name => 'Image'
accepts_nested_attributes_for :image, :allow_destroy => true
#app/models/post.rb
has_many :images, -> { uniq }, :class_name => 'Image', :through => :images_posts, dependent: :destroy
has_many :images_posts, :class_name => 'ImagePost'
accepts_nested_attributes_for :images_posts, :allow_destroy => true
Controller
def new
#post = Post.new
#post.images_posts.build.build_image
end
def create
#Using Inherited Resources Gem
create!
end
private
def permitted_params
{:post => params.require(:post).permit(:title, :body, images_posts_attributes: [:caption, image_attributes: [:image]] )}
end
Form
<%= form_for [:admin, resource], :html => { :multipart => true } do |f| %>
<table class="resource_table">
<thead>
<th colspan="2"><%= params[:action].capitalize %> <%= resource_class %></th>
</thead>
<tbody class="form">
<% attributes.each do |attr| %>
<tr class="<%= cycle('odd', '')%>">
<td><%= resource_class.human_attribute_name(attr) %></td>
<td>
<% if attr == "body" %>
<%= f.text_area attr, :rows => 60, :cols => 80, :class => "redactor" %>
<% else %>
<%= f.text_field attr, :value => resource.public_send(attr).to_s %>
<% end %>
</td>
</tr>
<% end %>
<%= f.fields_for :images_posts do |images_posts| %>
<%= images_posts.fields_for :image do |images| %>
<tr>
<td>Image</td>
<td><%= images.file_field :image %></td>
</tr>
<% end %>
<tr>
<td>Caption</td>
<td><%= images_posts.text_field :caption %></td>
</tr>
<% end %>
<tr class="dull">
<td colspan="2"><%= f.submit "Go" %></td>
</tr>
</tbody>
</table>
<% end %>

Use accepts_nested_attributes
class topic
accepts_nested_attributes :posts
end
class post
accepts_nested_attributes :topic
end
Then in form you can use fields_for posts while creating topic form
Also in post form fileds_for for topic

You may want to fetch the post params as
params[:topic].fetch(:post_attributes, nil)
Rails 4 has been sanitized the mass-assignment to be called as strong_params
Example

Related

How to assign strong parameters of deeply-nested attibutes in Rails4?

I'm trying nested objects in a Rails4-app according to this Railscast. The model survey has_many :questions, the model questions in turn has_many :answers, the model answers belongs_to :questions and questions belongs_to :survey.
Now, the models were at first completely separated from one another which worked fine although I did not want it that way. I preferred them to be nested into one another so that I could assign and display the different objects at the same time.
I then had to figure out how to white-list strong parameters of these nested objects/attributes and there were good questions here that helped me with that.
When I create my database entries everything works fine. The problem comes when I want to edit the object in the database. In the log I get "Unpermitted parameter: answers" even though I've whitelisted every attribute including the ones for answers. I simply don't understand why.
Anybody who can point me in the right direction?
My surveys_controller:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
#surveys = Survey.all
end
def show
#survey = Survey.find(params[:id])
end
def new
#survey = Survey.new
3.times do
question = #survey.questions.build
4.times { question.answers.build }
end
end
def create
#survey = Survey.new(survey_params)
if #survey.save
flash[:notice] = 'Survey was successfully created.'
redirect_to(:action => 'index')
else
render('new')
end
end
def edit
#survey = Survey.find(params[:id])
end
def update
#Find an existing object using form parameters
#survey = Survey.find(params[:id])
#Update the object
if #survey.update_attributes(survey_params)
flash[:notice] = "Survey updated successfully."
#If update succeeds, redirect to 'show' action.
redirect_to(:action => 'show', :id => #survey.id)
else
#Else redisplay the 'edit' form.
render('edit')
end
end
def delete
#survey = Survey.find(params[:id])
end
def destroy
#survey = Survey.find(params[:id]).destroy
flash[:notice] = "Survey destroyed successfully."
redirect_to(:action => 'index')
end
private
def set_survey
#survey = Survey.find(params[:id])
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:survey_id, :id, :content, answers_attributes: [:id, :question_id, :correct_answer, :content]])
end
end
My survey.rb model:
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
EDIT: My question.rb-model:
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
my answer.rb-model
class Answer < ActiveRecord::Base
belongs_to :question
end
my /surveys/show.html.erb
<td><%= link_to('<< Back to list', {:action => 'index'}, :class => 'action_index') %></td>
<div class="survey show">
<h2><strong>Show survey:</strong></h2>
<table summary="Survey detail view">
<tr>
<th>Survey name: </th>
<td><%= h #survey.name %></td>
</tr>
<tr>
<th>Question: </th>
<td><% for question in #survey.questions do %>
<li><%= h question.content %></li>
<ul>
<% for answer in question.answers do %>
<li><%= h answer.content %></li>
<% end %>
</ul>
<% end %>
</td>
</tr>
<tr>
<th>Created_at: </th>
<td><%= #survey.created_at %></td>
</tr>
<tr>
<td><%= link_to('Edit', {:action => 'edit', :id => #survey.id }, :class => 'action_edit') %></td>
<td><%= link_to('Delete', {:action => 'destroy', :id => #survey.id }, :class => 'action_edit') %></td>
</tr>
</table>
</div>
My _form_for.html.erb
<%= form_for #survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<% end %>
<%= f.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
My _question_field.html.erb
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
</p>
My _answer_field.html.erb
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.radio_button :correct_answer, true %>
</p>
You have posted your Survey model twice instead of your Question model. Does your question model accept_nested_attributes_for :answers?
Assuming that you have and that I understand your structure correctly, your problem is most likely in your form - instead of f.fields_for :answers, you should have ff.fields_for :answers, nested within f.fields_for :questions, as this is a nested resource of Question and not Survey. So:
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<%= ff.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<% end %>

rails: mass-assign error caused by updating child (Post) from parent (Topic) controller (edit view)

I am trying to edit a Topic which has many Posts.
Edit page for a Topic has Topic's name and Post's content that can be edited.
The mass-assignment error occurs in topics_controller.rb, update method, post.update_attributes(params[:post]).
How do I avoid mass-assignment error.
topic.rb
class Topic < ActiveRecord::Base
has_many :posts, :dependent => :destroy
belongs_to :forum
accepts_nested_attributes_for :posts, :allow_destroy => true
attr_accessible :name, :last_post_id, :posts_attributes
end
post.rb
class Post < ActiveRecord::Base
belongs_to :topic
attr_accessible :content
end
topics_controller.rb
def update
#topic = Topic.find(params[:id])
post = #topic.posts.first
if #topic.update_attributes(params[:topic]) && post.update_attributes(params[:post])
topic = Topic.find(#post.topic_id)
flash[:success] = "Success!"
redirect_to topic_posts_path(topic)
else
render 'edit'
end
end
views/topics/edit.html.erb
<%= form_for #topic do |f| %>
<!-- render 'shared/error_messages_topic' -->
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for #topic.posts.first do |post| %>
<%= render :partial => "posts/form", :locals => {:f => post} %>
<% end %>
<%= f.submit "Edit", class: "btn btn-large btn-primary" %>
<% end %>
views/posts/_form.html.erb
<%= f.label :content %>
<%= f.text_area :content %>
In update method you don't have to update attributes of both the models instead of if #topic.update_attributes(params[:topic]) && post.update_attributes(params[:post]) it should this only if #topic.update_attributes(params[:topic]) it will update the posts automatically.
And change your view from this <%= f.fields_for #topic.posts.first do |post| %> to <%= f.fields_for :posts, #topic.posts.first do |post| %> it will work fine.
For more information read this http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for

Active Admin: Form with datepicker on custom page

Alright I made a custom page in Active Admin called "Newest Rooms" and it shows a table with the Hotel Rooms of the current date.
Now I want to add a form the this custom page where I can pick the Date. I've managed to make the form appear with the Datepicker through:
<%= semantic_form_for :newest_rooms, :builder => ActiveAdmin::FormBuilder do |f|
f.inputs do
f.input :Datum, :as => :datepicker
end
f.buttons
end %>
But no idea how to send this to the right controller and to the method HotelRoom.newest_rooms
I hope someone can explain to me how to do this. I've added the code below:
newest_room.rb
ActiveAdmin.register_page "Newest Rooms" do
menu :label => "Newest Rooms"
content do
render "newest_rooms"
end
end
_newest_room.html.erb
<% #cities = Hotel.cities %>
<%= semantic_form_for :newest_rooms, :builder => ActiveAdmin::FormBuilder do |f|
f.inputs do
f.input :Datum, :as => :datepicker
end
f.buttons
end %>
<ul class="room_list">
<% #cities.each do |c| %>
<li>
<table>
<tr>
<td>
<h2><%= c.City %></h2>
</td>
</tr>
<tr class="room_column">
<td>Hotel</td>
<td>Free Rooms</td>
<td>BN-Price</td>
<td>Old Price</td>
</tr>
<% #rooms = HotelRoom.newest_rooms(c.City) %>
<% #rooms.each do |r| %>
<tr>
<td><%= r.hotel.Hotelname %></td>
<td><%= r.FreeRooms %></td>
<td><b><%= r.Price %>€</b></td>
<td><%= r.OldPrice %>€</td>
</tr>
<%end%>
</table>
</li>
<% end %>
</ul>
hotel_room.rb
class HotelRoom < ActiveRecord::Base
validates :title, :presence => true
self.table_name = "hotel_room"
belongs_to :hotel, :foreign_key => 'H_ID'
accepts_nested_attributes_for :hotel
def to_key
[self.ID]
end
def self.newest_rooms(city)
HotelRoom.find(:all, :joins => :hotel, :conditions => ["hotel.City = ? and hotel_room.Date = ?", city, Date.today])
end
end
add an url to your semantic form, like...
<%= semantic_form_for :newest_rooms, :url => hotel_newest_room_path, :builder => ActiveAdmin::FormBuilder do |f| %>

Duplicate form fields when using fields_for

I am trying to create a nested form to handle a has_many :through relationship and getting duplicated fields being rendered.
Models
Company
has_many :provides
has_many :services, :through => :provides
accepts_nested_attributes_for :services, :provides
attr_accessible :service_ids
Provide
belongs_to :company
belongs_to :service
Service
has_many :provides
has_many :companies, :through => :provides
has_many :portfolio_items
acts_as_nested_set
Controller
Settings/Services
def index
#company_services = #company.services
#service_list = Service.where("parent_id IS NULL")
end
def show
#user = current_user
# Find features for supplier based users
unless #company.blank?
#my_sectors = #company.sectors
end
end
def update
if params[:company].nil?
#company.service_ids = nil
end
respond_to do |format|
if #company.update_attributes(params[:company])
format.html { redirect_to settings_path, :notice => "Services successfully updated" }
else
format.html { render :index }
end
end
end
Views
Form -
<%= form_for #company, :url => settings_service_path(#company), :method => :put do |f| %>
<div>
<ul>
<% #service_list.each do |item| %>
<%= f.fields_for :provides do |p| %>
<%= p.fields_for :services do |s| %>
<%= render :partial => "subs", :locals => {:subs => s, :service => item, :f => f, :p => p } %>
<% end %>
<% end %>
<% end %>
</ul>
<%= submit_tag("Update") %>
<% end %>
_subs.html.erb
<li>
<%= check_box_tag :service_ids, service.id, #company.services.include?(service), :name => "company[service_ids][]", :class => "checkbox" %>
<%= label_tag service.id, service.service_name %>
<% unless service.children.blank? %>
<ul>
<%= render :partial => "subs", :collection => service.children %>
</ul>
<% end %>
</li>
I know the fields_for is causing the duplication but I don't know why?
Could anybody clarify?
You need to write,
<% #service_list.each do |item| %>
<%= f.fields_for :provides do |p| %>
<%= p.fields_for :services, item do |s| %>
this way fields_for will iterate only for matching service each time.

Uninitialized constant Item::Types

I get this uninitialized constant error when i submit my nested forms.
order.rb
class Order < ActiveRecord::Base
has_many :items, :dependent => :destroy
has_many :types, :through => :items
accepts_nested_attributes_for :items
accepts_nested_attributes_for :types
validates_associated :items
validates_associated :types
end
item.rb
class Item < ActiveRecord::Base
has_one :types
belongs_to :order
accepts_nested_attributes_for :types
validates_associated :types
end
type.rb
class Type < ActiveRecord::Base
belongs_to :items
belongs_to :orders
end
new.erb.html
<% form_for #order do |f| %>
<%= f.error_messages %>
<% f.fields_for :items do |builder| %>
<table border="0">
<th>Type</th>
<th>Amount</th>
<th>Text</th>
<th>Price</th>
<tr>
<% f.fields_for :type do |m| %>
<td> <%= m.collection_select :type, Type.find(:all, :order => "created_at DESC"), :id, :name, {:prompt => "Select a Type" }, {:id => "selector", :onchange => "type_change(this)"} %> </td>
<% end %>
<td> <%= f.text_field :amount, :id => "amountField", :onchange => "change_total_price()" %> </td>
<td> <%= f.text_field :text, :id => "textField" %> </td>
<td> <%= f.text_field :price, :class => "priceField", :onChange => "change_total_price()" %> </td>
<td> <%= link_to_remove_fields "Remove Item", f %> </td>
</tr>
</table>
<% end %>
<p><%= link_to_add_fields "Add Item", f, :items %></p>
<p>
<%= f.label :total_price %><br />
<%= f.text_field :total_price, :class => "priceField", :id => "totalPrice" %>
</p>
<p><%= f.submit "Create"%></p>
<% end %>
<%= link_to 'Back', orders_path %>
create method in orders_controller.rb
def create
#order = Order.new(params[:order])
respond_to do |format|
if #order.save
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to(#order) }
format.xml { render :xml => #order, :status => :created,
:location => #order }
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors,
:status => :unprocessable_entity }
end
end
end
Hopefully you can see what i cant
You need to pay careful attention to pluralization in Rails. In this case you're setting up a singular relationship to a plural thing, so it's presumed that you're actually calling a class named "Types" and not "Type".
has_one, belongs_to are singular
has_many is plural
Possible fixes:
class Item < ActiveRecord::Base
has_one :type
belongs_to :order
accepts_nested_attributes_for :type
validates_associated :type
end
class Type < ActiveRecord::Base
belongs_to :item
belongs_to :order
end
In rails, type is a reserved word. You have to rename your model to something else. Also you have to follow tadman's instruction about singular names for has_one association.
Reference
Reserved words in rails

Resources