I am trying to add a comments section to posts on my web applications but getting an error when saving the comment
I have been following a tutorial to add a comments section to my posts and have been modifying as I go to work with my app. I am still relatively new to rails and I am still learning. I understand what the error message is telling me, but I am unsure as to how to proceed
Comments Controller:
class CommentsController < ApplicationController
def create
#micropost = Micropost.find_by(id: params[:id])
#comment =
#micropost.comments.create(params[:comment].permit(:body))
end
end
Microposts Contoller:
class MicropostsController < ApplicationController
before_action :logged_in_user, :upvote, :downvote, only:
[:create, :destroy]
before_action :correct_user, :upvote, :downvote, only:
:destroy, allow_destroy: true
def create
#micropost = current_user.microposts.build(micropost_params)
#maximum_length = Micropost.validators_on( :content,
:headline).first.options[:maximum]
if #micropost.save
flash[:success] = "Article Posted"
redirect_to root_url
else
#feed_items = []
render 'articles/home'
end
end
def destroy
#micropost.destroy
flash[:success] = "Micropost deleted"
redirect_to request.referrer || current_user
end
def show
#micropost = Micropost.find(params[:id])
end
private
def micropost_params
params.require(:micropost).permit(:content, :headline)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if #micropost.nil?
end
end
Comments Form being rendered on post:
<%= form_for([#micropost, #micropost.comments.build]) do |f| %>
<br>
<p>
<%= current_user.name %>
<%= f.text_area :body %>
</p>
<br>
<p>
<%= f.submit %>
</p>
<% end %>
Comments Model:
class Comment < ApplicationRecord
belongs_to :micropost
end
Microposts Model:
class Micropost < ApplicationRecord
acts_as_votable
has_many :comments
belongs_to :user
validates :user_id, presence: true
validates :headline, presence: true, length: { maximum: 200 }
validates :content, presence: true, length: { maximum: 5000 }
end
Tables:
create_table "comments", force: :cascade do |t|
t.string "name"
t.text "body"
t.bigint "microposts_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "micropost_id"
t.index ["microposts_id"], name:
"index_comments_on_microposts_id"
end
create_table "microposts", force: :cascade do |t|
t.text "content"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "headline"
t.index ["user_id", "created_at"], name:
"index_microposts_on_user_id_and_created_at"
t.index ["user_id"], name: "index_microposts_on_user_id"
end
The form is being rendered on the micropost show view. I can see the form fine.
When I press to save the comment on the post I get an error stating undefined method comments for nil:NilClass and it highlights the Comments controller and the line #comment = #micropost.comments.create(params[:comment].permit(:body))
I know there probably should a method somewhere for comments. The tutorial I watched didn't add anything like that. So I unsure do I need to modify some existing code, or do I need to add a method somewhere called comments?
where is the form to create comments, you are not sending the parameter params [: id], and with this parameter you are looking for the # micropost .. you must send the id in that form, a quick solution could be
<% = f.hidden_field: id, value: # micropost.id%>
in the comment creation form
Okay so the answers you all gave pointed me in the right direction. I wasn't actually passing an proper id. Instead of #micropost = Micropost.find_by(id: params[:id]) it should have been #micropost = Micropost.find_by(id: params[:micropost_id])
Many thanks all for getting me there.
Related
I'm very very new to this, and I'm trying to implement a to-do-list which allows use of tags with user authentication. When updating the task, I get a routing error which states No route matches [PATCH] "/users/1/articles". I suspect it's because I didn't pass the article ID when updating the article. Could anyone please help guide me on how to solve this? Any advice will be greatly appreciated thank you!
Routes from the error page
user_article_path GET /users/:user_id/articles/:id(.:format)
articles#show
PATCH /users/:user_id/articles/:id(.:format)
articles#update
PUT /users/:user_id/articles/:id(.:format)
articles#update
DELETE /users/:user_id/articles/:id(.:format)
articles#destroy
Routes.rb
Rails.application.routes.draw do
get 'sessions/new'
get 'welcome/index'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users do
resources :articles
end
get 'tags/:tag', to: 'articles#index', as: :tag, :constraints => { :tag => /[^\/]+/ }
root 'welcome#index'
end
article.rb
class Article < ApplicationRecord
attr_accessor :content, :name, :tag_list
before_create :downcase_fields
has_many :taggings , dependent: :destroy
has_many :tags, through: :taggings, dependent: :destroy
belongs_to :user
validates :user_id, presence: true
validates :title, presence: true,
length: { minimum: 1}
def self.tagged_with(name)
Tag.find_by_name!(name).articles
end
def downcase_fields
self.title.downcase
end
def self.tag_counts
Tag.select("tags.*, count(taggings.tag_id) as count").
joins(:taggings).group("taggings.tag_id")
end
def tag_list
tags.map(&:name).join(", ")
end
def tag_list=(names)
self.tags = names.split(",").map do |n|
Tag.where(name: n.strip).first_or_create!
end
end
def self.search(term)
if term
where('title LIKE ?', "%#{term}%").order('id DESC')
else
order('id DESC')
end
end
end
articles_controller
class ArticlesController < ApplicationController
before_action :correct_user
def index
#user = User.find(params[:user_id])
#articles = #user.articles.search(params[:term])
# if params[:tag]
# #articles = #user.articles.tagged_with(params[:tag])
# else
# #articles = #user.articles.all
# end
end
def show
#user = User.find(params[:user_id])
#article = #user.articles.find(params[:id])
end
def new
#user = User.find(params[:user_id])
#article = #user.articles.new
end
def edit
#user = User.find(params[:user_id])
#article = #user.articles.find(params[:id])
end
def create
#user = User.find(params[:user_id])
#article = #user.articles.new(article_params)
if #article.save
redirect_to user_articles_url
else
render 'new'
end
end
def update
#user = User.find(params[:user_id])
#article = #user.articles.find(params[:id])
if #article.update(article_params)
redirect_to user_articles_path
else
render 'edit'
end
end
def destroy
#user = User.find(params[:user_id])
#article = #user.articles.find(params[:id])
#article.destroy
redirect_to user_articles_path
end
private
def article_params
params.require(:article).permit(:title, :text, :tag_list, :term)
end
# Confirms a logged_in user_
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user
def correct_user
#user = User.find(params[:user_id])
redirect_to(root_url) unless current_user?(#user)
end
end
db.schema.rb
ActiveRecord::Schema.define(version: 2019_01_27_093653) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "text"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "user_id"
t.index ["user_id"], name: "index_articles_on_user_id"
end
create_table "taggings", force: :cascade do |t|
t.bigint "tag_id"
t.bigint "article_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "user_id"
t.index ["article_id"], name: "index_taggings_on_article_id"
t.index ["tag_id"], name: "index_taggings_on_tag_id"
t.index ["user_id"], name: "index_taggings_on_user_id"
end
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.index ["email"], name: "index_users_on_email", unique: true
end
add_foreign_key "articles", "users"
add_foreign_key "taggings", "articles"
add_foreign_key "taggings", "tags"
add_foreign_key "taggings", "users"
end
User controller
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update]
before_action :correct_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
redirect_to user_articles_path(#user)
end
def new
#user = User.new
end
def index
#users = User.paginate(page: params[:page])
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome to the To-Do-Manager!"
redirect_to user_articles_path(#user)
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
private
_form.html.erb(Create and update share the same form)
<%= form_with model: #article,url: user_articles_path(#user), local: true do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :Task %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :Deadline %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.label :tag_list, "Tags (separated by commas)" %><br />
<%= form.text_field :tag_list %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
Your user article path should be singular according to your routes:
<%= form_with model: #article, url: user_article_path(#user), local: true do |form| %>
You routes requires two parameters: the user (:user_id) and the article (:id), you are only passing the user user_articles_path(#user) and also using the plural form, and that route does not exists with that HTTP method.
You have to use url: user_article_path(#user,#article) (or you can use the shortcut version url: [#user, #article]).
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Thank you for visiting. I'm currently building a blogging app which will have 3 models. User, Article and Comments that will go into comments. I hope someone can help explain to me where I'm going wrong and how I can associated models better. Current error I get is undefined method `users' in Articles#new controller.
My current code is:
Schema
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "body"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "comments", force: :cascade do |t|
t.text "body"
t.integer "post_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["post_id"], name: "index_comments_on_post_id"
t.index ["user_id"], name: "index_comments_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.string "password_digest"
t.text "about_me"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Models
class User < ApplicationRecord
has_secure_password
has_many :articles
has_many :comments
end
class Article < ApplicationRecord
belongs_to :user
has_many :comments
validates :user_id, presence: true
end
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
end
Controller
class ArticlesController < ApplicationController
before_action :find_article, only: [:show, :edit, :update, :destroy]
def index
#articles = Article.all
end
def new
#article = Article.new
end
def create
#user = User.find(params[:user_id])
#article = #user.articles.create(article_params)
if #article.save
redirect_to articles_path
else
redirect_to new_article_path
end
end
def show
end
def edit
end
def update
if #article.update(article_params)
redirect_to :back
else
end
end
def destroy
if #article.destroy
flash[:notice] = "Successfully destroy the article"
redirect_to #article
else
flash[:alert] = "Couldnt delete article"
redirect_to :back
end
end
private
def find_article
#article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :body)
end
end
View
<%= form_for(#article, #article.users.build) do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :body %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
Routes
root 'users#index'
resources :users
resources :articles do
resources :comments
end
If there is something else I might be missing in order to help explain what I'm doing wrong and how to improve/understanding association and building/creating. Please let me know and I'll add the additional information.
For everyone who teaches me/helps me, seriously thank you.
Your form should be
<%= form_for #article do |f| %>
Then in the controller
def create
#article = current_user.articles.build(article_params)
if #article.save
redirect_to articles_path
else
redirect_to new_article_path
end
end
I have an app with an account model and an account manager (user).
I would like the account manager to be able to add other users to the account by typing in their email.
The account id of the account manager should be added to the new user's record.
My model looks like
class Account < ActiveRecord::Base
has_many users
...
end
class User < ActiveRecord::Base
belongs_to account
...
end
In the console it's fairly simple.
u = User.find(2)
u.update_attributes(account_id: 1)
But I'm not sure how to do this on the site.
I have the below partial which renders fine but when I put an email in the box nothing happens.
I get the message, user not added.
<h4>Add User</h4>
<div class = "row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:add_user, url: add_users_path) do |f| %>
<%= f.hidden_field #account = Account.find(params[:id]) %>
<%= f.label :email %>
<%= f.email_field :email, class:'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
I don't necessarily want/need to route to another page for example "url: add_users_path" but without that line the code breaks and won't render.
I added this to user.rb
def add_user_to_account
update_attribute(:account_id)
end
I also created this controller
class AddUsersController < ApplicationController
before_action :get_user, only: [:edit, :update]
def new
end
def edit
end
def create
#user = User.find_by(email: params[:email])
if #user
#user.add_user_to_account
flash[:info] = "Added User to Account"
redirect_to accounts_url
else
flash[:danger] = "User NOT Added"
redirect_to accounts_url
end
end
def update
if params[:user][:account_id].empty?
#user.errors.add(:account_id, "can't be empty")
render 'edit'
elsif #user.update_attributes(user_params)
log_in #user
flash[:success] = "Account Updated"
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:account_id, :user_id)
end
def get_user
#user = User.find_by(email: params[:email])
end
end
Am I on the right track? Is there a better way to do this?
UPDATE: Added schema
create_table "accounts", force: :cascade do |t|
t.string "name"
t.integer "account_manager_id"
t.integer "subscription_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "password_digest", null: false
t.string "remember_digest"
t.string "email", null: false
t.string "activation_digest"
t.boolean "activated"
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "access_granted"
t.boolean "requested_access"
t.string "slug"
t.integer "account_id"
end
I think that you had the params wrong.
you are using params[:email] instead of params[:add_user][:email] to find the user by email
in you controller
class UsersController < ApplicationController
...
def add_users
user = User.find_by(email: params[:add_user][:email])
if user
user.add_user_to_account
...
else
...
end
end
end
I am trying to implement a message system into my rails project but Ive been stuck for the past few days.. .
1) What I ve done so far
in db/schema file:
create_table "messages", force: :cascade do |t|
t.integer "conversation_id"
t.integer "user_id"
t.datetime "read_at"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "conversations", force: :cascade do |t|
t.integer "user1_id"
t.integer "user2_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
in models/message.rb
class Message < ActiveRecord::Base
belongs_to :conversation
belongs_to :user
validates_presence_of :conversation, :user, :content
end
in models/convcersation.rb
class Conversation < ActiveRecord::Base
belongs_to :user1, class_name: "User"
belongs_to :user2, class_name: "User"
has_many :messages
accepts_nested_attributes_for :messages
validates_presence_of :user1, :user2
end
in config/routes
resources :convocations, only: [:edit, :update] do
resources :messages, only: [:new, :create]
end
in controller/messages_controller.rb
class MessagesController < ApplicationController
before_action :set_conversation, only: [:show, :reply, :reply_server]
after_action :verify_policy_scoped, :only => :index
after_action :verify_authorized, except: [:new, :create]
respond_to :js, only: :reply
def new
#message = Message.new
end
def create
#conversation = Conversation.new
#conversation.user1 = current_user
#conversation.user2 = Convocation.find(params[:convocation_id]).subscription.tournament.user
#message = Message.new(message_params)
#message.user = current_user
#message.conversation = #conversation
if #message.save
redirect_to new_convocation_message_path
else
render :new
end
end
def index
end
private
def message_params
params.require(:message).permit(:content)
end
end
now here's the errors I get :
1) in my views/convocations/edit at some point Ive got
<div class="modal-footer text-center">
<p> Envoyez un message au juge arbitre pour lui donner vos dispos</p>
<%= link_to "envoyer un message",new_convocation_message_path(#convocation), class: "btn btn-primary" %>
</div>
when I click on the button I get :
undefined method `messages_path' for #<#<Class:0x007fec5c8feda0>:0x007fec5561b1f8>
this is strange as I don't know why I would need a messages_path for what I m trying to do... but I currently fix the problem by adding to my routes:
get "messages", to: "tournaments#index", as: "messages"
I am then able to get to the message creation page
in views/messages/new which has a simple form like that
<%= simple_form_for [#convocation, #message] do |f| %>
<%= f.input :content %>
<%= f.button :submit, class: "btn btn-success" %>
<% end %>
Don't worry it's almost the end :)
When I click on the submit btn here's the error I get:
No route matches [POST] "/messages"
well I dont get it because I have a route that matches that:
convocation_messages_path POST /convocations/:convocation_id/messages(.:format) messages#create
So im stuck right now... Thanks to the one who had the courage to read all this !
I am currently using Rails 4 to create a website and I need to create a form for a model "candidature" inside a model "post" using another model "reponse".
This the post model :
class Post < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
belongs_to :entrepreneur
belongs_to :categorie
has_many :candidatures
accepts_nested_attributes_for :candidatures
has_many :questions
accepts_nested_attributes_for :questions,
:reject_if => lambda { |a| a[:enonce].blank? },
:allow_destroy => true
end
The candidature model :
class Candidature < ActiveRecord::Base
has_many :reponses
accepts_nested_attributes_for :reponses, :allow_destroy => true
end
And the reponse model :
class Reponse < ActiveRecord::Base
belongs_to :candidature
end
I don't have controllers for candidature and reponse, because i don't think i need any (do correct me if I'm wrong).
I have created posts that are projects and in the show view of posts, i need to create a form where a guest can answer some questions through answer and all those answers have to be saved in one candidature.
The schema.rb looks like that :
ActiveRecord::Schema.define(version: 20130718083016) do
create_table "candidatures", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.integer "post_id"
end
create_table "questions", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.integer "post_id"
t.text "enonce"
end
create_table "reponses", force: true do |t|
t.integer "candidature_id"
t.datetime "created_at"
t.datetime "updated_at"
t.text "enonce"
end
create_table "posts", force: true do |t|
t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
t.text "defi"
t.text "mission"
t.text "competences"
t.text "gain"
t.text "lieny"
t.text "liendb"
t.string "link"
end
The controller for post :
class PostsController < ApplicationController
layout :resolve_layout
include Candidaturetopost
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
flash[:notice] = "Successfully created project."
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
#post.candidatures.build
end
def index
#posts = Post.all
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
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(:title, :image, :defi, :mission, :competences, :gain, :lieny, :liendb, :link, questions_attributes: [:enonce, :post_id, :id, :_destroy],
candidatures_attributes: [:post_id, :id, reponses_attributes: [:candidature_id, :id, :enonce]])
end
The show view i have tried to get working :
<%= form_for #post do |f| %>
<%= f.fields_for :candidatures do |cform| %>
<ol>
<% for question in #post.questions %>
<li><%= question.enonce %></li>
<%= cform.fields_for :reponses do |rform| %>
<%= rform.text_area :enonce %>
<% end %>
<% end %>
</ol>
<%= cform.submit %>
<% end %>
<% end %>
With the code shown here, the text_area for enonce isn't even displayed.
Is what I want to do even possible ? How can I have something similar if not ?
Try this
#post.candidatures.build
#post.candidatures.each {|candidature| candidature.responses.build }
But as I wrote in comment you overcomplicated the model structure and I belive you should rethink it.
You were right, it was way too complicated.
I used this to help me get it working, I have now the links I wanted between my three models.