Ruby on Rails validate for multiple choice test - ruby-on-rails

What is the best way to validate a answer input by a user, validation rules below:
Examples of formats allowed 1, 2, 3, 4...to 12
The value is 2 answers for 12 choices
model:
class Questions < ApplicationRecord
belongs_to :user
validates :user, presence: true
validates :answers, presence: true
end
Html:
<h3>question</h3>
<% (1..12).each do |x| %>
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-danger btn-circle">
<input type="checkbox" name="question[answer][]" id="optionsCheckbox<%= x %>" value="<%= x %>" />
<%= x %>
</label>
</div>
<% end %>
</ul>
<%= f.button :submit, "submit", class: "btn btn-success" %>
in controller:
class QuestionsController < ApplicationController
skip_before_action :authenticate_user!, only: [ :new ]
before_action :find_question, only: [:show, :edit, :update, :destroy]
def create
#question = Question.new(ticket_params)
#question.user = current_user
if #question.save
redirect_to new_charge_path
else
render :new, alert: "Oops, something went wrong..."
end
end
def question_params
params.require(:question).permit(answer: [])
end
def find_question
#question = Question.find(params[:id])
end
end
answer is a string in questions table
It is an array, he have 12 choices and 2 possible responses. Like a multiple choice quiz.. I would just define the possible number of choice (2 choices)
That is my submit response in console:
Started POST "/questions" for 127.0.0.1 at 2016-05-24 18:26:08 +0200
Processing by QuestionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mAoBIf9jqDoungeeFKe6KitIf0ahAxhi6rVODmz6v1xGExYeVAVL8qXBfJj37KTpIkBBZJV2F1MRuBJKA==", "question"=>{"answer"=>["2", "8"], "commit"=>"Submit"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = ORDER BY "users"."id" ASC LIMIT 2 [["id", 6], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (27.0ms) INSERT INTO "questions" ("answer", "created_at", "updated_at", "user_id") VALUES (1, 2 ) RETURNING "id" [["answer", "[\"2\", \"8\"]"], ["created_at", 2016-05-24 16:26:08 UTC], ["updated_at", 2016-05-24 16:26:08 UTC], ["user_id", 6]]
(23.5ms) COMMIT
Redirected to http://localhost:3000/charge/new
Completed 302 Found in 112ms (ActiveRecord: 51.3ms)

Not sure I fully understand the requirement, but seems like something like this:
class Question...
validate :validate_answers
def validate_answers
unless answers.length == 2
errors.add(:answers, 'must have 2 selected')
end
end
end

Related

Rails Nested Form submits but does not persist the data

