I am trying to add form validation to a simple blog post form. Currently I am testing for presence of title and description and if I submit an empty form it gives me the errors, but if I fill out the form it still gives me the errors. When I use byebug in my create action for the post_controller it shows the contents of the title and description that I enter but says permitted false.
Here is the form partial:
<%= form_with(model: post, local: true) do |f| %>
<% if post.errors.any? %>
<div class="row center-align">
<div class="card-panel alert alert-danger">
<span class="white-text">
<strong><%= pluralize(post.errors.count, "error") %></strong>
occured:
<ul>
<% post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</span>
</div>
</div>
<% end %>
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :description %>
<%= f.text_area :description, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :header_image %>
<%= f.file_field :header_image %>
</div>
<div class="mt-1">
<% if post.header_image.present? %>
<small>Current Image</small>
<%= image_tag(post.header_image, width: '100%') %>
<% end %>
</div>
<div class="actions">
<%= f.submit class: 'btn btn-primary mt-1 form-control' %>
</div>
<% end %>
Here is the post_controller:
module Authors
class PostsController < AuthorsController
before_action :set_post, only: [:edit, :update, :destroy]
# GET /posts
def index
#posts = current_author.posts
end
# GET /posts/new
def new
#post = current_author.posts.build
end
# GET /posts/1/edit
def edit
#element = #post.elements.build
end
# POST /posts
def create
#post = current_author.posts.build
if #post.save
redirect_to edit_post_path(#post), notice: 'Post was successfully created.'
else
render :new
end
end
# PATCH/PUT /posts/1
def update
if #post.update(post_params)
redirect_to edit_post_path(#post), notice: 'Post was successfully updated.'
else
render :edit
end
end
# DELETE /posts/1
def destroy
#post.destroy
redirect_to posts_url, notice: 'Post was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = current_author.posts.find(params[:id])
end
# Only allow a list of trusted parameters through.
def post_params
params.require(:post).permit(:title, :description, :header_image)
end
end
end
And her is the post.rb:
class Post < ApplicationRecord
belongs_to :author
has_many :elements
has_one_attached :header_image
validates :title, :description, presence: true
end
The post form was working fine until I tried adding the validations.
When I add buy but and test it I get the following when I run params:
#<ActionController::Parameters {"authenticity_token"=>"1hQyn6kWCiCwAaeaUa1PD9q76D29RF-56LKof1uC64auGtNvA2VCZ-mDLdY7EjCYP9zWpLLvfkgkE33AUZP7Ng", "post"=>{"title"=>"Testing", "description"=>"test"}, "commit"=>"Create Post", "controller"=>"authors/posts", "action"=>"create"} permitted: false>
(byebug)
In the create method, you're not instantiating the Post object with post_params. So, #post.title and #post.description are nil causing the validations to fail.
#post = current_author.posts.build(post_params)
Related
I'm trying to include a form partial for the model request in another partial view.
I have a model called request.
class CreateRequests < ActiveRecord::Migration[5.0]
def change
create_table :requests do |t|
t.string :name
t.string :email
t.string :phone
t.string :product
t.string :details
t.timestamps
end
end
end
I have my requests_controller.rb
class RequestsController < ApplicationController
before_action :set_request, only: [:show, :edit, :update, :destroy]
# GET /requests
# GET /requests.json
def index
#requests = Request.all
end
# GET /requests/1
# GET /requests/1.json
def show
end
# GET /requests/new
def new
#request = Request.new
end
# GET /requests/1/edit
def edit
end
# POST /requests
# POST /requests.json
def create
# Create the user from params
#request = Request.new(request_params)
if #email.save
# Deliver the signup email
RequestNotifierMailer.send_email(#request).deliver
flash[:success] = "Thanks! We'll be in touch soon!"
redirect_to :action => 'new'
else
render :action => 'new'
end
end
# PATCH/PUT /requests/1
# PATCH/PUT /requests/1.json
def update
respond_to do |format|
if #request.update(request_params)
format.html { redirect_to #request, notice: 'Request was successfully updated.' }
format.json { render :show, status: :ok, location: #request }
else
format.html { render :edit }
format.json { render json: #request.errors, status: :unprocessable_entity }
end
end
end
# DELETE /requests/1
# DELETE /requests/1.json
def destroy
#request.destroy
respond_to do |format|
format.html { redirect_to requests_url, notice: 'Request was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_request
#request = Request.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def request_params
params.require(:request).permit(:name, :email, :phone, :product, :details)
end
end
The form partial I have requests/_form.html.erb
<%= form_for(request) do |f| %>
<% if request.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(request.errors.count, "error") %> prohibited this request from being saved:</h2>
<ul>
<% request.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :phone %>
<%= f.text_field :phone %>
</div>
<div class="field">
<%= f.label :product %>
<%= f.text_field :product %>
</div>
<div class="field">
<%= f.label :details %>
<%= f.text_field :details %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And I included the resource in my config/routes.rb by adding:
resources :requests
I'm trying to include requests/form into static_pages/_requestquote.html.erb:
<div class="actionsHolder">
<div class="buyHolder">
<h2>Starting from <%= price %></h2>
<h3 id="myBtn">
<a href="#">
Request for Quote
</a>
</h3>
</div>
</div>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">×</span>
<%= render :partial => "requests/form" %>
</div>
</div>
However I'm getting the error:
NoMethodError in Products#bigmikepopular113
Showing /Users/beckah/Documents/projects/Envirovacs/app/views/requests/_form.html.erb where line #1 raised:
undefined method `model_name' for #<ActionDispatch::Request:0x007fee89b49040>
Extracted source (around line #1):
1
2
3
4
5
6
<%= form_for(request) do |f| %>
<% if request.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(request.errors.count, "error") %> prohibited this request from being saved:</h2>
<ul>
Products#bigmikepopular113 is the controller view where the original partial is conluded, so essentially the structure is
products/bigmikepopular113.html.erb -> static_pages/_requestquote.html.erb -> requests/_form.html.erb
What can I do differently to make sure I can have an embedded partial?
From what I can see, you're not initializing the creation of a New request anywhere. Only under the new action inside your controller, but there's no view corresponding to that action. You only have the view _form.html.erb.
To clarify: every action in your controller, must correspond to a view, sharing the same name as the action inside the controller. Even if it's a partial.
So, def new automatically points to new.html.erb. def index to index.html.erb and so on.
You therefor have two options. Either create a def form inside the controller, and set up the routes in your routes.rb accordingly, or simply write the statement directly inside the view instead of in the controller:
Remove:
<%= form_for(request) do |f| %>
Instead:
<%= form_for Request.new do |f| %>
Hope that'll help.
I'm trying to create a post that will contain it's forum_id that the form resides in. I need both ID to save in the object to achieve that.
I initialize a new #post in the new action using #post = Forum.find(params[:forum_id]).posts.build
Which will spit out unsaved instance of a post containing it the forum_id just as intended.
I then fill out my form here:
<%= form_for #post, :url => {:controller => "posts", :action => "create"} do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title, class: "form-control" %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_area :description, class: "form-control" %>
</div>
<div class="actions">
<%= f.submit class: "btn btn-primary" %>
</div>
<% end %>
And when I click the submit button and inspect post_params with byebug after the line #post = Post.new(post_params) in the create action, only the :title and :description come through. The forum_id is lost in between actions and I cannot the save #post without it. I have :forum_id whitelisted in my post_params but it's not coming through. I would think that if an instance of post is created in the new action with a forum_id that it should persist into the create action inside post_params but something is wrong here. Here is the relevant information that might help with my issue.
My model's relationships:
# User model
has_many :forums
has_many :posts
# Forum model
belongs_to :user
has_many :posts
# Post model
belongs_to :user
belongs_to :forum
# post_controller
def new
#post = Forum.find(params[:forum_id]).posts.build
end
Post Controller
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
...
# Rest of actions
...
def post_params
params.require(:post).permit(:title, :description, :forum_id, :user_id)
end
end
Form doesn't submit forum_id because it doesn't exist on there
I think you need to add this to that form
<%= f.hidden_field :forum_id %>
I'm creating a rails app that allows someone to create an album by adding a title, a description and several pictures. However, I only find on the web a way that needs two controller, one to create the album and another to attach the images on it.
I'm now used to Rails even though I know I still have lots of things to learn.
So far, I managed to allow the user to create something that only allows him to upload to only upload one photo.
So I changed the form part where the file is uploaded, and now, it doesn't save anything...
Here are my files:
_form.html.erb
<%= form_for #pet, html: { multipart: true } do |f| %>
<% if #pet.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#pet.errors.count, "error") %> prohibited this album from being saved:</h2>
<ul>
<% #pet.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :image %><br>
# added :multiple => true
<%= f.file_field :image, :multiple => true %>
<% if f.object.image? %>
<%= image_tag f.object.image.thumb.url %>
<%= f.label :remove_image %>
<%= f.check_box :remove_image %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
pets_controller.rb
class PetsController < ApplicationController
before_action :set_pet, only: [:show, :edit, :update, :destroy]
# GET /pets
def index
#pets = Pet.all
end
# GET /pets/1
def show
end
# GET /pets/new
def new
#pet = Pet.new
end
# GET /pets/1/edit
def edit
end
# POST /pets
def create
#pet = Pet.new(pet_params)
if #pet.save
redirect_to #pet, notice: 'Album was successfully created.'
else
render :new
end
end
# PATCH/PUT /pets/1
def update
if #pet.update(pet_params)
redirect_to #pet, notice: 'Album was successfully updated.'
else
render :edit
end
end
# DELETE /pets/1
def destroy
#pet.destroy
redirect_to pets_url, notice: 'Album was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_pet
#pet = Pet.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def pet_params
params.require(:pet).permit(:name, :description, :image)
end
end
So my summed up question is the following: is there a simple way to allow my form to save multiple images inside an array where it would be easy to show them up?
Thank you in advance
Im trying to add comments to my topics model the same way you can add comments to posts on my app. i currently have to partials for comments _comment.html.erb and _form.html.erb
_comment :
<%= content_tag :div, class: 'media', id: "comment-#{comment.id}" do %>
<div class= "media">
<div class= "media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if user_is_authorized_for_comment?(comment) %>
| <%= link_to "Delete", [comment.post, comment], method: :delete %>
<% end %>
</small>
<p> <%= comment.body %></p>
</div>
</div>
<% end %>
_form :
<h4>Add a comment</h4>
<%= form_for [post, comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
my topic show is :
#DISPLAY Topic comments here
<h3> Comments</h3>
<%= render #topic.comments %>
</div>
<% if current_user %>
<%= render 'comments/form', comment: Comment.new, post: #post %>
<% end %>
#------
comment controller :
def create
#post = Post.find(params[:post_id])
comment = #post.comments.new(comment_params)
comment.user = current_user
if comment.save
flash[:notice] = "Comment saved successfully."
redirect_to [#post.topic, #post]
else
flash[:alert] = "Comment failed to save."
redirect_to [#post.topic, #post]
end
end
def destroy
#post = Post.find(params[:post_id])
comment = #post.comments.find(params[:id])
if comment.destroy
flash[:notice] = "Comment was deleted"
redirect_to [#post.topic, #post]
end
end
i have updated the routes for topic comments :
resources :topics do
resources :posts, except: [:index]
resources :comments, only: [:create, :destroy]
end
my question is do i need to create a separate partial to add comments to topics or can i update my _comment partial to work for both post and topic comments . and how can i accomplish this ?
Models
You'll need a polymorphic association on the Comment model:
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
#app/models/topic.rb
class Topic < ActiveRecord::Base
has_many :comments, as: :commentable
end
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
Controllers
This will allow you to save the comments for your various models, the controllers / flow coming secondary:
#config/routes.rb
resources :topics, :posts do
resources :comments, only: [:create, :destroy] #-> url.com/topics/:topic_id/comments
end
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
id = params[:post_id] || params[:topic_id]
if params[:post_id]
#parent = Post.find id
elsif params[:topic_id]
#parent = Topic.find id
end
#comment = #parent.comments.find params[:id]
#comment.save
end
def destroy
#parent = params[:post_id] || params[:topic_id]
#comment = #parent.comments.new comment_params
#comment.destroy
end
private
def comment_params
params.require(:comment).permit(:x, :y)
end
end
Because you're passing the data to the comments controller, you'll only need to evaluate which #parent you're working with.
Views
For your views, you need to pass locals to your _form partial:
#app/views/posts/show.html.erb
<%= render "comments/form", locals: {parent: #post} %>
#app/views/comments/_form.html.erb
<%= form_for [parent, parent.comments.new] do |f| %>
I had this road-block as well and here is what I came up with that passed. I must first give #Richard Peck applause for getting my wheels turning, so thank you :).
Models
Did not implement a polymorphic association. Stuck with has_many and belongs_to, nothing more
Partials
_comment.html.erb
Set up the delete partial to accept "parent" as a local
<div class="media">
<div class="media-body">
<small>
<%= comment.user.name %>
commented
<%= time_ago_in_words(comment.created_at) %>
ago
<% if user_is_authorized_for_comment_via_post?(comment) %>
|
<%= link_to "Delete", [parent, comment], method: :delete %>
<% end %>
</small>
<p>
<%= comment.body %>
</p>
</div>
</div>
_form.html.erb
same idea as _comment.html.erb, see above
<h4>Add a comment</h4>
<%= form_for [parent, comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
Setting up CommentController
...
def create
# upon clicking on create, determine what param id is passed
if params[:post_id]
# if it is a post id, set instance of post id as #parent
#parent = Post.find(params[:post_id])
elsif params[:topic_id]
# if it is a topic id, set instance of topic id as #parent
#parent = Topic.find(params[:topic_id])
end
# create instance as #comment. Build/create
# comment belonging to #parent (Topic or Post)
#comment = #parent.comments.build(comment_params)
# The comment must be associated to the current user.
# A comment must have a user, and value of user within instance of #comment
# is currently nil. Set user id as current user
#comment.user = current_user
# save comment to database
if #comment.save
# direction of save through if and elsif
# Redirection depends on the comment's parent.
# .is_a? method determines if it is of a certain class. Here, is #parent
# of class Post? Is #parents is the same parent id passed through params?
if #parent.is_a?(Post) # template error with this included: (== params[:post_id])
flash[:notice] = 'Comment saved successfully'
redirect_to [#parent.topic, #parent]
# if not part of the class Post, is it a Topic? If so, save here and
# redirect to the topic after save
elsif #parent.is_a?(Topic)
flash[:notice] = 'Comment saved successfully'
redirect_to #parent
end
end
end
def destroy
comment = Comment.find(params[:id])
# #topic = Topic.find(params[:topic_id])
# topic_comment = #topic.comments.find(params[:id])
# #post = Post.find(params[:post_id])
# post_comment = #post.comments.find(params[:id])
if comment.destroy
flash[:notice] = 'Comment was deleted'
redirect_to :back
else
flash[:alert] = "Comment counld't be deleted. Try again"
redirect_to :back
end
end
...
Passing in Comments from topic/show and post/show
topic/show
Note: notice how locals are passed into the controller from here
...
<div class="row">
<%= render 'comments/form', comment: Comment.new, parent: #topic %>
</div>
<% #topic.comments.each do |comment| %>
<%= render partial: 'comments/comment', locals: { parent: #topic, comment: comment } %>
<% end %>
...
post/show
<% if current_user %>
<% #post.comments.each do |comment| %>
<%= render partial: 'comments/comment', locals: { parent: #post, comment: comment } %>
<% end %>
<% end %>
<% if current_user %>
<%= render 'comments/form', comment: Comment.new, parent: #post %>
<% end %>
Hope this helps.
Updated version - I have taken the initial advice provided (thanks for that!) but I'm still having the same issue. I have updated everything below.
I have two models, products that belong to a store.
I'm attempting to display a related object's column (Store.name) in a couple of views for products that belong to a store and can't seem to get the store to save correctly. Please note: Still very new to this and learning.
Model for Product:
class Product < ActiveRecord::Base
belongs_to :store
validates_presence_of :name, :url, :price
end
Model for Store:
class Store < ActiveRecord::Base
has_many :products
has_many :pins, through: :products
accepts_nested_attributes_for :products
end
Controller for Product:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
#stores = Store.all
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render action: 'show', status: :created, location: #product }
else
format.html { render action: 'new' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:name, :description, :imageurl, :url, :price, :Store_id)
end
end
Form for products
<%= form_for(#product) do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= f.label :name, "Name" %>
<%= f.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :description, "Description" %>
<%= f.text_area :description, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :imageurl, "Image" %>
<%= f.text_field :imageurl, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :url, "Web Address" %>
<%= f.text_field :url, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :price, "Price" %>
$<%= f.text_field :price, class: "form-control" %>
</div>
<div class="form-group">
<%= collection_select(:product, :Store_id, Store.all, :id, :name, {:prompt=> "Select A Store"}, {:class => "form-control"} ) %>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary" %>
</div>
<% end %>
<%= params.inspect %>
View for products (show.html.erb):
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #product.name %>
</p>
<p>
<strong>Description:</strong>
<%= #product.description %>
</p>
<p>
<strong>Store Id:</strong>
<%= #product.Store_id %>
</p>
<p>
<strong>Store Name:</strong>
<%= #product.store.try(:name) %>
</p>
<p>
<strong>Image:</strong>
<%= #product.imageurl %>
</p>
<p>
<strong>Url:</strong>
<%= #product.url %>
</p>
<p>
<strong>Price:</strong>
$<%= #product.price %>
</p>
<%= params.inspect %>
<%= link_to 'Edit', edit_product_path(#product) %> |
<%= link_to 'Back', products_path %>
You'll notice that I have .try in place where I'm referencing product.store.name from product.store so that I stop getting the error listed in the subject of this post.
When I look up the product I'm viewing using the console I see that Store_id: 2
When I look up the store with id of 2 I see that Store_id: 1 - so there is a value present there.
I printed params on the show view and only get this: {"action"=>"show", "controller"=>"products", "id"=>"2"}.
Can anyone find what I'm missing in this whole set up to get product.store.name to display in my product views? Let me know if I can provide more info!
First, you should be using snake_case for attributes. UpperCamelCase is reserved for constant names in Ruby, that includes things like classes and modules. Update your code to not use UpperCamelCase style naming for attributes (eg. ProdDesc, ProdImageUrl). Also it is unnecessary to use prefixes like Prod* for attributes.
So instead of
class Product < ActiveRecord::Base
belongs_to :store
validates_presence_of :ProdTitle, :ProdUrl, :ProdPrice
end
Your class will look like this:
class Product < ActiveRecord::Base
belongs_to :store
validates_presence_of :title, :url, :price
end
You can read more about Ruby and Rails naming conventions here:
http://rubylearning.com/satishtalim/ruby_names.html
https://github.com/bbatsov/rails-style-guide
I suspect that this causes the problem. You will also need to rename columns in database and change your controller and view code to reflect the change. If you can, please start again with your application.
You have taken name for field In database which are non conventional. By default The active record looks for store_id field in product table . Field name in a table should be snake case .so now you have to explicitly tell in product model the foreign key for store model