Why aren't my Active Record validations validating? - ruby-on-rails

So I have a Review model in my app for users leaving reviews for movies. I'm rendering a form for a new Review in the Show view for each individual movie. However, when I try to submit a blank review to check that the errors display on the page, they don't. What could I be doing wrong?
My review model:
class Review < ActiveRecord::Base
attr_accessible :content
belongs_to :user
belongs_to :movie
validates :user_id, presence: true
validates :movie_id, presence: true
validates :content, presence: true, length: { maximum: 1000 }
end
My reviews controller:
class ReviewsController < ApplicationController
before_filter :signed_in_user, only: [:new, :create, :destroy]
def new
end
def create
#I'm doing this to get around the mass assignment error.
movie_id = params[:review].delete(:movie_id)
#review = current_user.reviews.build(params[:review])
#review.movie_id = movie_id
if #review.save
flash[:success] = "Review created!"
redirect_to movie_path(#review.movie)
else
redirect_to movie_path(#review.movie)
end
end
def destroy
end
end
My form:
<%= form_for(:review, url: reviews_path) do |f| %>
<%= f.hidden_field :movie_id %>
<div class="field">
<%= f.text_area :content, placeholder: "Write a new review..." %>
</div>
<%= f.submit "Submit", class: "btn btn-large btn-primary" %>
<% end %>

When you redirect away from the #create method, you're losing the errors... Change to
if #review.save
flash[:success] = "Review created!"
redirect_to movie_path(#review.movie)
else
render :new
end

Related

How do I make rails form responsive to a hyperlink?

I am following Michael Hartl's Rails Tutorial and have completed the part about creating microposts. I was wondering if anyone have an idea about how to make the micropost form responsive to a hyperlink. For example, when a user types in "Visit our HTML tutorial" in the micropost, I want the link to active. Any help would be appreciated.
micropost_controller.rb
class MicropostsController < ApplicationController
before_action :signed_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#micropost.destroy
redirect_to root_url
end
private
def micropost_params
params.require(:micropost).permit(:html)
end
def correct_user
#micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if #micropost.nil?
end
end
micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') } validates :content,
presence: true, length: { maximum: 140 } validates :user_id,
presence: true end
...
end
micropost_form.html.erb
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
You can use the sanitize helper method and pass in the anchor (a) tag as the only allowable tag. You don't use it when they create the post, you use it when you are showing the micropost in the view
app/views/microposts/show.html.erb
<%= sanitize micropost.content, tags: ['a'] %>
(I don't know exactly how you are showing the content of a micropost, but this should give you an idea)
This is safer than other options like html_safe because you can actually control which html tags you will allow the user to be able to input.

saving two Rails models with accepts_nested_attributes_for

I have two models: author and profile. Author accepts_nested_attributes_for profile. I can't figure out how to save the profile model (namely the :avatar attribute) through author.
author.rb
class Author < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
end
profile.rb
class Profile < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
belongs_to :author
end
authors_controller.rb
class AuthorsController < ApplicationController
before_action :set_author, only: [:show, :edit, :update, :destroy]
def index
#authors = Author.all.order("last_name ASC, first_name ASC")
end
def new
#author = Author.new
end
def create
#author = Author.new(author_params)
if #author.save
AuthorMailer.activate(#author).deliver
flash[:notice] = "Please check your email to activate your account."
redirect_to root_path
else
render :new
end
end
def edit
#profile = #author.build_profile
end
def update
if #author.update(author_params)
flash[:notice] = "Profile has been updated."
redirect_to #author
else
flash[:alert] = "Profile has not been updated."
render :edit
end
end
private
def author_params
params.require(:author).permit(:first_name, :last_name, :email, :password, :password_confirmation, :avatar)
end
def set_author
#author = Author.find(params[:id])
end
end
app/views/authors/edit.html.erb
<div class="center">
<h1>Edit Author</h1>
</div>
<div class="row">
<div class="col-xs-6 col-lg-4 center-block">
<%= simple_form_for(#author, :defaults => { :wrapper_html => {:class => 'form-group'}, :input_html => { :class => 'form-control' } }) do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.fields_for [#author, #profile] do |p| %>
<%= p.file_field :avatar %>
<% end %>
<%= f.submit "Submit", class: "btn small btn-default" %>
<% end %>
</div>
</div>
This form doesn't save anything to my profile table.
EDIT1
updating the permit parameters didn't save anything to the profile table. But I did notice that adding the following to my authors_controller in the update action saves an incomplete record to the profile table (the avatar field is blank):
author_controller#update
if #author.update(author_params)
#profile = #author.build_profile()
#author.profile = #profile
flash[:notice] = "Profile has been updated."
redirect_to #author
else
flash[:alert] = "Profile has not been updated."
render :edit
end
I tried placing pry inside the update action and my params look like this:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"EROrMzOejPmMU/wzlnC5iaoTPu4pyBXelHAs3uiPA2U=",
"author"=>
{"first_name"=>"Mike",
"last_name"=>"Glaz",
"profile"=>
{"avatar"=>
#<ActionDispatch::Http::UploadedFile:0x007feb48127ab0
#content_type="image/jpeg",
#headers=
"Content-Disposition: form-data; name=\"author[profile][avatar]\"; filename=\"me_and_lekeziah.jpg\"\r\nContent-Type: image/jpeg\r\n",
#original_filename="me_and_lekeziah.jpg",
#tempfile=#<File:/tmp/RackMultipart20140504-15793-l4uu6l>>}},
"commit"=>"Submit",
"action"=>"update",
"controller"=>"authors",
"id"=>"3"}
so then I tried the following in my update action:
#profile = #author.build_profile(params[:author][:profile])
#author.profile = #profile
but then I get the following error:
ActiveModel::ForbiddenAttributesError in AuthorsController#update
when you use "fields_for" for a association model xxx, your params need to permit :xxx_attributes fields, which's value should be a array include it's attributes, so change your author_params method to something like this:
def author_params
params.require(:author).permit(:first_name, :last_name, :email, :password, :password_confirmation, profile_attributes: [:avatar])
end
First, you should update your params permitted fields as:
def author_params
params.require(:author).permit(:first_name, :last_name, :email, :password, :password_confirmation, :profile_attributes[:id, :avatar])
end
Second, you are mixing Rails form_for helper and Simple_form in your view.
change
<%= f.fields_for [#author, #profile] do |p| %>
to:
<%= f.simple_fields_for [#author, #profile] do |p| %>

repeated issue, could be validations in my models? [Rails 4]

it seems like a lot of people posted this problem as here but the answer there, is not my case I guess.
in my models I have
class Project < ActiveRecord::Base
has_many :questions, dependent: :destroy
validates :name, presence: true, uniqueness: true
validates :category_id, presence: true
end
and
class Question < ActiveRecord::Base
belongs_to :project
validates :question, presence: true
validates :question_type, presence: true
validates :project_id, presence: true
QUESTIONS_TYPES = ['Single', 'Multiple', 'A text']
end
<%= form_for(#question) do |f| %>
tha form:
<div class="field">
<%= f.label :project_id %><br>
<%= f.select :project_id, #project_options %>
</div>
<div class="field">
<%= f.label :question %><br>
<%= f.text_field :question %>
</div>
<div class="field">
<%= f.label :question_type %><br>
<%= f.select :question_type, Question::QUESTIONS_TYPES %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The controller
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
before_action :set_projects, only: [:new, :edit]
.
.
def create
#question = Question.new(question_params)
.
.
end
.
.
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:question, :question_type, :project_id)
end
def set_projects
#project_options = Project.all.map{|p| [p.name, p.id]}
end
end
The error
undefined method `empty?' for nil:NilClass
Extracted source (around line #16):
<div class="field">
<%= f.label :project_id %><br>
<%= f.select :project_id, #project_options %>
</div>
the post answer I mentioned before says it should be project_id in the form, but it is correct in my code. In my case the error raises only if I have some empty fields otherwise it goes well. could be something wrong with validations?
Thanks!!
Yes it seems it has validation issue and could cot create/update the record.
But the error is: your create and update action probably have a else block in the controller to handle the failed cases. make sure you initiate #project_options there in the else block. What happens is it cant create the record hence tries to render the new/edit form directly(not executing new/edit methods so #project_options is not initialized). put something in your controller in create action like:
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully updated.' }
format.json { head :no_content }
else
format.html do
set_projects #need to call set_projects method here.
render action: "new"
end
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
You should have same thing in your update action as well
The error you have is that empty? method is called on nil. The two objects here are #question and #project_options. Check if you have assigned the proper values to these instance variables in your controller.

Absolutely stuck trying to create nested association in rails form with has_many through

I posted an earlier question about this and was advised to read lots of relevant info. I have read it and tried implementing about 30 different solutions. None of which have worked for me.
Here's what I've got.
I have a Miniatures model.
I have a Manufacturers model.
Miniatures have many manufacturers THROUGH a Productions model.
The associations seem to be set up correctly as I can show them in my views and create them via the console. Where I have a problem is in letting the Miniatures NEW and EDIT views create and update to the Productions table.
In the console the command #miniature.productions.create(manufacturer_id: 1) works, which leads me to believe I should be able to do the same in a form.
I THINK my problem is always in the Miniatures Controller and specifically the CREATE function. I have tried out a ton of other peoples solutions there and none have done the trick. It is also possible that my field_for stuff in my form is wrong but that seems less fiddly.
I've been stuck on this for days and while there are other things I could work on, if this association isn't possible then I'd need to rethink my entire application.
The form now creates a line in the Productions table but doesn't include the all important manufacturer_id.
Any help VERY much appreciated.
My New Miniature form
<% provide(:title, 'Add miniature') %>
<h1>Add a miniature</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#miniature) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :production do |production_fields| %>
<%= production_fields.label :manufacturer_id, "Manufacturer" %>
<%= production_fields.select :manufacturer_id, options_from_collection_for_select(Manufacturer.all, :id, :name) %>
<% end %>
<%= f.label :release_date %>
<%= f.date_select :release_date, :start_year => Date.current.year, :end_year => 1970, :include_blank => true %>
<%= f.submit "Add miniature", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
Miniatures controller
class MiniaturesController < ApplicationController
before_action :signed_in_user, only: [:new, :create, :edit, :update]
before_action :admin_user, only: :destroy
def productions
#production = #miniature.productions
end
def show
#miniature = Miniature.find(params[:id])
end
def new
#miniature = Miniature.new
end
def edit
#miniature = Miniature.find(params[:id])
end
def update
#miniature = Miniature.find(params[:id])
if #miniature.update_attributes(miniature_params)
flash[:success] = "Miniature updated"
redirect_to #miniature
else
render 'edit'
end
end
def index
#miniatures = Miniature.paginate(page: params[:page])
end
def create
#miniature = Miniature.new(miniature_params)
if #miniature.save
#production = #miniature.productions.create
redirect_to #miniature
else
render 'new'
end
end
def destroy
Miniature.find(params[:id]).destroy
flash[:success] = "Miniature destroyed."
redirect_to miniatures_url
end
private
def miniature_params
params.require(:miniature).permit(:name, :release_date, :material, :scale, :production, :production_attributes)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
end
Miniature model
class Miniature < ActiveRecord::Base
has_many :productions, dependent: :destroy
has_many :manufacturers, :through => :productions
accepts_nested_attributes_for :productions
validates :name, presence: true, length: { maximum: 50 }
validates :material, presence: true
validates :scale, presence: true
validates_date :release_date, :allow_blank => true
def name=(s)
super s.titleize
end
end
Production model
class Production < ActiveRecord::Base
belongs_to :miniature
belongs_to :manufacturer
end
Manufacturer model
class Manufacturer < ActiveRecord::Base
has_many :productions
has_many :miniatures, :through => :productions
validates :name, presence: true, length: { maximum: 50 }
accepts_nested_attributes_for :productions
end
Instead of calling:
#production = #miniature.productions.create
Try Rails' "build" method:
def new
#miniature = Miniature.new(miniature_params)
#miniature.productions.build
end
def create
#miniature = Miniature.new(miniature_params)
if #miniature.save
redirect_to #miniature
else
render 'new'
end
end
Using the build method uses ActiveRecord's Autosave Association functionality.
See http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html
You also need to update your params method, e.g.
def miniature_params
params.require(:miniature).permit(:name, :release_date, :material, :scale, productions_attributes: [:manufacturer_id])
end
Also your fields_for should be plural (I think)...
<%= f.fields_for :productions do |production_fields| %>

Rails - Nested Form

I have Users who bet on matches. A single bet is called "Tipp" and the users predict the match score in "tipp.tipp1" and "tipp.tipp2"
I have problems with my form which is supposed to save "tipps" of users.
With the code below I get "Can't mass-assign protected attributes: tipp" although i have set "accepts_nested_attributes_for :tipps" and "attr_accessible :tipps_attributes".
I hope I have provided all the necessary code. Thanks in advance for your help!
Here is the parameters output:
Parameters:
{
"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"mPPpCHjA3f/M2l1Bd3ffO1QUr+kdETGkNE/0CNhbJXE=",
"user" =>{
"tipp"=>{
"6"=>{"tipp1"=>"4","tipp2"=>"6"},
"7"=>{"tipp1"=>"-1","tipp2"=>"-1"},
"8"=>{"tipp1"=>"-1","tipp2"=>"-1"}
}
},
"commit"=>"Update User",
"user_id"=>"1"
}
Shortened Code:
Controllers:
1) Users
class UsersController < ApplicationController
def edit_tipps
#user = current_user
end
def update_tipps
#user = current_user
if #user.update_attributes(params[:user])
flash[:notice] = "success (maybe)"
redirect_to user_edit_tipps_path(#user)
else
flash[:error] = "errors"
redirect_to user_edit_tipps_path(#user)
end
end
Models:
1) Users
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :tipps_attributes
has_many :tipps
accepts_nested_attributes_for :tipps
end
2) Tipps
class Tipp < ActiveRecord::Base
attr_accessible :match_id, :points, :round_id, :tipp1, :tipp2, :user_id
belongs_to :user
end
My Form:
<%= form_for #user, :url => { :action => "update_tipps" } do |user_form| %>
<% #user.tipps.each do |tipp| %>
<%= user_form.fields_for tipp, :index => tipp.id do |tipp_form|%>
<%= tipp_form.text_field :tipp1 %><br/>
<%= tipp_form.text_field :tipp2 %><br/>
<% end %>
<% end %>
<%= submit_or_cancel(user_form) %>
<% end %>
Instead of doing what you did,
you could try either:
1.
Instead of:
<% #user.tipps.each do |tipp| %>
<%= user_form.fields_for tipp, :index => tipp.id do |tipp_form|%>
I would do this:
<%= user_form.fields_for :tipps do |tipp_form| %>
Or:
2.
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :tipps_attributes, :tipps
Goodluck

Resources