I'm trying to get my nested form to work. It's a form for a New Album with a space to write a review underneath. The form submits and the Album displays on the page, but the Review does not, it just appears blank. I only am getting one error in the log "Unpermitted parameter: reviews_attributes"
Log:
Started POST "/albums" for ::1 at 2020-04-19 12:10:58 -0400
Processing by AlbumsController#create as HTML
Parameters: {"authenticity_token"=>"jYHM+yeExcTJtENvjQBDsOMo8Ig1g5bRa+hYZ9kCkiI4NO3KP3xdV7SpSZ2IeIOp0wC+5WLxflu22NTIXtoibg==", "album"=>{"artist"=>"Blink 182", "title"=>"California", "avatar"=>#<ActionDispatch::Http::UploadedFile:0x00007fc372d8d5f0 #tempfile=#<Tempfile:/var/folders/26/p006tryd6yb9sp9rq446p07c0000gn/T/RackMultipart20200419-64975-1bp8ang.jpg>, #original_filename="71GfPCWJHXL._SL1500_.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"album[avatar]\"; filename=\"71GfPCWJHXL._SL1500_.jpg\"\r\nContent-Type: image/jpeg\r\n">, "reviews_attributes"=>{"0"=>{"title"=>"Blink 182 review", "date"=>"2020-04-19", "content"=>"NOT THE BLINK182 I KNOW AND LOVE WHERE IS ToM BRING BACK TOM"}}}, "commit"=>"Create Album"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 11], ["LIMIT", 1]]
↳ app/controllers/application_controller.rb:10:in `current_user'
Unpermitted parameter: :reviews_attributes
(0.1ms) begin transaction
↳ app/controllers/albums_controller.rb:29:in `create'
Album Create (0.4ms) INSERT INTO "albums" ("artist", "title", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["artist", "Blink 182"], ["title", "California"], ["created_at", "2020-04-19 16:10:58.838672"], ["updated_at", "2020-04-19 16:10:58.838672"]]
↳ app/controllers/albums_controller.rb:29:in `create'
ActiveStorage::Blob Load (0.3ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" INNER JOIN "active_storage_attachments" ON "active_storage_blobs"."id" = "active_storage_attachments"."blob_id" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 6], ["record_type", "Album"], ["name", "avatar"], ["LIMIT", 1]]
↳ app/controllers/albums_controller.rb:29:in `create'
ActiveStorage::Attachment Load (0.2ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 6], ["record_type", "Album"], ["name", "avatar"], ["LIMIT", 1]]
↳ app/controllers/albums_controller.rb:29:in `create'
ActiveStorage::Blob Create (0.3ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["key", "l22w59ulprgmmqrs025woawhi1h6"], ["filename", "71GfPCWJHXL._SL1500_.jpg"], ["content_type", "image/jpeg"], ["metadata", "{\"identified\":true}"], ["byte_size", 137504], ["checksum", "IXxJAt318tAPkwmaLBUW/A=="], ["created_at", "2020-04-19 16:10:58.852999"]]
↳ app/controllers/albums_controller.rb:29:in `create'
ActiveStorage::Attachment Create (0.4ms) INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES (?, ?, ?, ?, ?) [["name", "avatar"], ["record_type", "Album"], ["record_id", 6], ["blob_id", 11], ["created_at", "2020-04-19 16:10:58.856948"]]
↳ app/controllers/albums_controller.rb:29:in `create'
Album Update (0.2ms) UPDATE "albums" SET "updated_at" = ? WHERE "albums"."id" = ? [["updated_at", "2020-04-19 16:10:58.862445"], ["id", 6]]
↳ app/controllers/albums_controller.rb:29:in `create'
(3.3ms) commit transaction
↳ app/controllers/albums_controller.rb:29:in `create'
Disk Storage (2.5ms) Uploaded file to key: l22w59ulprgmmqrs025woawhi1h6 (checksum: IXxJAt318tAPkwmaLBUW/A==)
[ActiveJob] Enqueued ActiveStorage::AnalyzeJob (Job ID: 9114fadd-5897-4939-9cd6-1d0751f129b2) to Async(active_storage_analysis) with arguments: #<GlobalID:0x00007fc372ddf5d0 #uri=#<URI::GID gid://review-project/ActiveStorage::Blob/11>>
Redirected to http://localhost:3000/albums/6
Completed 302 Found in 63ms (ActiveRecord: 5.5ms | Allocations: 15980)
Nested form (albums/_form.html.erb)
<%= form_for(#album) do |f| %>
<% if #album.errors.any? %>
<ul>
<% #album.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= f.label :artist %>
<%= f.text_field :artist %>
<br><br>
<%= f.label :title %>
<%= f.text_field :title %>
<br><br>
<%= f.label "Album Image:" %><br>
<%= f.file_field :avatar %>
<br><br>
<h2>Write your review of the album</h2>
<%= f.fields_for :reviews do |ff| %>
<%= ff.label :title %>
<%= ff.text_field :title %>
<br>
<%= ff.label :date %>
<%= ff.date_field :date %>
<br>
<%= ff.label :content %>
<%= ff.text_area :content %>
<% end %>
<br>
<%= f.submit %>
<% end %>
<br><br><br>
<%= link_to "Back to Album", albums_path(#album) %>
albums controller:
class AlbumsController < ApplicationController
before_action :set_album, only: [:show, :edit, :update, :destroy]
before_action :must_login, only: [:new, :show, :create, :edit, :update, :destroy]
def index
#albums = Album.all
#user = current_user
end
def show
#review = #album.reviews.build
#review.user = current_user
#review.save
#reviews = Review.recent #scope
end
def new
#album = Album.new
#review = #album.reviews.build
#user = current_user
end
def create
##user = User.find(current_user.id)
#album = current_user.albums.build(album_params)
##album.user_id = current_user.id
#album.reviews.each { |r| r.user ||= current_user } # I'm using ||= so i can use the same code on update without changing reviews that already have a user
if #album.save
redirect_to album_path(#album)
else
render :new
end
end
def edit
#user = current_user
end
def update
##album = current_user.albums.build(album_params)
#album.user_id = current_user.id
if #album.update(album_params)
redirect_to album_path(#album), notice: "Your album has been updated."
else
render 'edit'
end
end
def destroy
#album.delete
#album.avatar.purge
redirect_to albums_path
end
private
def set_album
#album = Album.find(params[:id])
end
def album_params
params.require(:album).permit(:artist, :title, :avatar, :user_id, review_attributes:[:title, :date, :content])
end
end
Reviews controller
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
before_action :set_current_user, only: [:index, :show, :new, :edit, :destroy]
before_action :find_album, only: [:show, :create, :edit, :update, :destroy]
before_action :must_login, only: [:index, :show, :new, :create, :edit, :update, :destroy]
def index
#albums = Album.with_recent_reviews
end
def show
##reviews = Review.where("album_id = ?", params[:album_id])
end
def new
if params[:album_id] && #album = Album.find_by(id: params[:client_id])
#review = #album.reviews.build
else
redirect_to albums_path
end
end
def create
#review = current_user.reviews.build(review_params)
#review.album = #album
if #review.save
redirect_to album_path(#album)
else
#album = #review.album
render :new
end
end
def edit
end
def update
if #review.update(review_params)
redirect_to album_path(params[:album_id])
else
render 'edit'
end
end
def destroy
if current_user.id == #review.user_id
#album.reviews.find(params[:id]).destroy
redirect_to album_path(params[:album_id])
else
flash[:error] = "Unable to delete your review. Please try again."
redirect_to album_reviews_path(#review)
end
end
private
def set_review
#review = Review.find(params[:id])
end
def set_current_user
#user = current_user
end
def find_album
#album = Album.find(params[:album_id])
end
def review_params
params.require(:review).permit(:title, :date, :content, album_attributes:[:artist, :title, :user_id])
end
end
Album model:
class Album < ApplicationRecord
has_many :reviews
has_many :users, through: :reviews
has_one_attached :avatar
accepts_nested_attributes_for :reviews
validates_presence_of :artist
validates_presence_of :title
scope :with_recent_reviews, -> { includes(:reviews).where(reviews: { date: [(Date.today - 7.days)..Date.tomorrow] }) } #scope relies on include method and custom query on related model (reviews)
end
Review model:
class Review < ApplicationRecord
belongs_to :album, optional: true
belongs_to :user
validates_presence_of :content
validates :title, presence: true, uniqueness: true
validates :date, presence: true
accepts_nested_attributes_for :album
scope :recent, -> { where("date(date) >= ?", Date.today - 7.days) } #scope
end
Routes.rb
Rails.application.routes.draw do
get '/auth/:provider/callback' => 'sessions#omniauth'
get 'auth/failure', to: redirect('/')
get '/signup' => 'users#new', as: 'signup'
post '/signup' => 'users#create'
get '/signin' => 'sessions#new'
post '/signin' => 'sessions#create'
get '/signout' => 'sessions#destroy'
post '/logout', to: "sessions#destroy"
resources :albums do
resources :reviews, except: [:index]
end
resources :users, only: [:show, :destroy]
resources :reviews, only: [:index]
root to: "albums#index"
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
The reviews association is a has_many so fields_for should use the plural form.
= f.fields_for :reviews do |ff|
So that way rails creates that parameter reviews_attributes you are permitting.
If you are still having problems with that change show the new error and stacktrace.
EDIT:
If you want to set the current user id as the user of the review (similar to what you did to assign the current user as the album creator), you can assign that before saving the record:
def create
# #user = User.find(current_user.id) you don need this, you already have the user at current_user, no need to find it again
#album = current_user.albums.build(album_params)
# #album.user = current_user you don't need this, current_user.albums.build already sets this
#album.reviews.each { |r| r.user ||= current_user } # I'm using ||= so you can use the same code on update without changing reviews that already have a user
if #album.save
redirect_to album_path(#album)
else
render :new
end
end
And also, remove the :user_id and :album_id from the permitted parameters for reviews_attributes, you don't want users to exploit that assignation adding those parameters that you are actually not using

nested attributes, update and create diferents tables and fields at the same time

In my application I have a goal, this goal belongs to a company and has start and end date, when it is created automatically values ​​are created in the day table that go between the start date and end date defined in the goal, this field has the name of date_day.
In addition to this field each Day has a value field, which is the total amount collected during the day, this value is not automatically defined when the user creates a goal, ie it comes null, it is necessary to edit the Day to define how much was collected in that day. When I go to do the edition of the day I would like to add an employee in my model Salesman, that employee would be tied to that Day.
I managed through the Day edit form to add an employee, but that's it, the edition of the day value is not updated and I also have no increment of the DaySalesman tables.
My models:
class Day < ApplicationRecord
belongs_to :goal
has_many :day_salesmen, dependent: :destroy
has_many :salesmen, through: :day_salesmen
validates_presence_of :date_day, :goal_id
accepts_nested_attributes_for :day_salesmen
end
class DaySalesman < ApplicationRecord
belongs_to :day
belongs_to :salesman
accepts_nested_attributes_for :salesman
end
class Salesman < ApplicationRecord
belongs_to :company
has_many :goal_salesmen, dependent: :destroy
has_many :goals, through: :goal_salesmen
has_many :day_salesmen, dependent: :destroy
has_many :days, through: :day_salesmen
end
Below are my controllers and the result of my controllers:
class DaysController < ApplicationController
before_action :find_day, only: [:show, :edit, :update]
before_action :find_company, only: [:show, :edit]
def index
#day = current_owner.companies.find(params[:company_id]).goal.find(params[:goal_id]).days
end
def show
end
def edit
#dayup = Day.new
#day_salesmen = #dayup.day_salesmen.build
#salesman = #day_salesmen.build_salesman
end
def update
if #day.update(params_day)
flash[:notice] = "Day updated!"
redirect_to company_salesman_path(:id => #day.id)
else
flash.now[:error] = "Could not update day!"
render :edit
end
end
private
def find_company
#company = Company.find(params[:company_id])
end
def find_day
#day = Day.find(params[:id])
end
def params_day
params.require(:day).permit(:value, day_salesman_attributes: [:id, salesman_attributes:[:id, :name]]).merge(goal_id: params[:goal_id])
end
end
Salesman:
class SalesmenController < ApplicationController
before_action :find_salesman, only: [:edit, :update, :destroy]
def index
#salesmen = current_owner.companies.find(params[:company_id]).salesman
#company = Company.find(params[:company_id])
end
def create
#salesman = Salesman.new(params_salesman)
if #salesman.save
flash[:notice] = "Salesman saved!"
else
flash.now[:error] = "Cannot create salesman!"
render :new
end
end
def update
if #salesman.update(params_salesman)
flash[:notice] = "salesman updated!"
else
flash.now[:error] = "Could not update salesman!"
render :edit
end
end
def destroy
#salesman.destroy
end
private
def find_salesman
#salesman = Salesman.find(params[:id])
end
def params_salesman
params.require(:day).require(:salesman).permit(:name, :id).merge(company_id: params[:company_id])
end
end
My controller day view edit is:
<%= render "shared/sidebar2" %>
<div class="container">
<div class="row">
<div class="col s10 offset-s2">
<div class="row">
<h4>Edit day</h4>
<%= form_for(#dayup, url: company_salesmen_path) do |f| %>
<%= f.label :value_of_day %>
<%= f.number_field :value %>
<%= f.fields_for :day_salesman do |ff| %>
<%= f.fields_for :salesman do |fff| %>
<%= fff.label :names_of_salesmen %>
<%= fff.text_field :name %>
<% end %>
<% end %>
<%= f.submit "Create" %>
<% end %>
</div>
</div>
</div>
</div>
My log when I click the submit button on this view is:
Started POST "/companies/1/salesmen" for 172.26.0.1 at 2017-10-31 22:11:56 +0000
Cannot render console from 172.26.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by SalesmenController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"CQaRlVPYsPK8ilOQJCdw04htgXFkUQOKEVsOIDAmRXOM1X3QO+HfOzY2PGnmItMTK9jRj0YFZDYRJPg7qBV27A==", "day"=>{"value"=>"100", "salesman"=>{"name"=>"Renata"}}, "commit"=>"Create", "company_id"=>"1"}
[1m[36mOwner Load (0.7ms)[0m [1m[34mSELECT "owners".* FROM "owners" WHERE "owners"."id" = $1 ORDER BY "owners"."id" ASC LIMIT $2[0m [["id", 1], ["LIMIT", 1]]
[1m[35m (12.4ms)[0m [1m[35mBEGIN[0m
[1m[36mCompany Load (0.4ms)[0m [1m[34mSELECT "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT $2[0m [["id", 1], ["LIMIT", 1]]
[1m[35mSQL (111.1ms)[0m [1m[32mINSERT INTO "salesmen" ("name", "company_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"[0m [["name", "Renata"], ["company_id", 1], ["created_at", "2017-10-31 22:11:56.344478"], ["updated_at", "2017-10-31 22:11:56.344478"]]
[1m[35m (36.3ms)[0m [1m[35mCOMMIT[0m
No template found for SalesmenController#create, rendering head :no_content
Completed 204 No Content in 436ms (ActiveRecord: 161.0ms)
I wanted to know how through this day's edit form I can update the value for day, create a new employee and associate it to that day through the Daysalesmen table.

rails 4 ajax edit form not firing update

I'm trying to update comments on a post in place, and I can get the edit form partial to render correctly, but trying to save does nothing--there's no console error, no error in the server logs, and the record isn't saved.
edit.js.erb
$('#comment-<%= #post.id %>-<%= #comment.id %>').find('.comment-content').html("<%= j render partial: 'comments/comment_form', locals: { comment: comment, post: post } %>");
comments/_comment_form.html.erb
<div class="comment-form col-sm-11">
<%= form_for([post, comment], :remote => true) do |f| %>
<%= f.text_area :content, class: "comment_content", id: "comment_content_#{post.id}" %>
</div>
<div class="col-sm-1">
<%= f.submit "Save", class: "btn btn-primary btn-xs" %>
<% end %>
</div>
update.js.erb
$('comment-form').hide();
$('#comment-<%= #post.id %>-<%= #comment.id %>').find('.comment-content').html('<%= #comment.content %>');
routes.rb
resources :users do
end
resources :posts do
resources :comments
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments, dependent: :destroy
end
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
Below is the last thing in the server log when the edit_post_comment_path link is clicked; literally nothing happens when save is clicked in the edit form partial, and a test alert in update.js.erb doesn't fire.
Started GET "/posts/471/comments/30/edit" for 72.231.138.82 at 2016-07-17 01:25:27 +0000
Processing by CommentsController#edit as JS
Parameters: {"post_id"=>"471", "id"=>"30"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 2]]
RailsSettings::SettingObject Load (0.2ms) SELECT "settings".* FROM "settings" WHERE "settings"."target_id" = ? AND "settings"."target_type" = ? [["target_id", 2], ["target_type", "User"]]
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? ORDER BY "posts"."created_at" DESC LIMIT 1 [["id", 471]]
Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."user_id" = ? AND "comments"."id" = ? LIMIT 1 [["user_id", 2], ["id", 30]]
Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."id" = ? LIMIT 1 [["id", 30]]
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 2]]
Rendered comments/_comment_form.html.erb (1.2ms)
Rendered comments/edit.js.erb (13.8ms)
Completed 200 OK in 102ms (Views: 34.4ms | ActiveRecord: 1.7ms)
I have similar create, destroy and custom actions (including an update_attributes method) that are firing correctly, so I'm not sure why this form can't submit. Any help is appreciated, I'm very new to js.
Update with the comment controller actions:
class CommentsController < ApplicationController
before_action :set_post
before_action :user_signed_in?, only: [:create, :destroy]
before_action :authenticate_user!, only: [:create, :edit, :new, :destroy, :update]
before_action :correct_user, only: [:edit, :update, :destroy]
...
def edit
#comment = Comment.find(params[:id])
respond_to do |format|
format.js { render 'comments/edit', locals: {comment: #comment, post: #post } }
end
end
def update
#comment = Comment.find(params[:id])
respond_to do |format|
format.js
end
if #comment.update_attributes(comment_params)
#comment.toggle!(:edited)
flash[:success] = "Comment edited!"
redirect_to root_path
else
render 'edit'
end
end
private
def comment_params
params.require(:comment).permit(:content, :user_id, :post_id)
end
def set_post
#post = Post.find(params[:post_id])
end
def correct_user
#comment = current_user.comments.find_by(id: params[:id])
redirect_to root_url if #comment.nil?
redirect_to root_url if #comment.userlocked?
end
end
I've updated the edit form with :method => :put, to no effect, and thanks for the typo catch #Emu.
As you're submitting an edit form, you should define the method in the form like:
<%= form_for([post,comment ], :remote=>true, :method => :put) do |f| %>
Also, in the update.js.erb the following line missing the "." or "#"
$('.comment-form').hide();

Ruby on Rails - Adding comments to post

Trying to learn RoR. Currently adding comments to posts by user. So far I have a posts model, comments model and post_comments model (to associate the two). So for in 'rails console' I can run: (say I set p = Post.first and c = Comment.first)
p.comments << c
This forms an association so it works in the console. I just can't seem to get the comments to form this association from the UI. So far I am creating the comments at "comments/new" (not sure if this is the issue. Do they need to be created on the "show view" for "post").
Here are some code snippets
Controllers
comments_controller.rb
class CommentsController < ApplicationController
def index
#comment = Comment.all
end
def new
#comment = Comment.new
end
def create
#comment = Comment.new(commentParams)
if #comment.save
flash[:success] = "Comment successfully added"
redirect_to comments_path(#comment)
else
render 'new'
end
end
def show
#comment = Comment.find(params[:id])
end
private
def commentParams
params.require(:comment).permit(:comment)
end
end
posts_controller
class PostsController < ApplicationController
before_action :setPost, only: [:edit, :update, :show, :destroy, :sold]
before_action :requireUser, except: [:index, :show]
before_action :requireSameUser, only: [:edit, :update, :destroy, :sold]
def index
#posts = Post.paginate(page: params[:page], per_page: 20)
end
def new
#post = Post.new
end
def create
#post = Post.new(postParams)
#post.user = currentUser
if #post.save
flash[:success] = "Post successfully added."
redirect_to post_path(#post)
else
render 'new'
end
end
def update
if #post.update(postParams)
flash[:success] = "Post successfully updated."
redirect_to post_path(#post)
else
render 'edit'
end
end
def show
end
def edit
end
def sold
#post.toggle(:sold)
#post.save
redirect_to post_path(#post)
end
def destroy
#post.destroy
flash[:danger] = "Item successfully deleted."
redirect_to posts_path
end
private
def postParams
params.require(:post).permit(:title, :price, :description, category_ids:[])
end
def setPost
#post = Post.find(params[:id])
end
def requireSameUser
if currentUser != #post.user and !currentUser.admin
flash[:danger] = "You can only edit or delete your own items"
redirect_to root_path
end
end
end
Models
comment.rb
class Comment < ActiveRecord::Base
belongs_to :post_comments
belongs_to :user
belongs_to :post
end
post_comment.rb
class PostComment < ActiveRecord::Base
belongs_to :post
belongs_to :comment
end
post.rb
class Post < ActiveRecord::Base
belongs_to :user
has_many :post_categories
has_many :categories, through: :post_categories
has_many :post_comments
has_many :comments, through: :post_comments
validates :title, presence: true,
length: { minimum: 4, maximum: 20 }
validates :description, presence: true,
length: { maximum: 1000 }
validates :user_id, presence: true
end
Views
posts/show.html.erb
<p>Comments: <%= render #post.comments %></p>
This renders the partial below
comments/_comment.html.erb
<%= link_to comment.name, comment_path(comment) %>
Finally is the new comment page as it is.
comments/new.html.erb
<h1>New Comment</h1>
<%= render 'shared/errors', obj: #comment %>
<div class="row">
<div class="col-xs-12">
<%= form_for(#comment, :html => {class: "form-horizontal", role: "form"}) do |f| %>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :comment %>
</div>
<div class="col-sm-8">
<%= f.text_area :comment, rows: 3, class: "form-control", placeholder: "Please enter a comment", autofocus: true %>
</div>
</div>
<div class="form-group">
<div class="center col-sm-offset-1 col-sm-10">
<%= f.submit class: "btn btn-primary btn-lg" %>
</div>
</div>
<% end %>
Any help would be greatly received.
Update
Log
Started GET "/posts/2" for ::1 at 2016-01-15 12:39:55 +0000
Processing by PostsController#show as HTML
Parameters: {"id"=>"2"}
[1m[36mPost Load (0.1ms)[0m [1mSELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT 1[0m [["id", 2]]
[1m[35mUser Load (0.1ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
[1m[36m (0.1ms)[0m [1mSELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = ?[0m [["user_id", 1]]
[1m[35mCategory Exists (0.1ms)[0m SELECT 1 AS one FROM "categories" INNER JOIN "post_categories" ON "categories"."id" = "post_categories"."category_id" WHERE "post_categories"."post_id" = ? LIMIT 1 [["post_id", 2]]
[1m[36mCategory Load (0.0ms)[0m [1mSELECT "categories".* FROM "categories" INNER JOIN "post_categories" ON "categories"."id" = "post_categories"."category_id" WHERE "post_categories"."post_id" = ?[0m [["post_id", 2]]
Rendered categories/_category.html.erb (0.2ms)
[1m[35mComment Load (0.1ms)[0m SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 2]]
Rendered comments/_comment.html.erb (0.1ms)
Rendered posts/show.html.erb within layouts/application (6.5ms)
[1m[36mCategory Load (0.1ms)[0m [1mSELECT "categories".* FROM "categories"[0m
Rendered layouts/_navigation.html.erb (0.9ms)
Rendered layouts/_messages.html.erb (0.1ms)
Rendered layouts/_footer.html.erb (0.1ms)
Completed 200 OK in 52ms (Views: 50.3ms | ActiveRecord: 0.5ms)
As a comment can belong to a single post only, you do not need an association table (post_comments). You just need a simple one-to-many relationship.
Your post comment would be:
class Post < ActiveRecord::Base
has_many :comments
...
end
And comment would be like this:
class Comment < ActiveRecord::Base
belongs_to :post
...
end
Just make sure that you have the necessary post_id column in the comments table (you can check the db/schema.rb file). If that is missing, you can use the following migration to add it:
class AddPostIdToComments < ActiveRecord::Migration
def change
add_column :comments, :post_id, :integer
add_index :comments, :post_id
end
end
You also need to make sure you keep somewhere the reference to the post, whenever a user tries to create a comment to a post. You can add this in a hidden field to your comments/new.html.erb template. You could set the hidden field in the new action, in PostsController, after passing it through the URL.
So, in your posts/show.html.erb template you would have:
<%= link_to "Add Comment", new_comment_path(post_id: #post.id) %>
In your new action, in PostsController:
def new
#comment = Comment.new(post_id: params[:post_id])
end
And finally the hidden field in your form would be:
<%= f.hidden_field :post_id %>
Finally, add the post_id parameter to the list of permitted parameters in CommentsController.
instead of
In your new action, in PostsController:
def new
#comment = Comment.new(post_id: params[:post_id])
end
you can use this in the form views
<%= form.hidden_field :post_id, value: "#{params[:post_id]}" %>

Can't update user in Ruby on Rails

I am using ruby on rails try to update user information, but when I submit, the console will show an error saying the user exists and redirect to the correct page. What's wrong with my code?
The error message:
Started PATCH "/users/6" for ::1 at 2015-06-08 21:27:00 -0500
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"sJm38g36DAYDo4eXdpcIPRX0e40Jp6cECmMwEvhCAEhTlDwwmmgOfXZqeczglNmJ4K9pQXiyXAsRsgP/C8lScg==", "name"=>"test123", "department"=>"123", "commit"=>"Update User", "id"=>"6"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 6]] CACHE (0.0ms)
SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "6"]] (0.1ms)
begin transaction
User Exists (0.2ms)
SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'test#test.com' AND "users"."id" != 6) LIMIT 1 (0.1ms)
rollback transaction
Redirected to http://localhost:3000/users/6
Completed 302 Found in 9ms (ActiveRecord: 0.5ms)
Started GET "/users/6" for ::1 at 2015-06-08 21:27:00 -0500
Processing by UsersController#show as HTML
Parameters: {"id"=>"6"} User Load (0.1ms)
SELECT "users".* FROM "users" WHERE "users"."id"
= ? LIMIT 1 [["id", 6]]
Rendered users/show.html.erb within layouts/application (0.1ms)
User Load (0.2ms)
SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 6]] CACHE (0.0ms)
SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 6]]
Completed 200 OK in 66ms (Views: 64.3ms | ActiveRecord: 0.3ms)
The edit page
<h1 class="center">Edit name</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_tag "/users/#{#user.id}", :method => 'patch' do %>
<p>
<%= label_tag :name %>
<%= text_field_tag :name, #user.name %>
</p>
<p>
<%= label_tag :department %>
<%= text_field_tag :department, #user.dept %>
</p>
<input type="submit" name="commit" value="Update User">
<% end %>
</div>
</div>
The controller is like this
class UsersController < ApplicationController
before_action :authorize, only: [:show, :edit, :update]
def authorize
#user = User.find_by(id: params[:id])
if #user.blank? || session[:user_id] != #user.id
redirect_to root_url, notice: "Nice try!"
end
end
def new
#user = User.new
end
def show
end
def edit
end
def update
#user = User.find_by(id: params[:id])
#user.name = params[:name]
#user.dept = params[:department]
#user.save
redirect_to user_path(#user.id)
end
def create
#user = User.new(email: params[:email],
name: params[:name],
password: params[:password],
role: params[:role],
dept: params[:dept])
if #user.save
redirect_to root_url, notice: "Thanks for signing up."
else
render "new"
end
end
end
The router concerning this part is like:
# sign up
get '/signup' => 'users#new'
post '/users' => 'users#create'
get '/users/:id' => 'users#show', as: :user
get '/users/:id/edit' => 'users#edit', as: 'edit_user'
patch '/users/:id' => 'users#update'
The problem is in the form_tag,it should be like this
<%= form_tag({:action => :update}, {:method => :patch}) do %>
Also your code for form_tag looks vulnerable. Changing it to like this will be better.
<%= form_tag update_user_path(#user) do %>
or
<%= form_tag user_path(#user), :method => :patch do %>
Are you using rails 4?
You should update your controller to conform to strong_parameters if you are.
def update
#user = User.find(params[:id)
if #user.update_attributes(user_params)
redirect_to user_path(#user.id)
else
render :edit
end
end
private
def user_params
params.require(:user).permit(:name, :dept)
end
Doing this will mean that you have to wrap your name and dept params inside a user scope, eg.
user: { name: "Howard Moon", dept: "Zookeeper" }
But is the standard way to handle params in the controller.
Hope this helps!
EDIT: Link to Strong Parameters which does a better job at explaining this than I can. Haha

Resources