Scenario
I have got four resources in my Rails project, viz. Drive, Drivecomment, College and Manager
resources :drives do
resources :drivecomments
end
devise_for :colleges, controllers: { sessions: "college/sessions", registrations: "college/registrations" }
devise_for :managers, controllers: { sessions: "manager/sessions", registrations: "manager/registrations" }
The association between them are as follows:
Manager => belongs_to :college
College => has_many :managers
College => has_many :drives
Drives => belongs_to :college
Drives => has_many :drivecomments
Drivecomments => belongs_to :drives
As it is clear from above association, each Drive can have multiple Drivecomments, which can be submitted by the Managers.
Also, I have created a custom Controller#View resource "m" for providing a Manager Interface (coz default manager is generated from devise and using the same resource means all post operations get redirected to "create" method, including sign_up, which eventually means i cannot sign_up at all)
resources :m, :only => [:index] do
member do
get :drive
end
end
So, inside my "m", I have created a form for posting a comment as well as a table for displaying all comments.
FOR DISPLAYING COMMENTS:
../drive.html.erb
<h2>Drive Comments</h2>
<table>
<thead>
<tr>
<th>Jobseeker</th>
<th>Comment</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #comments.each do |comment| %>
<tr>
<td><%= comment.jobseeker.first_name %></td>
<td><%= comment.title %></td>
</tr>
<% end %>
</tbody>
</table>
../m_controller.erb
def drive
#drive = #drives.find_by_id(params[:id])
#comments = #drive.drivecomments
#addcomment = Drivecomment.new
end
FOR ADDING A COMMENT:
../drive.html.erb
<%= form_for [#addcomment], as: :addcomment, url: {action: "addcomment"} do |f| %>
<div><%= f.hidden_field :drive_id, :value => #drive.id %></div>
<div><%= f.hidden_field :jobseeker_id, :value => 2 %></div>
<div><%= f.label "Comment:" %><%= f.text_field :title %></div>
<div><%= f.submit "Post Comment" %></div>
<% end %>
../m_controller.erb
def new
#addcomment = Drivecomment.new
end
def create
#drive = #drives.find_by_id(params[:id])
#addcomment = #drive.drivecomments.build
#addcomment.save
end
Problem
When I submit my form, I get an error saying
ActionController::UrlGenerationError in M#drive
Showing /home/xxx/xxx/xxx/app/views/m/drive.html.erb where line #30 raised:
No route matches {:action=>"addcomment", :controller=>"m", :id=>"5"}
Extracted source (around line #30):
</table>
<%= form_for [#addcomment], as: :addcomment, url: {action: "addcomment"} do |f| %>
<div><%= f.hidden_field :drive_id, :value => #drive.id %></div>
<div><%= f.hidden_field :jobseeker_id, :value => 2 %></div>
Alternatively, If I write my form like this
<%= form_for [#drive, #addcomment] do |f| %>
<div><%= f.hidden_field :drive_id, :value => #drive.id %></div>
<div><%= f.hidden_field :jobseeker_id, :value => 2 %></div>
<div><%= f.label "Comment:" %><%= f.text_field :title %></div>
<div><%= f.submit "Post Comment" %></div>
<% end %>
it submits the form successfully and a value gets added, but I get redirected to "http://localhost:3000/drives/5/drivecomments/8" from "http://localhost:3000/m/5/drive" which I don't want.
Please tell what might be the workaround for this problem.
Additional Info
Drivecomments _form.html.erb
<%= form_for([#drive, #drivecomment]) do |f| %>
<% if #drivecomment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#drivecomment.errors.count, "error") %> prohibited this drivecomment from being saved:</h2>
<ul>
<% #drivecomment.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :jobseeker_id %><br>
<%= f.number_field :jobseeker_id %>
</div>
<div class="field">
<%= f.label :drive_id %><br>
<%= f.number_field :drive_id %>
</div>
<div class="field">
<%= f.label :title %><br>
<%= f.text_area :title %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Extract from drivecoments_controller.rb
before_action :set_drivecomment, only: [:show, :edit, :update, :destroy]
before_filter :load_drive
def new
#drivecomment = #drive.drivecomments.new
end
def create
#drivecomment = #drive.drivecomments.new(drivecomment_params)
respond_to do |format|
if #drivecomment.save
format.html { redirect_to [#drive, #drivecomment], notice: 'Drivecomment was successfully created.' }
format.json { render :show, status: :created, location: #drivecomment }
else
format.html { render :new }
format.json { render json: #drivecomment.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_drivecomment
#drivecomment = Drivecomment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def drivecomment_params
params.require(:drivecomment).permit(:jobseeker_id, :drive_id, :title)
end
def load_drive
#drive = Drive.find(params[:drive_id])
end
If you want to user the action create from the controller m you should define your form like this:
<%= form_for #addcomment, url: url_for(controller: :m, action: :create) do |f| %>
Also, you need to define the create action on your routes, update this piece of your routes:
resources :m, :only => [:index, :create]
Related
Alright, been looking everywhere for this one. Tried all the solutions. Maybe someone can help on this.
So, I have a WebRequest model that has many WebSites. Each WebSite belongs to a WebRequest. My problem is in the nested form. Ive gone in an permitted the params (atleast, based on the documentation I have) and everything works fine until I go into the server logs. Posted below
class WebRequest < ActiveRecord::Base
has_many :web_sites
accepts_nested_attributes_for :web_sites
end
and here is the WebSite model
class WebSite < ActiveRecord::Base
belongs_to :web_request, dependent: :destroy
end
_form.html.erb
<% 1.times do %>
<%= f.fields_for :web_site do |ff| %>
<%= ff.input :url %>
<%= ff.input :description %>
<% end %>
<% end %>
WebRequests Controller
class WebRequestsController < ApplicationController
def new
#web_request = WebRequest.new
# #web_request.web_sites.build
end
def index
#web_requests = WebRequest.all
end
def create
#web_request = WebRequest.new(web_request_params)
respond_to do |format|
if #web_request.save
RequestMailer.web_request_submit(#web_request).deliver
format.html { render partial: 'success' }
format.json { render action: 'show', status: :created, location: #web_request }
else
format.html { render action: 'new' }
format.json { render json: #web_request.errors, status: :unprocessable_entity }
end
end
end
def web_request_params
params.require(:web_request).permit(:web_needs, :primary_goal, :secondary_goal, : :call_to_action, :hero_image, :image_count, :existing, :resources, :web_examples, :special_functions, :social_network, web_sites_attributes: [:id, :url, :description])
end
end
And here is the server log:
Started POST "/web_requests" for 127.0.0.1 at 2014-07-10 15:56:12 -0400
Processing by WebRequestsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0iisNgGk/AhC4jRrp1cmKWVCBsCcSx5G2dueEI/+p2A=", "web_request"=>{"web_needs"=>"", "primary_goal"=>"", "secondary_goal"=>"", "call_to_action"=>"", "hero_image"=>"", "image_count"=>"", "existing"=>"", "web_site"=>{"url"=>"TROLL", "description"=>"TROLL"}, "resources"=>"", "special_functions"=>"", "social_network"=>""}, "commit"=>"Create Web request"}
Unpermitted parameters: web_site
Notice at how the form fields get passed but they get restricted out of making it to the DB.
THANKS!
Update::
Here is the full form path:
<%= simple_form_for(#web_request) do |f| %>
<% if #web_request.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#web_request.errors.count, "error") %>
prohibited this Web Request from being saved:</h2>
<ul>
<% #web_request.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="container">
<div class="field">
<%= f.input :web_needs, label: 'What web needs do you have?' %>
</div>
<div class="container"><h5>Please list below 5 URL's and explanations of why you like them.</h5></div>
<% 1.times do %>
<%= f.fields_for :web_sites do |ff| %>
<div class="field">
<%= ff.input :url, label: "URL of example site" %>
</div>
<div class="field">
<%= ff.input :description, label: "Description of example site" %>
</div>
<% end %>
<% end %>
<div class="field">
<%= f.input :resources, label: 'Will you be providing any kind of resource on this page? e.g. chord chart download.' %></br>
</div>
</div>
<div class="actions">
<%= f.button :submit, :class => "button" %>
</div>
</div>
<% end %>
Update::Full error log
undefined method `url' for #<ActiveRecord::Associations::CollectionProxy []>
The line is here
<% 1.times do %>
<%= f.simple_fields_for :web_site, #web_request.web_sites do |ff| %>
<div class="field">
<%= ff.input :url, label: "URL of example site" %> <--ERROR HERE on ':url'
</div>
<div class="field">
<%= ff.input :description, label: "Description of example site" %>
</div>
<% end %>
Add to controller in new action:
def new
#web_request = WebRequest.new
#web_site = #web_request.web_sites.build
end
and form:
<%= f.simple_fields_for #web_site do |ff| %>
<%= ff.input :url %>
<%= ff.input :description %>
<% end %>
<% end %>
and web_request_params:
def web_request_params
params.require(:web_request).permit(:web_needs,
:primary_goal,
:secondary_goal,
:call_to_action,
:hero_image,
:image_count,
:existing,
:resources,
:web_examples,
:special_functions,
:social_network,
{ web_sites: [:id, :url, :description] })
end
In your controller in web_request_params change web_sites_attributes for web_site_attributes
In your controller remove the comment from #web_request.web_sites.build
In your view remove 1.times do
In yor form change f.fields_for :web_sites do |ff| with f.simple_fields_for :web_sites do |ff|
I'm new to Rails and struggling to get my belongs_to association right. I have an app where a painting belongs to an artist and an artist can have_many paintings. I can create and edit my paintings, however I can not edit or create artists except through the console. Through much Googling I feel I have got myself turned around. Any help would be much appreciated!
Here's my routes.rb file:
MuseumApp::Application.routes.draw do
resources :paintings
resources :paintings do
resources :artists
resources :museums
end
root 'paintings#index'
end
Here's my paintings Controller
def show
#painting = Painting.find params[:id]
end
def new
#painting = Painting.new
##artist = Artist.new
end
def create
safe_painting_params = params.require(:painting).permit(:title, :image)
#painting = Painting.new safe_painting_params
if #painting.save
redirect_to #painting
else
render :new
end
end
def destroy
#painting = Painting.find(params[:id])
#painting.destroy
redirect_to action: :index
end
def edit
#painting = Painting.find(params[:id])
end
def update
#painting = Painting.find(params[:id])
if #painting.update_attributes(params[:painting].permit(:title, :image)) #safe_params
redirect_to #painting
else
render :edit
end
end
Here's the form in my paintings view:
<%= form_for(#painting) do |f| %>
<fieldset>
<legend>painting</legend>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :image %>
<%= f.text_field :image %>
</div>
<%= form_for([#painting,#painting.create_artist]) do |f| %>
<div>
<%= f.label :Artist %>
<%= f.text_field :name %>
</div>
</fieldset>
<%= f.submit %>
<% end %>
<% end %>
Artists Controller:
class ArtistsController < ApplicationController
def index
#artists = Artist.all
#artists = params[:q] ? Artist.search_for(params[:q]) : Artist.all
end
def show
#artist = Artist.find params[:id]
end
def new
#artist = Artist.new
end
def create
#painting = Painting.find(params[:painting_id])
#artist = #painting.create_artist(artist_params)
redirect_to painting_path(#painting)
end
def destroy
#artist = Artist.find(params[:id])
#Artist.destroy
redirect_to action: :index
end
def edit
#artist = Artist.find(params[:id])
end
def update
#painting = Painting.find(params[:painting_id])
#artist = #artist.update_attributes(artist_params)
redirect_to painting_path(#painting)
end
end
private
def artist_params
params.require(:artist).permit(:name)
end
Index view:
<h1> Hello and Welcome to Museum App</h1>
<h3><%= link_to "+ Add To Your Collection", new_painting_artist_path %></h3>
<%= form_tag '/', method: :get do %>
<%= search_field_tag :q, params[:q] %>
<%= submit_tag "Search" %>
<% end %>
<br>
<div id="paintings">
<ul>
<% #paintings.each do |painting| %>
<li><%= link_to painting.title, {action: :show, id:painting.id} %> by <%= painting.artist_name %></li>
<div id = "img">
<br><%= link_to (image_tag painting.image), painting.image %><br>
</div>
<%= link_to "Edit", edit_painting_path(id: painting.id) %>
||
<%= link_to 'Destroy', {action: :destroy, id: painting.id},method: :delete, data: {confirm: 'Are you sure?'} %>
<% end %>
</ul>
</div>
In your case you should use accepts_nested_attributes_for and fields_for to achieve this.
Artist
has_many :paintings, :dependent => :destroy
accepts_nested_attributes_for :paintings
Painting
belongs_to :artist
And also you should try creating artist with paintings like this
form_for(#artist) do |f| %>
<fieldset>
<legend>Artist</legend>
<%= f.label :Artist %>
<%= f.text_field :name %>
<%= fields_for :paintings, #artist.paintings do |artist_paintings| %>
<%= artist_paintings.label :title %>
<%= artist_paintings.text_field :title %>
<%= artist_paintings.label :image %>
<%= artsist_paintings.text_field :image %>
</fieldset>
<%= f.submit %>
<% end %>
Note:
You should be having your Artist Controller with at least new,create,edit and update methods defined in it to achieve this.
Edit
Try the reverse
Artist
has_many :paintings, :dependent => :destroy
Painting
belongs_to :artist
accepts_nested_attributes_for :paintings
form_for(#painting) do |f| %>
<fieldset>
<legend>Painting</legend>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :image %>
<%= f.text_field :image %>
<%= fields_for :artists, #painting.artists do |ff| %>
<%= ff.label :Artist %>
<%= ff.text_field :name %>
</fieldset>
<%= f.submit %>
<% end %>
Put this form in paintings views.
I have a nested form with the parent model built with devise and the child model without devise. I'm working on the edit form which has the user model fields and nested inside them expert model fields. The update/save works but logs me out and gives me an error saying "You need to sign in or sign up before continuing"
When I sign back in I see that the fields have been updated correctly. I would like it to not sign me out and show me the updated Edit page upon submitting the form.
User model - built with devise.
class User < ActiveRecord::Base
// some stuff
has_one :expert
accepts_nested_attributes_for :expert, :update_only => true
// more stuff
Expert model - built withOUT devise.
class Expert < ActiveRecord::Base
belongs_to :user
User controller:
before_action :set_user, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!, :only => [:show, :edit]
def edit
#user = User.friendly.find(params[:id])
#title = "Edit Profile"
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:id, :name, :email, :phone, :password, :role, :expert_attributes => [:id, :Location])
end
end
Expert controller:
def edit
#expert = Expert.find(params[:id])
#title = "Edit Expert Profile"
end
Edit user view form partial:
<%= form_for(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :Name %><br>
<%= f.text_field :name, :class => "form-control" %>
</div>
<div class="field">
<%= f.label :Email %><br>
<%= f.text_field :email, :class => "form-control" %>
</div>
<div class="field">
<%= f.label :Phone %><br>
<%= f.text_field :phone, :class => "form-control" %>
</div>
<div class="field">
<%= f.label :Password %><br>
<%= f.text_field :password, :class => "form-control" %>
</div>
<div class="field">
<%= f.fields_for :expert do |e| %>
<%= e.label :Location %><br />
<%= e.text_field :Location %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
In your UsersController, you should add :update to the authenticate_user! filter:
before_filter :authenticate_user!, :only => [:show, :edit, :update]
I'm trying to add a custom create action for my Book model, but i keep ending up with a "Couldn't find Book without an ID".
routes.rb:
Books::Application.routes.draw do
resources :books
resources :books do
collection do
post 'create_new_record', :action => :create_new_record
end
end
match 'create_new_record' => 'books#create_new_record', via: [:post]
The relevant controller action:
def create_new_record
#book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'New book record created.' }
end
end
end
And my form (in new.html.erb). I'm looping through results that i get from goodreads.com.
<% #book_search.results.work.each do |stuff| %>
<%= form_for(#book, :url => create_new_record_books_path) do |f| %>
<div class="field">
<%= f.label :author %><br>
<%= f.text_field :author, :value => stuff.best_book.author.name %>
</div>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title, :value => stuff.best_book.title %>
</div>
<div class="field">
<%= f.label :isbn %><br>
<%= f.text_field :isbn, :value => stuff.best_book.isbn %>
</div>
<div class="field">
<%= f.label :image %><br>
<%= f.text_field :image, :value => stuff.best_book.image_url %>
</div>
<div class="field">
<%= f.label :bookid %><br>
<%= f.text_field :bookid, :value => stuff.best_book.id %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<hr>
<% end %>
The error i get when submitting the form is:
ActiveRecord::RecordNotFound in BooksController#create_new_record
on the callback
def set_book
#book = Book.find(params[:id])
end
I'm pretty much stumped now, my understanding is that it doesn't even reach the action, but instead looks for a book id that doesn't exist?
Thank you!
If you use before_filter so you don't pass an id to create action. Call your before filter the following way:
before_filter :set_book, except: [:index, :new, :create]
If you use model callback, params is unavailable in the model so pass the id some other way, for example via attr_accessor.
use #book = Book.where(id: params[:id]).first
I am working on getting multiple file uploads working for an model in my application, I have included the code below:
delivers_controller.rb
# POST /delivers
def create
#deliver = Deliver.new(params[:deliver])
process_file_uploads(#deliver)
if #deliver.save
flash[:notice] = 'Task was successfully created.'
redirect_to(#deliver)
else
render :action => "new"
end
end
protected
def process_file_uploads(deliver)
i = 0
while params[:attachment]['file_'+i.to_s] != "" && !params[:attachment]['file_'+i.to_s].nil?
deliver.assets.build(:data => params[:attachment]['file_'+i.to_s])
i += 1
end
end
deliver.rb
has_many :assets, :as => :attachable, :dependent => :destroy
validate :validate_attachments
Max_Attachments = 5
Max_Attachment_Size = 5.megabyte
def validate_attachments
errors.add_to_base("Too many attachments - maximum is #{Max_Attachments}") if assets.length > Max_Attachments
assets.each {|a| errors.add_to_base("#{a.name} is over #{Max_Attachment_Size/1.megabyte}MB") if a.file_size > Max_Attachment_Size}
end
assets_controller.rb
class AssetsController < ApplicationController
def show
asset = Asset.find(params[:id])
# do security check here
send_file asset.data.path, :type => asset.data_content_type
end
def destroy
asset = Asset.find(params[:id])
#asset_id = asset.id.to_s
#allowed = Deliver::Max_Attachments - asset.attachable.assets.count
asset.destroy
end
end
asset.rb
class Asset < ActiveRecord::Base
has_attached_file :data,
belongs_to :attachable, :polymorphic => true
def url(*args)
data.url(*args)
end
def name
data_file_name
end
def content_type
data_content_type
end
def file_size
data_file_size
end
end
Whenever I create a new deliver item and try to attach any files I get the following error:
NoMethodError in DeliversController#create
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
/Users/danny/Dropbox/SVN/railsapps/macandco/surveymanager/trunk/app/controllers/delivers_controller.rb:60:in `process_file_uploads'
/Users/danny/Dropbox/SVN/railsapps/macandco/surveymanager/trunk/app/controllers/delivers_controller.rb:46:in `create'
new.html.erb (Deliver view)
<% content_for :header do -%>
Deliver Repositories
<% end -%>
<% form_for(#deliver, :html => { :multipart => true }) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :caseref %><br />
<%= f.text_field :caseref %>
</p>
<p>
<%= f.label :casesubject %><br />
<%= f.text_area :casesubject %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description %>
</p>
<p>Pending Attachments: (Max of <%= Deliver::Max_Attachments %> each under <%= Deliver::Max_Attachment_Size/1.megabyte%>MB)
<% if #deliver.assets.count >= Deliver::Max_Attachments %>
<input id="newfile_data" type="file" disabled />
<% else %>
<input id="newfile_data" type="file" />
<% end %>
<div id="attachment_list"><ul id="pending_files"></ul></div>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', delivers_path %>
Show.html.erb (Delivers view)
<% content_for :header do -%>
Deliver Repositories
<% end -%>
<p>
<b>Title:</b>
<%=h #deliver.caseref %>
</p>
<p>
<b>Body:</b>
<%=h #deliver.casesubject %>
</p>
<p><b>Attached Files:</b><div id="attachment_list"><%= render :partial => "attachment", :collection => #deliver.assets %></div></p>
<%= link_to 'Edit', edit_deliver_path(#deliver) %> | <%= link_to 'Back', deliver_path %>
<%- if logged_in? %>
<%= link_to 'Edit', edit_deliver_path(#deliver) %> |
<%= link_to 'Back', delivers_path %>
<% end %>
_attachment.html.erb (Delivers view)
<% if !attachment.id.nil? %><li id='attachment_<%=attachment.id %>'><a href='<%=attachment.url %>'><%=attachment.name %></a> (<%=attachment.file_size/1.kilobyte %>KB)
<%= link_to_remote "Remove", :url => asset_path(:id => attachment), :method => :delete, :html => { :title => "Remove this attachment", :id => "remove" } %></li>
<% end %>
I have been banging my head against the wall with the error all day, if anyone can shed some light on it, I would be eternally grateful!
Thanks,
Danny
The error message indicates that params[:attachment] is nil inside process_file_uploads(), which causes params[:attachment]['file_'+i.to_s] to raise an exception.
This happens because there's no field named attachment in the form in new.html.erb.