Thank you for taking the time to review this question.
I have a nested resource called templates, which belong to categories. I have the template create, edit and delete actions displayed in the Category Show page. I am able to create with no issue, however, when attempting to edit or delete the same template gets retrieved by its ID (usually the first template created under a category).
Code
config/routes.rb
resources :categories do
resources :templates
app/models/template.rb
class Template < ApplicationRecord
belongs_to :category
validates :title, presence: true
validates :content, presence: true
end
app/models/category.rb
class Category < ApplicationRecord
belongs_to :user
has_many :templates, dependent: :destroy
validates :name, presence: true
end
db/schema.rb
create_table "categories", force: :cascade do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_categories_on_user_id"
end
create_table "templates", force: :cascade do |t|
t.integer "user_id"
t.integer "category_id"
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["category_id"], name: "index_templates_on_category_id"
t.index ["user_id"], name: "index_templates_on_user_id"
end
app/views/categories/show.html.erb
<% #templates.each do |template| %>
<div class= "template-grid">
<div class= "template-index-card">
<h1><%= template.title%></h1>
<p><%= template.content %></p>
<div class= "template-options">
<%= link_to image_tag("edit.png", class: "icon"), edit_category_template_path([#category, #template])%>
<%= link_to image_tag("bin.png", class: "icon"), category_template_path([#category, #template]), method: :delete,
data: { confirm: 'Are you sure?' }%>
app/views/templates/edit.html.erb
<%= simple_form_for([#category, #template]) do |f| %>
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :content %>
</div>
<div class= "form-btn-flex">
<%= f.button :submit, "Update Template", class:"btn-mdpm-forms"%>
<% end %>
app/controllers/templates_controller.rb
class TemplatesController < ApplicationController
def new
#template = Template.new
end
def create
#template = Template.new(template_params)
#template.user_id = current_user.id
#template.category = Category.find(params[:category_id])
if #template.save
redirect_to category_path(#template.category_id)
else
render :show
end
end
def index
#templates = Template.all
end
def show
#template = Template.find(params[:id])
end
def edit
#category = Category.find(params[:category_id])
#template = #category.templates.find_by(params[:id])
end
def update
#category = Category.find_by(params[:category_id])
#template = #category.templates.find_by(params[:id])
if #template.update(template_params)
redirect_to category_path(#category)
else
render :edit
end
end
def destroy
#template = Template.find_by(params[:id])
#template.destroy
redirect_to category_path(#template.category_id)
end
private
def template_params
params.require(:template).permit(:title, :content)
end
end
app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
before_action :set_category, only: [:edit, :update]
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
#category.user = current_user
if #category.save
redirect_to categories_path
else
render :index
end
end
def index
#category = Category.new
#categories = Category.all
end
def show
#template = Template.new
#category = Category.find(params[:id])
#templates = #category.templates
end
def edit
#category = Category.find(params[:id])
end
def update
if #category.update(category_params)
redirect_to category_path(#category)
else
render :edit
end
end
def destroy
#category = Category.find(params[:id])
#category.destroy
redirect_to categories_path
end
private
def category_params
params.require(:category).permit(:name)
end
def set_category
#category = Category.find(params[:id])
end
end
Routes
category_templates GET /categories/:category_id/templates(.:format) templates#index
POST /categories/:category_id/templates(.:format) templates#create
new_category_template GET /categories/:category_id/templates/new(.:format) templates#new
edit_category_template GET /categories/:category_id/templates/:id/edit(.:format) templates#edit
category_template GET /categories/:category_id/templates/:id(.:format) templates#show
PATCH /categories/:category_id/templates/:id(.:format) templates#update
PUT /categories/:category_id/templates/:id(.:format) templates#update
DELETE /categories/:category_id/templates/:id(.:format) templates#destroy
categories GET /categories(.:format) categories#index
POST /categories(.:format) categories#create
new_category GET /categories/new(.:format) categories#new
edit_category GET /categories/:id/edit(.:format) categories#edit
category GET /categories/:id(.:format) categories#show
PATCH /categories/:id(.:format) categories#update
PUT /categories/:id(.:format) categories#update
DELETE /categories/:id(.:format) categories#destroy
Thanks!
The problem ended up being a matter of too many variables named the same. In order to fix the issue I had to make the following changes:
1 - On app/views/categories/show.html.erb I removed the # form template since I was inside an iteration, I also passed the params not as an array as suggested.
<% #templates.each do |template| %>
<div class= "template-grid">
<div class= "template-index-card">
<h1><%= template.title%></h1>
<p><%= template.content %></p>
<div class= "template-options">
<%= link_to image_tag("edit.png", class: "icon"), edit_category_template_path(#category, template)%>
<%= link_to image_tag("bin.png", class: "icon"), category_template_path(#category, template), method: :delete,
data: { confirm: 'Are you sure?' }%>
I updated the name of the #template variable to #templately on the show method of app/controllers/categories_controller.rb so that I could use #template to pass the id on the edit method on the template_controller.
how about passing the params, not as an array?
<%= link_to image_tag("edit.png", class: "icon"), edit_category_template_path(category_id: #category.id, id: #template.id)%>
<%= link_to image_tag("bin.png", class: "icon"), category_template_path(category_id: #category.id, id: #template.id), method: :delete,
...
Related
hey everyone so I'm working on a small blog and there is a new section a want to add. An "editor's choice" section so basically i want to add a small div that contains 3 randomly pick posts from the blog. The problem is I do not know how to show only three randomely select titles from all the posts. I tried #posts.sample(3) and that shows the entire record of the Post. I just want to show the title only.
I want to show the editors choice on the posts index view
Schema
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id"
t.string "nickname"
end
Posts_controller
class PostsController < ApplicationController
before_action :current, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all
end
def show
end
def new
#post = Post.new
end
def create
#post = Post.new(posts_params)
#post.user = current_user
if #post.save
redirect_to posts_path
else
render :new
end
end
def edit
end
def update
#post.user = current_user
#post.update(posts_params)
#post.save
redirect_to posts_path
end
def destroy
if #post.destroy
redirect_to posts_path
end
end
private
def posts_params
params.require(:post).permit(:title, :content, :user_id, :image, :nickname)
end
def current
#post = Post.find_by(id: params[:id])
end
end
post_index.html.erb
<%= render "layouts/blog_header" %>
<% #posts.each do |post| %>
<div class="posts">
<div>
<% unless !post.image.attached? %>
<%= image_tag post.image, class: "post-index-image" %><br>
<% end %>
</div>
<p class="post-index-title"><%= link_to post.title, post_path(post.id), class: "post-index-title" %></p>
<p>Published by: <strong><%= post.user.nickname %></strong></p>
</div>
<% end %>
<%= #posts.sample(3) %>
Thanks!
I would start with something like this when you are using PostgreSQL:
#posts.order('RANDOM()').limit(3)
or when you are on MySQL:
#posts.order('RAND()').limit(3)
I think it should do what you want:
#posts.sample(3).pluck(:title)
I'd go with
#posts.pluck(:title).sample(3)
More efficient than
#posts.sample(3).pluck(:title)
Been trying to figure out how to get a Multi Step form for an object a user will build using Wicked Wizard gem. I have been following the docs on the gem's github and this question on that Scheem's answered himself, but I am getting a forbidden method error when on the last step of the wizard and am trying to submit. I've seen in other questions, to refer to strong parameters, and I have been using that as well and am unsure what is causing the error. So far I have the following.
Error message:
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/places/steps_controller.rb:14:in `update'
Started GET "/places/20/steps/id" for 127.0.0.1 at 2017-07-02 03:10:57 -0400
Processing by Places::StepsController#show as HTML
Parameters: {"place_id"=>"20", "id"=>"id"}
Place Load (183.0ms) SELECT "places".* FROM "places" WHERE "places"."id" = ? LIMIT ? [["id", 20], ["LIMIT", 1]]
Rendering places/steps/id.html.erb within layouts/application
Rendered places/steps/id.html.erb within layouts/application (851.5ms)
Completed 200 OK in 5753ms (Views: 4688.2ms | ActiveRecord: 183.0ms)
Started PUT "/places/20/steps/id" for 127.0.0.1 at 2017-07-02 03:11:24 -0400
Processing by Places::StepsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"/VrDWevWLY+A6841zIR5awBGopfnoXWxE8zf+6xkogJC6ufPQ+qYGp4Mg72N3Dlc547oSF/cXa4c/vMK+JU6qg==", "place"=>{"cost"=>"$100.00", "user_id"=>"1"}, "commit"=>"Complete", "place_id"=>"20", "id"=>"id"}
Place Load (14.9ms) SELECT "places".* FROM "places" WHERE "places"."id" = ? LIMIT ? [["id", 20], ["LIMIT", 1]]
(34.9ms) begin transaction
(0.1ms) rollback transaction
Completed 500 Internal Server Error in 212ms (ActiveRecord: 49.8ms)
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/places/steps_controller.rb:14:in `update'
Schema.rb
ActiveRecord::Schema.define(version: 20170630190146) do
create_table "places", force: :cascade do |t|
t.string "name"
t.string "location"
t.string "cost"
t.date "available"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "status"
t.index ["user_id"], name: "index_places_on_user_id"
end
create_table "users", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "username"
t.string "password_digest"
end
end
place.rb
class Place < ApplicationRecord
belongs_to :user
validates :name, :location, :cost, :available, :presence => true, :if => :active?
def active?
status == 'active'
end
end
places_controller.rb
class PlacesController < ApplicationController
before_action :set_place, only: [:show, :edit, :update, :destroy]
# GET /places
# GET /places.json
def index
#places = Place.where.not(name: nil)
end
# GET /places/1
# GET /places/1.json
def show
end
# GET /places/new
def new
#place = Place.new
end
# GET /places/1/edit
def edit
end
# POST /places
# POST /places.json
def create
#place = Place.new
if #place.save(validate: false)
redirect_to place_step_path(place_id: #place.id, id: :date)
else
render new
end
end
# PATCH/PUT /places/1
# PATCH/PUT /places/1.json
def update
respond_to do |format|
if #place.update(place_params)
format.html { redirect_to #place, notice: 'Place was successfully updated.' }
format.json { render :show, status: :ok, location: #place }
else
format.html { render :edit }
format.json { render json: #place.errors, status: :unprocessable_entity }
end
end
end
# DELETE /places/1
# DELETE /places/1.json
def destroy
#place.destroy
respond_to do |format|
format.html { redirect_to places_url, notice: 'Place was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_place
#place = Place.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def place_params
params.require(:place).permit(:name, :location, :cost, :available, :user_id, :status)
end
end
steps_controller.rb
class Places::StepsController < ApplicationController
include Wicked::Wizard
steps :date, :id
def show
#place = Place.find(params[:place_id])
render_wizard
end
def update
#place = Place.find(params[:place_id])
params[:place][:status] = 'active' if step == steps.last
#place.update_attributes(params[:place])
render_wizard #place
end
def create
#place = Place.create
redirect_to wizard_path(steps.first, :place_id => #product.id)
end
private
end
routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
resources :places
resources :places do
resources :steps, controller: 'places/steps'
end
resources :users
# session routes
resources :sessions
get 'session/new'
get '/sessions/new', to: 'sessions#new'
post '/sessions/new', to: 'sessions#create'
get '/logout', to: 'sessions#destroy'
delete '/logout', to: 'sessions#destroy'
end
date.html.erb
<%= form_for #place, method: :put, url: wizard_path do |f| %>
<% if f.object.errors.any? %>
<div class="error-messages">
<% f.object.errors.full_messages.each do |error| %>
<p><%= error %></p>
<% end %>
</div>
<% end %>
<div class="h2">When Is Place available?</div>
<div class="fied">
<%= f.label "Date" %>
<%= f.date_select :available, start_year: 2017 %>
</div>
<div class="actions">
<%= f.submit "Next" %>
or <%= link_to "skip this step", next_wizard_path %>
</div>
<% end %>
id.html.erb
<%= form_for #place, method: :put, url: wizard_path do |f| %>
<% if f.object.errors.any? %>
<div class="error-messages">
<% f.object.errors.full_messages.each do |error| %>
<p><%= error %></p>
<% end %>
</div>
<% end %>
<div class="h2">
<div class="field">
<%= f.label "Cost" %>
<%= f.text_field :cost %>
</div>
<div class="field">
<%= f.label "Belongs to" %>
<%= f.text_field :user_id %>
</div>
</div>
<div class="actions">
<%= f.submit "Complete" %>
</div>
<% end %>
ActiveModel::ForbiddenAttributesError
(ActiveModel::ForbiddenAttributesError)
I believe the update method in Places::StepsController is the culprit. You are not using strong params in that method. Try changing it to below
def update
#place = Place.find(params[:place_id])
params[:place][:status] = 'active' if step == steps.last
#place.update_attributes(place_params) #here you should use strong params
render_wizard #place
end
And define a place_params under the private
private
def place_params
params.require(:place).permit(:name, :location, :cost, :available, :user_id, :status)
end
How can I call comments + user information that is specified to the specific post the comment was created under. For example:
/articles/8
new comment created with user_id = 3
Page will show Username + comment + created_at
This is my current code:
Post Show Page
<%= #post.title %>
<%= render '/comments/form' %>
<% #post.user.comments.reverse.each do |comment| %>
<li>
<%= comment.user.email %>
<%= comment.comment %>
</li>
<% end %>
I grab user information associated with the comment but the problem is, it's listing all the comments. How do I make only comments with article_id of 8 for example appear.
Post Controller
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save!
flash[:notice] = "Successfully created..."
redirect_to posts_path
else
flash[:danger] = "failed to add a post"
render 'new'
end
end
def show
#comment = Comment.new
#comments = Comment.find(params[:id])
end
Comment Controller
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
if #comment.save
flash[:notice] = "Successfully created..."
redirect_to post_path(#post)
else
flash[:alert] = "failed"
redirect_to root_path
end
end
Routes
resources :sessions, only: [:new, :create, :destroy]
resources :users
resources :posts do
resources :comments
end
Schema of the comments
create_table "comments", force: :cascade do |t|
t.text "comment"
t.integer "user_id"
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I am assuming that your model looks like
class Comments < ApplicationRecord
belongs_to :user
belongs_to :post
end
class User < ApplicationRecord
has_many :posts
has_many :comments
end
class Post < ApplicationRecord
belongs_to :user
has_many :comments
end
We want to get all the comments for a post so we we can do something like this
# Note that I took the user from this line
<% #post.comments.reverse.each do |comment| %>
<li>
<%= comment.user.email %>
<%= comment.comment %>
</li>
<% end %>
I hope that this should work.
I have a very basic Photo and Comments model that works and then I have a built a Cflags model that is used to flag comments. I am getting the following error from Heroku log when I visit the photos/show.html.erb view.
LoadError (Unable to autoload constant Cflag, expected /app/app/models/cflag.rb to define it)
photos/show.html.erb
.
.
<% #photo.comments.each do |comment| %>
<%= form_for([comment, Cflags.new]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>
PhotosController
def show
#photo = Photo.approved.find(params[:id])
end
CommentsController
def create
#photo = Photo.find(params[:photo_id])
#comment = #photo.comments.build(comment_params)
#comment.save
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
class CFlag < ActiveRecord::Base
belongs_to :comment, counter_cache: true
belongs_to :user, counter_cache: true
validates :user_id, presence: true
validates :comment_id, presence: true
validates :user_id, uniqueness: {
scope: [:comment_id],
message: 'You can only flag a comment once. Thank you for your feedback.'
}
default_scope -> { order(created_at: :desc) }
end
class CflagsController < ApplicationController
before_action :logged_in_user
def create
#comment = Comment.find(params[:comment_id])
#cflag = #comment.cflags.build(cflag_params)
if #cflag.save
if #comment.cflags_count > 1
#comment.update_attribute(:approved, false)
flash[:success] = "Flag created! Comment ##{#comment.id} has been removed for review. Thank you for your feedback"
redirect_to :back
else
flash[:success] = "Flag created! Thank you for your feedback"
redirect_to :back
end
else
redirect_to :back, notice: #cflag.errors.full_messages
end
end
private
def cflag_params
params.require(:cflag).permit(:user_id, :comment_id).merge(user_id: current_user.id)
end
end
resources :photos do
resources :comments do
resources :cflags
end
end
create_table "cflags", force: :cascade do |t|
t.integer "comment_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "cflags", ["comment_id"], name: "index_cflags_on_comment_id"
add_index "cflags", ["user_id"], name: "index_cflags_on_user_id"
If I change the form to:
<% #photo.comments.each do |comment| %>
<%= form_for([comment, comment.cflags.build]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>
I get the same error:
LoadError (Unable to autoload constant Cflag, expected /app/app/models/cflag.rb to define it):
There is a typo in the controller name.
Your model is CFlag with F uppercase but your controller has f lowercase.
Change it to CFlagsController
class CFlagsController < ApplicationController
before_action :logged_in_user
def create
...
end
end
building my first blog, head is spinning.
I am trying to show comments in my dashboard view. I am able to pull in the comments and the ID but what I would really like to do is also put the title of the post the comment lives and link to it. From my very amateur understanding I think I am not making the connection from comments to post.
Here is what I got...
Post model has_many :comments
Comments model belongs_to :posts
routes
resources: posts do
resources: comments
end
Dashboard Controller:
class DashboardController < ApplicationController
before_filter :authorize, only: [:index]
def index
#posts = Post.all
#comments = Comment.all
end
end
Comments Controller:
class CommentsController < ApplicationController
before_filter :authorize, only: [:destroy, :show]
def create
#post = Post.find(params[:post_id])
#comment =
#post.comments.create(comments_params)
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def comments_params
params.require(:comment).permit(:commenter, :body)
end
end
Posts Controller:
class PostsController < ApplicationController
before_filter :authorize, only: [:edit, :update, :destroy, :create, :new]
def index
#posts = Post.where(:state => "published").order("created_at DESC")
end
def new
#post = Post.new
end
def show
#post = Post.find(params[:id])
redirect_to_good_slug(#post) and return if bad_slug?(#post)
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to dashboard_path
else
render 'new'
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(post_params))
redirect_to dashboard_path
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to dashboard_path
end
private
def post_params
params.require(:post).permit(:title, :text, :author, :short, :photo, :state)
end
end
Dashboard View:
</div>
<div class="fluid dash">
<div class="gridContainer clearfix">
<div class="fluid dash_post_list">
<h3>Manage Posts</h3>
<% #posts.each do |post| %>
<div class="fluid list-items">
<div class="fluid list_each">
| <%= post.id %> | <%= link_to post.title, post %>, <%= post.created_at.strftime('%b, %d') %> - <%= post.state %>
</div>
<div class="fluid crud">
<i class="fa fa-pencil-square-o"></i><%= link_to 'Edit', edit_post_path(post) %>
<i class="fa fa-times-circle"></i><%= link_to 'Delete', post_path(post),
method: :delete, data: { confirm: 'Are you sure?' } %>
</div>
</div>
<% end %>
</div>
<div class="fluid dash_right">
<div class="fluid create_new">
<h3>Create New</h3>
<%= link_to 'New Post', new_post_path %>
</div>
<div class="fluid alerts">
<h3>Alerts!</h3>
<% #comments.each do |comment| %>
<%= comment.post_id %>
<% end %>
</div>
</div>
</div>
</div>
DB Schema
create_table "comments", force: true do |t|
t.string "commenter"
t.text "body"
t.integer "post_id"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "flag", default: false
end
create_table "posts", force: true do |t|
t.string "title"
t.text "text"
t.datetime "created_at"
t.datetime "updated_at"
t.text "short"
t.text "author"
t.string "photo_file_name"
t.string "photo_content_type"
t.integer "photo_file_size"
t.datetime "photo_updated_at"
t.string "state"
end
The end goal is, when a user makes a new comment, add it to alerts in the dashboard where I can then take a link from the dashboard to the post and then approve or delete it.
Thanks you for any help you can give me.
So from you question, My understanding is that you cannot create a link to the post?
Forgetting ajax for now. This is how you like it to the post show page.
Dashboard:
-#comments.each do |comment|
.comment
=link_to comment.body, comment.post
and in the post show page
-#comment.each do |comment|
.comment
=link_to 'Approve', approve_comment_path(comment)
=link_to 'Delete', comment_path(comment), method: :delete
You will need a custom action to set it to approve or you can reuse the update action.