I'm working on an app that allows users to comment on a single "work" (think blog post). The associations in the models are as follows:
class User < ActiveRecord::Base
has_many :works
has_many :comments
class Work < ActiveRecord::Base
belongs_to :user
has_many :comments
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
belongs_to :work
There's a form on the Works show page that allows users to post a comment:
<%= form_for(#comment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Post a comment!" %>
</div>
<%= f.submit "Post", class: "btn btn-small btn-primary" %>
<% end %>
The Works controller is as follows. Note that I'm adding the build comment functionality here so that the form on the Works page functions:
class WorksController < ApplicationController
#before_filter :current_user, only: [:edit, :update]
def index
#works = Work.all
#comment = #work.comments.build(params[:comment])
#comment.user = current_user
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #works }
end
end
def create
#work = current_user.works.create(params[:work])
redirect_to current_user
end
def edit
#work = current_user.works.find(params[:id])
end
def new
#work = current_user.works.new
end
def destroy
#work = current_user.works.find(params[:id]).destroy
flash[:success] = "Work deleted"
redirect_to current_user
end
def update
#work = current_user.works.find(params[:id])
if #work.update_attributes(params[:work])
flash[:success] = "Profile updated"
redirect_to #work
else
render 'edit'
end
end
def show
#work = Work.find(params[:id])
#comment = #work.comments.build
#comment.user = current_user
#activities = PublicActivity::Activity.order("created_at DESC").where(trackable_type: "Work", trackable_id: #work).all
#comments = #work.comments.order("created_at DESC").where(work_id: #work ).all
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #work }
end
end
end
And lastly, here is the Comments controller:
class CommentsController < ApplicationController
before_filter :authenticate_user!
def index
#comments = Comment.all
end
def show
#comment = Comment.find(params[:id])
#activities = PublicActivity::Activity.order("created_at DESC").where(trackable_type: "Comment", trackable_id: #comment).all
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #comment }
end
def update
#comment = current_user.comments.find(params[:id])
if #comment.update_attributes(params[:comment])
flash[:success] = "Comment updated"
redirect_to #comment
end
end
def create
#work = Work.find(params[:id])
#comment = #work.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
#flash[:success] = "Post created!"
redirect_to #work
else
render 'home#index'
end
end
end
end
When I attempt to submit a comment using the comment form on the works show view page, I get the following error:
Unknown action
The action 'create' could not be found for CommentsController
Why can't it find the create action on the Comments controller? Any help would be greatly appreciated.
Thanks!
Your comments controller show action is missing an end, i.e. this:
def show
#comment = Comment.find(params[:id])
#activities = PublicActivity::Activity.order("created_at DESC").where(trackable_type: "Comment", trackable_id: #comment).all
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #comment }
end
should be this:
def show
#comment = Comment.find(params[:id])
#activities = PublicActivity::Activity.order("created_at DESC").where(trackable_type: "Comment", trackable_id: #comment).all
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #comment }
end
end
(right now it is 'falling' into the create action so the create action never gets set up).
You will then probably need to change the final:
end
end
end
end
to be
end
end
end
as this was probably 'compensating' for the mismatch and allowing the page to precompile in Ruby.
Related
I am working on user_profile_reviews and have got stuck. I have 3 models for now, and I know, that doing a separate model for a profile wasn't really a great idea, but since all my routes depend on this structure, meaning the links in all the views also, I decided not to change it.
To give you a clearer understanding:
Rails.application.routes.draw do
devise_for :user, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: "users/registrations" }
resources :users do
resources :profiles do
resources :reviews, only: [:new, :create]
end
end
root 'home#index'
end
Here are my controllers:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def index
#profiles = Profile.all
end
def show
#user = User.find(params[:user_id])
#profile = Profile.find(params[:id])
#reviews = Review.where("profile_id = ?", params[:id])
end
def new
#user = User.find(params[:user_id])
end
def edit
#profile = Profile.find(params[:id])
end
def create
#profile = current_user.build_profile(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to user_profile_path(current_user.id, current_user.profile.id), notice: 'Profile was successfully created.' }
format.json { render :show, status: :created, location: #profile }
else
format.html { render :new }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to user_profile_path(current_user.id, current_user.profile.id), notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_profile
#profile = Profile.find(params[:id])
end
def profile_params
params.permit(:about, :rating, :avatar)
end
end
Reviews
class ReviewsController < ApplicationController
before_filter :set_profile
before_filter :set_review, only: [:new, :create]
def new
#review = Review.new
end
def create
#profile = Profile.find(params[:profile_id])
#review = #profile.reviews.build(review_params)
#review.user_id = current_user.id
if #review.save
redirect_to #profile
else
redirect_to #profile, notice: "Error saving"
end
end
private
def review_params
params.permit(:content, :rating)
end
def set_pfofile
#profile = Profile.find(params[:profile_id])
end
def set_review
#profile = Profile.find(params[:id])
end
end
So now, I am trying to create a form for reviews, which I am then rendering in Profiles#show, and getting the mistake above.
<div class="submit-review">
<%= form_for [#review, :url => user_profile_reviews_path(#profile)] do |f| %>
<label for="review">How was your experience?</label><br>
<%= f.label :rating %>
<%= f.select :rating, options_for_select([["Please select one", ""], 5, 4, 3, 2, 1]) %>
<%= f.input :content, placeholder:"Please enter your feedback here" %>
<%= f.submit "Submit your review", class: "btn btn-default" %> <br><br>
<% end %>
Showing ... /_form.html.erb where line #2 raised:
No route matches {:action=>"create", :controller=>"reviews", :id=>"2", :user_id=>#<Profile id: 2, about: "lena", rating: 3, created_at: "2019-11-22 21:27:03", updated_at: "2019-11-22 21:27:03", user_id: 2>}, missing required keys: [:profile_id]
But, as I see, it gets to the profile, I am onto, so I don't understand what's the issue here.
Something wrong with the syntax, try this
= form_for([#profile.user, #profile, #review], :url => user_profile_reviews_path(#profile.user, #profile)) do |f|
Since your resources are nested, you need to pass user, profile and then review as the first argument in form_for
Suggestion: Looking at your code, you don't even need user_id, you can avoid nesting profile and review under user in routes.
Hope that helps!
Ok, this worked perfectly for solving the described problem with missing id.
form_for([#profile.user, #profile, #review], :url => user_profile_reviews_path(#profile.user, #profile)) do |f|
I was getting another error though:
First argument in form cannot contain nil or be empty
I saw then, that in my Profile#show I wasn't defining #review. Only reviews. So I did it this way:
def show
#user = User.find(params[:user_id])
#profile = Profile.find(params[:id])
#review = Review.new
#reviews = Review.where("profile_id = ?", params[:id])
end
I can now finally go to profile and there is a review window, which is great! I can't save the reviews though, as another error is showing up. But that's different case. Thank you so much!
i have a noob problem. I need render view from another controller. I use devise and create a perfil controller.
Perfil Controller:
class PerfilController < ApplicationController
before_filter :authenticate_user!
def index
end
def show
#usuario = User.find(current_user)
#usuario.perfil ||= #usuario.build_perfil
#perfil = #usuario.perfil
end
def update
#usuario = User.find(current_user)
#perfil = Perfil.new(perfil_params)
#usuario.perfil ||= #usuario.build_perfil
respond_to do |format|
if #usuario.perfil.update_attributes(perfil_params)
format.html {redirect_to #usuario, notice: "update" }
else
format.html { render action: "show"}
end
end
end
private
def perfil_params
params.require(:perfil).permit(:nombre, :apellido)
end
end
User controller:
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
def index
#users = User.all
authorize User
end
def show
#user = User.find(params[:id])
authorize #user
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
authorize user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
private
def secure_params
params.require(:user).permit(:role)
end
end
class PerfilController < ApplicationController
before_filter :authenticate_user!
def index
end
def show
#usuario = User.find(current_user)
#usuario.perfil ||= #usuario.build_perfil
#perfil = #usuario.perfil
end
def update
#usuario = User.find(current_user)
#perfil = Perfil.new(perfil_params)
#usuario.perfil ||= #usuario.build_perfil
respond_to do |format|
if #usuario.perfil.update_attributes(perfil_params)
format.html {redirect_to #usuario, notice: "actualizado" }
else
format.html { render action: "show"}
end
end
end
private
def perfil_params
params.require(:perfil).permit(:nombre, :apellido)
end
end
Perfil/show
<h2>Edit </h2>
<%= form_for #perfil, :url=>{:action=>:update}, :html=>{:method=>:put} do |f| %>
<p><%= f.label :nombres %><br />
<%= f.text_field :nombre %></p>
<div><%= f.label :apellidos %><br />
<%= f.text_field :apellido %></div>
<div><%= f.submit "Actualizar" %></div>
<% end %>
<%= link_to "AtrĂ¡s", :back %>
well, i need show update in users/index
<h3>Configuracion de la cuenta</h3>
<div class = "col-md-3"> <h3>Users</h3>
<div class="nav nav-pills nav-stacked">
<%= render 'users/menu' %>
</div>
</div>
<div class = "col-md-7">
<h3>Informacion</h3>
<%= render 'users/show' %>
</div>
I hope I explained well.
Regards.
You can render any view from your application..using render
take care of the variables used inside the view and do include them in the method call.
RENDER ONLY RENDERS THE VIEW AND DO NOT CALLS THE ACTION OF THE CONTROLLER
render other controller view from the controller..
in users_controller.rb
##render partial from views/profile
respond_to do |format|
format.js { render 'profile/upload' }
format.html 'profile/upload'
end
same can be used in view file as well
<%= render 'profile/upload'%>
I'm having issues associating a users status post to them, I also can't get it to show on their account.
This is in my users_controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
after_action :signed_in_after_register, only: :create
def index
#users = User.all
end
def profile
#user = User.find(session[:user_id]) unless session[:user_id] == ""
redirect_to login_path, notice: "You're not logged in" unless #user
#posts = #user.posts
end
def _nav
#user = User.find(session[:user_id])
end
def destroy
#user = User.find(session[:user_id])
end
def welcome
#user = User.find(session[:user_id])
end
def show
#user = Post.first.update_attributes(user_id: 1)
#post = Post.first.update_attributes(#signed_in_user)
end
def new
#post = Post.new(params[:post_id])
#user = User.new
end
def edit
end
def create
#post.user = current.user
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to profile_path, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_user
#user = User.find(params[:id])
end
def signed_in_after_register
session[:user_id] = #user.id
end
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :email, :age, :profile_picture, :post)
end
end
This is in my posts_controller
class PostsController < ApplicationController
def index
#posts = Post.all.order('created_at DESC')
end
def new
#post = Post.new
end
def create
#post = #signed_in_user
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
#signed_in_user = session[:user_id]
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:body))
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:body)
end
end
This is the profile.html.erb page that I want the signed_in_users most recent post to be returned to
<%- #posts.each do |post| %>
<div class="post_wrapper">
<h2 class="title"><%= link_to post.body, post %></h2>
<p class="date"><%= post.created_at.strftime("%B, %d, %Y") %> </p>
</div>
<% end %>
This is in my routes
Rails.application.routes.draw do
root 'welcome#welcome'
get 'login' => 'sessions#login', :as => :login
get 'profile' => 'users#profile', :as => :profile
post 'logging/user' => 'sessions#create'
get 'logout' => 'sessions#destroy', :as => :logout
get 'about' => 'about'
resources :users
resources :posts
get 'index' => 'posts#index'
get 'register' => 'users#new', :as => :register
Just to clear the question up a bit more and be very specific, I want it to be exactly like a status post that Facebook offers, You write it, It post, It shows on your profile. That's it, I have no problems with Writing, Posting and it saving. It just doesn't show on the profile page of the user that created it.
run this command in your terminal:
rails generate migration AddUserRefToPosts user:references
in your user model:
class User < ActiveRecord::Base
has_many :posts
...
end
3.in post controller
def create
#post = Post.new(post_params)
#post.user_id = #signed_in_user.id
if #post.save
redirect_to #post
else
render 'new'
end
end
Try this
def create
#post = #signed_in_user.posts.build(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
Your create action should look like this
def create
#post = Post.new(post_params)
#post.user_id = #signed_in_user.id
if #post.save
redirect_to #post
else
render 'new'
end
end
class CommentsController < ApplicationController
def create
#contact = Contact.find(params[:contact_id])
#comment = #contact.comments.create(params[:comment])
respond_to do |format|
format.html { redirect_to contact_path(#contact) }
format.js
end
end
def destroy
#contact = Contact.find(params[:contact_id])
#comment = #contact.comments.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to contact_path(#contact) }
format.js
end
end
end
Is it possible to also create and destroy comments for the company model? How do you check whether a user is on a certain page? Because then I can just have an if statement.
The changed CommentsController
class CommentsController < ApplicationController
def create
#object = find_object
#comment = #object.comments.create(params[:comment])
respond_to do |format|
format.html { redirect_to [#object] }
format.js
end
end
def destroy
#object = find_object
#comment = object.comments.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to [#object] }
format.js
end
end
private
def object
#object = if params[:contact_id]
Contact.find(params[:contact_id]
elsif params[:company_id]
Company.find(params[:company_id])
end
end
end
you can do it with routing
# routes.rb
resources :contacts do
resources :comments
end
resources :company do
resources :comments
end
So in controller you can handle if there any company or contact around:
def destroy
#object = find_object
#comment = #object.comments.find(params[:id])
#comment.destroy
redirect_to [#object]
end
private
def find_object
#object = if params[:contact_id]
Contact.find(params[:contact_id])
elsif params[:company_id]
Company.find(params[:company_id])
end
end
But best solution here is to use POLYMORPHISM here. Check out:
http://railscasts.com/episodes/154-polymorphic-association
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