Rails: form with :url specified can't be used as partial? - ruby-on-rails

Hi I'm trying to use a form as a partial for both the new/edit views of my albums controller/model. However, it's giving me the error when I try to edit the album:
No route matches [PUT] "/users/22/albums"
I think this might have to do with my form's :url. My form works fine when I submit it to create an album, but gets that error when I try to edit it.
I tried taking out the url: user_albums_path in my form, but it would just give me an error when I try to create a new album.
No route matches [POST] "/albums"
Is there any way to make it so that the form works for both actions? I feel like the :url can't coexist properly in both actions.
_form.html.erb
<%= form_for (#album), url: user_albums_path, :html => { :id => "uploadform", :multipart => true } do |f| %>
<div class="formholder">
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<br>
<%=f.submit %>
</div>
<% end %>
albums controller
class AlbumsController < ApplicationController
def index
#user = User.find(params[:user_id])
#albums = #user.albums.all
respond_to do |format|
format.html
format.json { render json: #albums }
end
end
def show
#user = User.find(params[:user_id])
#album = #user.albums.find(params[:id])
end
def update
#user = User.find(params[:user_id])
#album = #user.albums.find(params[:id])
respond_to do |format|
if #album.update_attributes(params[:album])
format.html { redirect_to user_album_path(#user, #album), notice: 'Album successfully updated' }
else
format.html { render 'edit' }
end
end
end
def edit
#user = User.find(params[:user_id])
#album = #user.albums.find(params[:id])
end
def create
#user = User.find(params[:user_id])
#album = #user.albums.build(params[:album])
respond_to do |format|
if #user.save
format.html { redirect_to user_album_path(#user, #album), notice: 'Album was successfully created.' }
format.json { render json: #album, status: :created, location: #album}
else
format.html { render action: "new" }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end
def new
#user = User.find(params[:user_id])
#album = Album.new
end
def destroy
end
end
please help!
update:
FIXED IT! on my own! the form just needed to be <%= form_for([#user, #album])...

Try
<%= form_for [#user, #album] %>
# other arguments can be inserted before the closing brace
That syntax properly scopes the resource routes
Remember to have an #album instance variable if you need one. You can build an empty album using #user.album.build

Related

Redirect to edit page automatically if record exists

I want to create a new record if it not exist. Otherwise, redirect to edit page automatically.
In case the record exists (e.g: #sale is not nil). However, I got error message:
app/views/sales/_form.html.erb where line #1 raised:
First argument in form cannot contain nil or be empty
Please show place where I had mistake.
sales_controller.rb
def new
#sale = Sale.find_on_today
if #sale.nil?
#sale = Sale.new
else
redirect_to edit_sale_path(#sale)
end
end
def edit
end
# POST /sales
def create
Kpi.transaction do
#sale = Sale.new(sale_params)
#sale.user_id = current_user.id
respond_to do |format|
if #sale.save
format.html { redirect_to sales_path, notice: 'Sale was successfully created.' }
else
format.html { render :new }
end
end
end
end
# PATCH/PUT /sales/1
def update
respond_to do |format|
if #sale.update(sale_params)
format.html { redirect_to sales_path, notice: 'Sale was successfully updated.' }
else
format.html { render :edit }
end
end
end
private
def sale_params
params.require(:sale).permit(:sale_money, :no_of_items)
end
_form.html.erb
<%= form_for #sale do |f| %>
<label for="sale_money"><%= t('.sale_money_label')%></label>
<%= f.text_field :sale_money, class: "form-control" %>
<label for="no_of_items"><%= t('.no_of_items_label')%></label>
<%= f.text_field :sale_amount, class: "form-control" %>
<%= f.submit t('.btn_submit'), class: 'btn btn-primary' %>
<% end %>
UPDATE: Based on fylooi's answer, I forget redirect is a new request, so #sale is nil. I change method new, edit and update in controller, it is working
sales_controller.rb
def new
#sale = Sale.find_on_today
if #sale.nil?
#sale = Sale.new
else
redirect_to edit_sale_path(#sale.id)
end
end
def edit
#sale = Sale.find(params[:id])
end
def update
#sale = Sale.find(params[:id])
respond_to do |format|
if #sale.update(kpi_params)
format.html { redirect_to kpis_path, notice: 'Sale was successfully updated.' }
else
format.html { render :edit }
end
end
end
edit.html.erb
<%= render partial: 'form' %>
A redirect is a fresh request to Rails. You need to set #sale again in the edit action as it is not persisted from update.
Alternatively, you can render the edit view directly from update if the update fails. That will preserve the instance variables from update.

Show username when posting comments

I would like to be able to display the username of the person who commented on a post. I am having some difficulty displaying it. As the code stands right now I am getting an undefined method `user' for nil:NilClass on the view for line: <%= #comment.user.name %>. Thank you in advance again.
Comments Controller
class CommentsController < ApplicationController
before_filter :authenticate_user!
def new
#post = Post.find(params[:post_id])
#comment = Comment.new
end
def create
#post = Post.find_by_id(params[:post_id])
#comment = #post.comments.create(params.require(:comment).permit(:commenter, :body))
#comment.user = current_user
respond_to do |format|
if #comment.save
format.html { redirect_to #post, notice: 'Comment was successfully created.' }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
def show
#comment = Comment.new
end
private
def comment_params
params.require(:comments).permit(:commenter, :body)
end
end*
View / _comments
<%= div_for(comment) do %>
<p>
<strong>Posted <%= time_ago_in_words(comment.created_at) %> ago</strong></br>
<%= h(comment.body) %>
<%= #comment.user.name %>
<%= link_to 'Delete', [#post, comment], :method => :delete, :confirm => "Are you sure?" %>
</p>
<% end %>
User Model
has_many :comments
Comment Model
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
In your comments controller your should your create method should look like this:
def create
#comment = current_user.comments.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
Then in your view/_comments file you should have something like this:
Added:<%= time_ago_in_words(comment.created_at) %> ago<br>
Added by: <%= current_user.email %>
Let me know if this helps
With the Rails / REST convention, the show action is responsible for rendering a representation of a specific resource instance. To do that it takes an id from params, looks up resource instance by the id, and renders a response. Your show action should load an existing comment, rather than creating a new comment. A new comment won't have a user assigned, which is why you are getting an error for calling a method on a nil object - the user for the new comment has not been set.
In your show action try the following:
#commment = Comment.find params[:id]
<%= #comment.user.name %> should be <%= comment.user.name %> (no # before comment), because that view/partial is using a local variable at that point, not an instance variable.
Just a tip for your question: you should give a little more context for the view snippet. Is it a full view, a part of a view, or a partial? What's its filename/which controller action does it map to? If it's a partial, which view is it being rendered from?
I was able to resolve this. <%= current_user.name %> needed to be in my _comment view and I removed #comment.user = current_user from my Comments Controller. Thank you all for your help.
View / _comments.html
<%= div_for(comment) do %>
<p>
<strong>Posted <%= time_ago_in_words(comment.created_at) %> ago</strong></br>
<%= h(comment.body) %>
<%= current_user.name %>
<%= link_to 'Delete', [#post, comment], :method => :delete, :confirm => "Are you sure?" %>
</p>
<% end %>
Comment Controller
class CommentsController < ApplicationController
before_filter :authenticate_user!
def new
#post = Post.find(params[:post_id])
#comment = Comment.new
end
def create
#post = Post.find_by_id(params[:post_id])
#comment = #post.comments.create(params.require(:comment).permit(:commenter, :body))
respond_to do |format|
if #comment.save
format.html { redirect_to #post, notice: 'Comment was successfully created.' }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
def show
#commment = Comment.find params[:id]
end

When a Post comment is made, no more can be created, only shown

I am trying to get a post and comment system working, for this however i want only one comment to be made per post. Only as i am trying to create a system as where content will be displayed followed by a comment 7 times in one post... Example...
program model 1 body content 1
Commentmodel1
program model 1 body content 2
Commentmodel2
program model 1 body content 3
Commentmodel3
.etc.etc.
For Me this is the simplest way of being able todo this by creating 7 different comment models, i know there is probably an easier way but as im new this seems the simplest. However i am struggling getting the one comment model to only allow just one comment to be made.
In this application coach is the user.
Here are the files involved, For the Models, program is the basic Post model, and comments is comments.
programs/Show.html.erb
<p id="notice"><%= notice %></p>
<p>
<b>Title:</b><br />
<%= #program.title %>
</p>
<p>
<b>Body:</b><br />
<%= #program.cweekcomments %>
</p>
<%= link_to 'Edit', edit_program_path(#program) %> |
<%= link_to 'Back', programs_path %>
<br /><br /><br />
<i>Comments</i>
<% #program.comments.each do |comment| %>
<p>
<b>Comment:</b>
<% if comment %>
<%= comment.body %>
<br />
<%= link_to 'Edit', edit_program_comment_path(#program, comment) %> | <%= link_to 'Destroy', [#program, comment] , method: :delete, data: { confirm: 'Are you sure?' } %>
<% else %>
<%= form_for([#program, #program.comments.build]) do |f| %>
<div class="field">
<%= f.label :body %><br />
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
</p>
<% end %>
Programs_controller.rb
class ProgramsController < ApplicationController
before_filter :authenticate_coach!, :except => [:show]
# GET /programs
# GET /programs.json
def index
#programs = Program.find_all_by_coach_id(current_coach[:id])
respond_to do |format|
format.html # index.html.erb
format.json { render json: #programs }
end
end
# GET /programs/1
# GET /programs/1.json
def show
#program = Program.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #program }
end
end
# GET /programs/new
# GET /programs/new.json
def new
#program = Program.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #program }
end
end
# GET /programs/1/edit
def edit
#program = Program.find(params[:id])
end
# POST /programs
# POST /programs.json
def create
#program = Program.new(params[:program])
respond_to do |format|
if #program.save
format.html { redirect_to #program, notice: 'Program was successfully created.' }
format.json { render json: #program, status: :created, location: #program }
else
format.html { render action: "new" }
format.json { render json: #program.errors, status: :unprocessable_entity }
end
end
end
# PUT /programs/1
# PUT /programs/1.json
def update
#program = Program.find(params[:id])
respond_to do |format|
if #program.update_attributes(params[:program])
format.html { redirect_to #program, notice: 'Program was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #program.errors, status: :unprocessable_entity }
end
end
end
# DELETE /programs/1
# DELETE /programs/1.json
def destroy
#program = Program.find(params[:id])
#program.destroy
respond_to do |format|
format.html { redirect_to programs_url }
format.json { head :no_content }
end
end
end
Comments_controller.rb
class CommentsController < ApplicationController
def new
#comment = #program.comments.build
end
def create
#program = Program.find(params[:program_id])
#comment = #program.comments.create(params[:comment])
redirect_to program_path(#program)
end
def destroy
#program = Program.find(params[:program_id])
#comment = #program.comments.find(params[:id])
#comment.destroy
redirect_to program_path(#program)
end
def edit
#program = Program.find(params[:program_id])
#comment = #program.comments.find(params[:id])
end
def update
#program = Program.find(params[:program_id])
#comment = #program.comments.find(params[:id])
respond_to do |format|
#if #program.comments.update_attributes(params[:comment])
if #comment.update_attributes(params[:comment])
format.html { redirect_to program_path(#program), notice: 'Comment was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
end
In advance, thanks for your help, much appreciated!
Change your program comment relation to has_one.(has_one :comment in your program.rb)
def create
#program = Program.find(params[:program_id])
if #program.comment
flash[:error] = "Cannot comment more than once"
else
#comment = #program.comments.create(params[:comment])
flash[:notice] = "Comment created"
end
redirect_to program_path(#program)
end

Rails: ActiveRecord::RecordNotFound in AlbumsController#create Couldn't find User without an ID

Hi I'm currently receiving an error in my controller on submitting a form for creating an album. This is my first project and I am pretty shaky on controllers and what params to put and what to set the instance variables as... please help!!
on submission of my new.html.erb form, I receive
ActiveRecord::RecordNotFound in AlbumsController#create
Couldn't find User without an ID
here is my albums_controller
class AlbumsController < ApplicationController
def index
#albums = Albums.all
respond_to do |format|
format.html
format.json { render json: #albums }
end
end
def show
#albums = Album.all
#album = Album.find(params[:id])
#photo = Photo.new
end
def update
end
def edit
end
def create
#user = User.find(params[:user_id])
#user.album = Album.new(params[:album])
respond_to do |format|
if #user.album.save
format.html { redirect_to #user, notice: 'Album was successfully created.' }
format.json { render json: #album, status: :created, location: #album}
else
format.html { render action: "new" }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end
def new
#user = User.find(params[:user_id])
#album = Album.new
end
def destroy
end
end
here is the form I'm submitting
<%= form_for (#album), :html => { :id => "uploadform", :multipart => true } do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<br>
<%=f.submit %>
Let me know if you need any more files.
In your createmethod you're trying to fetch a user from the database, based on params[:user_id], and params doesn't contain any user_id.
In this case I believe that it should come from the URL
So one solution if an album belongs to the user would be to set your routes like that :
resources :users do
resources :albums
end
Then you'll have to tell your form that the album is nested under a user byt explicitly giving the url. user_albums_path matches /users/:user_id/albums(.:format)
<%= form_for (#album), url: user_albums_path :html => { :id => "uploadform", :multipart => true } do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<br>
<%=f.submit %>
The your create method you receive the user_id it need in the params.
Let me know if'm not clear enough or if you need more explanations
You should use the build method defined here
#user = User.find(params[:user_id])
#user.album = Album.new(params[:album])
to create the album, you should
#user = User.find(params[:user_id])
#user.albums.build params[:album])
this will automatically set the user_id attribute of the album to #user.id, you won't have anything to do
so your create method should be
def create
#user = User.find(params[:user_id])
#album = #user.albums.build params[:album]
respond_to do |format|
if #album.save
format.html { redirect_to user_path(#user), notice: 'Album was successfully created.' }
format.json { render json: #album, status: :created, location: #album} # I don't know what this location is
else
format.html { render action: "new" }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end

Creating an AJAX Form for a Polymorphic Object in Rails

I am trying to create an AJAX form for a polymorphic associated model.
I created "Comments" which have a polymorphic association with all objects you can comment on (i.e. user profiles, organization profiles, events, etc).
I can currently add comments to objects using a form created by:
form_for [#commentable, #comment] do |f|
I am trying to make this form via Ajax but I keep getting errors.
I've tried at least ten different pieces of code, using remote_form_tag, remote_form_for, etc..with all different options, and nothing works. The comment does not get inserted into the database.
Specifically, I tried:
<% remote_form_for(:comment, :url => comments_path(#profile)) do |f| -%>
In my routes.rb, profile has many comments. And comments belongs to Profile. But when I submit the form nothing happens and the comment does not get posted to the database.
Can anyone please tell me what I'm doing wrong?
For your reference, here are my controllers. Comments controller:
class CommentsController < ApplicationController
layout 'index'
def index
#commentable = find_commentable
#comments = #commentable.comments
end
def show
#comment = Comment.find(params[:id])
end
def new
#comment = Comment.new
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
#comment.user_id = current_user.id
if #comment.save
responsetext = "<p><b>Comment: </b>" + #comment.content + "</p>"
render :text => responsetext
else
responsetext = "error"
render :text => responsetext
end
end
def edit
#comment = Comment.find(params[:id])
end
def update
#comment = Comment.find(params[:id])
if #comment.update_attributes(params[:comment])
flash[:notice] = "Successfully updated comment."
redirect_to #comment
else
render :action => 'edit'
end
end
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
flash[:notice] = "Successfully destroyed comment."
redirect_to comments_url
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
Profile controller:
class ProfilesController < ApplicationController
# GET /profiles
# GET /profiles.xml
layout 'index'
def index
##profile = Profile.find_by_user_id(current_user.id)
#profile = current_user.profile
#comment = Comment.new
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #profile }
end
end
# GET /profiles/1
# GET /profiles/1.xml
def show
#profile = Profile.find(params[:id])
#commentable = #profile
#comment = Comment.new(:commentable => #profile)
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #profile }
end
end
# GET /profiles/new
# GET /profiles/new.xml
def new
#profile = Profile.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #profile }
end
end
# GET /profiles/1/edit
def edit
##profile = Profile.find(params[:id])
#profile = current_user.profile
end
# POST /profiles
# POST /profiles.xml
def create
#profile = Profile.new(params[:profile])
respond_to do |format|
if #profile.save
flash[:notice] = 'Profile was successfully created.'
format.html { redirect_to(#profile) }
format.xml { render :xml => #profile, :status => :created, :location => #profile }
else
format.html { render :action => "new" }
format.xml { render :xml => #profile.errors, :status => :unprocessable_entity }
end
end
end
# PUT /profiles/1
# PUT /profiles/1.xml
def update
#profile = Profile.find(params[:id])
respond_to do |format|
if #profile.update_attributes(params[:profile])
flash[:notice] = 'Profile was successfully updated.'
format.html { redirect_to(#profile) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #profile.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.xml
def destroy
#profile = Profile.find(params[:id])
#profile.destroy
respond_to do |format|
format.html { redirect_to(profiles_url) }
format.xml { head :ok }
end
end
end
View:
<% remote_form_for([#commentable,#comment], :url => '/profiles/1/comments') do |f| -%>
<% #form_for [#commentable, #comment] do |f| %>
<br />
<%= f.text_area :content %><br />
<%= submit_tag 'Post', :disable_with => 'Please wait...' %>
<% end %>
<h2>Comments</h2>
<div id="comments">
<% #profile.comments.each do |c| %>
<div>
<div style="float:left;width:50px">
<%= image_tag c.user.profile.photo.url(:thumb) %>
</div>
<div style="float:left;padding:5px">
<b><%= link_to c.user.name, profile_path(c.user.profile) %></b>
<%=h c.content %><br />
<font style="color:grey"><%=h distance_of_time_in_words(Time.at(c.created_at.to_i).to_i,Time.now.to_i, include_seconds = true) %> ago</font>
</div>
</div>
<div style="clear:both"></div>
<% end %>
</div>
Could you paste the controller code? You need to have accepts_nested_attributes_for :comments in your profile model. In your controller you need to have
profile[:comments_attributes] = params[:comment]
profile.save

Resources