Simpleform with two models not nested but linked, failing to submit - ruby-on-rails

For the moment I've got two models in my app.
class Budget < ApplicationRecord
belongs_to :project
end
class Project < ApplicationRecord
has_one :budget
end
So each project has a budget, but a budget could be used later for different class, so it's not especially a "child" of a project.
I tried differents things for the form, here is the one that works the most for the moment :
<%= simple_form_for #project do |f| %>
<%= f.error_notification %>
<%= f.input :name %>
<%= simple_fields_for #budget do |ff| %>
<%= ff.input :amount %>
<% end %>
<%= f.submit "Create", class: "btn" %>
<% end %>
I tried to replace simple_fields_for by simpleform_for but when I'm doing so when I click "Submit" button, nothing happens.
With that version when I click Submit there is this error :
"undefined method `model_name' for nil:NilClass"
I don't really understand how I'm not working on an existing class here.
Here's my conroller
def create
respond_to do |format|
#budget = Budget.new(params.require("budget").permit(:amount))
if #budget.save # If budget is saved
puts "Budget successfuly created"
#project.budget = #budget
if #project.save
puts "project successfuly created"
else
puts "Unable to create project"
format.html { render :new, notice: 'Project went wrong' }
end
format.html { redirect_to projects_path, notice: 'Success' }
else # If budget isn't saved
puts "Unable to create budget"
format.json { render json: #budget.errors, status: :unprocessable_entity }
format.html { render :new, notice: 'Budget went wrong' }
end
end
I'm searching for good practice concerning simpleform and rails coding, but I can't really find something that is related to my case, or at least I can't recognize what documentation relates to my case.
PS : In my rails console, I've got that output :
Unable to create budget

Your controller is a bit hacky. I would look into the default generated create method by the scaffold generater
To get a good idea of how you could clean it up. That would mean a better chance for debugging.
Nested params
One thing i do see is, that something is wrong in your param call.
#budget = Budget.new(params.require("budget").permit(:amount))
This is like saying: Find the param 'amount' for budget.
But your params are generated differently by your form:
You have a simple form for #projects with a simple field for #budget.
Therefore your params will be nested like this:
params[:project][:budget]
Hope it helps.
By the way: Do you have a before_action to set #project?
Or else that would return nil on line 6 in your controller.

Related

Simple_form does not accept the instance variable from action create

I am creating a very simple StackOverflow type of website
written in Ruby on Rails. I created four actions in my questions controller and one of them is 'Create'.
I proceeded in the view page index.html.erb and I create a simple_form where I get input(question) from a user.
I get an error (NoMethodError in Questions#index..undefined method `[]' for nil:NilClass)
The question belongs to the current_user and I think that might be the problem. I thought maybe I need to initialize one more variable in order to get my form to work.
Can please someone tell me what am I missing here?
Thank you in advance!
class QuestionsController < ApplicationController
def index
#questions = Question.all
end
def show
#question = Question.find(params[:id])
end
def new
#question = Question.find(params[:user_id])
#question = Question.new
end
def create
#question = Question.new(accepted_params)
if #question.save
redirect_to questions_show_path, notice: 'Question submitted'
else
#question = Question.find(params[:id])
render :new
end
end
<%= simple_form_for #questions, #user do |f| %>
<%= f.input :title %>
<% f.input :content %>
<%= f.submit :submit, class: 'btn btn-secondary'%>
<% end %>
You should use the following code:
def create
#question = Question.new(accepted_params)
if #question.save
redirect_to question_path(#question), notice: 'Question submitted'
else
render :new
end
end
Let me explain:
on a create action there is no params[:id] (because we are creating a new item)
also: we established that saving failed so trying to retrieve it from the database would only make sense on an edit action
third: simple-form will look at the error-messages and incorporate them into the form, so the user can then fix the errors.
and lastly: I fixed your redirect_to to be more "rails"-like, but this depends on your route-definition. I am assuming you have something like resources :questions in your routes (but if you do not give a parameter that could also never work imho)
E.g. if you have a validate_presence_of :name in your model, this could cause a validation-error upon save, and then we could present the field in red in the form when rerendering.
[TYPO in form?]
Lastly, after your comment I noticed it said simple_form_for #questions, #user and that should be either be the singular simple_form_for #question. If you want to edit the question as a nested path for the user, I think the correct form is simple_form_for [#user, #question].

Rails - undefined method `project_path' in first line of form?

I think I am stumbling upon a rendering/redirection issue. In my rails application I have clients who can have many projects. When I update a project I use a nested form with the form_for method, passing it a client and project instance variable, respectively.
Here is my code:
routes.rb:
resources :clients do
resources :projects
end
projects_controller.rb
def edit
#client = Client.find(params[:client_id])
#project = Project.find(params[:id])
end
def update
#project = Project.find(params[:id])
respond_to do |format|
if #project.update_attributes(params[:project])
format.html { redirect_to client_project_path(#project.client, #project), notice: 'Project was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" } # !!this seems to be the problem
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
views/projects/_form.html.erb:
<%= simple_form_for([#client, #project]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :start_on %>
<%= f.input :end_on %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
When I simply display the edit action, everything works fine. If I update the project with valid data, everything is fine, too. The problem occurs when I type in invalid or missing data into the form - then the "else part" of the update method is called, and the action "edit" should be rendered.
This indeed happens, but it seems to me that something is wrong either with the "edit" method or my form, because when I change render action: "edit" to redirect_to action: "edit", it does redirect me (of course without error messages so it is clearly not the desired solution, but I just wanted to test).
When "edit" is rendered, this is the error message I am getting:
undefined method `project_path'
which is only referring to the first line in my form.
What am I doing wrong here? Sorry for the long question and thanks for any help!
You need to load your #client variable in the update action. Think of it this way, if you hit the else, what did the edit provide to the view, because you need just that when you render instead of redirect. Being that your resources are nested, the form_for helper needs both the client and the project.

Ruby on Rails - HABTM - Inserting data into 2 models in one controller

I'm a beginner in RoR and am having issues on working with some of my models.
Basically I have a habtm relation between a product-ticket-reservation.
A product habtm reservations through tickets and vice-versa.
I also have a Supplier, which has_many :products and has_many :reservations.
What I want to do is after the user selects a supplier and sees it's products, he may then select the products he wants from that supplier.
In that reservations.new I got a form but since after the "submit" action I have to insert data in 2 models, I'm having issues with it.
When I create a reservation, it is supposed to create a reservation entry and a ticket entry at the same time, the ticket entry will have the reservation_id and the product_id as foreign keys.
My Reservations' view:
<%= form_for(#reservation) do |f| %>
Reservation Info
<div id="reservation_top"></div>
<div id="reservation">
<%= f.label :name %><br />
<%= f.text_field :name %>
<%= f.label :surname %><br />
<%= f.text_field :surname %>
(...)
<%= f.hidden_field :supplier_id, :value => #reservation.supplier_id %> #to get the supplier ID
Products:
<%= f.fields_for :tickets do |t| %>
<%= t.select("product_id",options_from_collection_for_select(#products, :id, :name))%>
#I also have another t.select and although this isn't my primary concern, I wanted this t.select option's to change according to what is selected on the previous t.select("product_id"). Something like a postback. How is it done in RoR? I've searched and only found observe_field, but I didn't understand it very much, can you point me in the right direction? thanks
<%end%>
<%= f.label :comments %>
<%= f.text_area :comments %>
<%= f.submit%>
<%end%>
Now i think the problem is in my controller, but I can't understand what to put there, I currently have:
def new
#supplier=Supplier.find(params[:supplier_id])
#reservation = Reservation.new(:supplier_id => params[:supplier_id])
#ticket = Ticket.new(:reservation_id => params[#reservation.id])
#products = Supplier.find(params[:supplier_id]).products
#ticket = #reservation.tickets.build
respond_to do |format|
format.html
format.json { render :json => #reservation }
end
end
def create
#reservation = Reservation.new(params[:reservation])
respond_to do |format|
if #reservation.save
#reservation.tickets << #ticket
format.html { redirect_to #reservation, :notice => 'Reservation Successful' }
else
format.html { render :action => "new" }
format.json { render :json => #reservation.errors, :status => :unprocessable_entity }
end
end
I'm now getting a
Called id for nil, which would mistakenly be 4
Is it because it is trying to create a ticket and it doesn't have the reservation_id?
I've never handled habtm associations before. Any tips?
Thanks in advance,
Regards
Take a look at the POST params for your create action in your log. That will show you exactly what data you have to work with from params when it comes time to save your data.
In
def create
#reservation = Reservation.new(params[:reservation])
respond_to do |format|
if #reservation.save
#reservation.tickets << #ticket
what is #ticket at that point? (There's your nil I believe)
I think it might also be interesting to see what your #reservation and #ticket look like in your new method right before generating the response... log a .inspect of each of those objects to make sure you have what you think you have.
And in a more complicated save like you have, I'd wrap it all in a transaction.

What's the solution for nil.update_attributes in ruby?

I'm unable to crack this error. Can't figure out why #customer is being assigned to value nil.
"You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.update_attributes"
Here is a snippet of the code :
def cedit
#title = "Edit Customer Information"
#customer = Customer.find(params[:id])
if request.post? and params[:customer]
attribute = params[:attribute]
case attribute
when "fname"
try_to_update #customer, attribute
when "email"
try_to_update #customer, attribute
when "add"
try_to_update #customer, attribute
end
end
end
private
def try_to_update(customer, attribute)
if customer.update_attributes(params[:customer])
flash[:notice] = "Customer's details updated."
redirect_to :action => "record", :controller => "c2"
end
end
First of all your code looks very none-rails like and breaks a couple of rails best practices. I would strongly recommend you read the official Rails guide and try to see if you can refactor some of your code.
I have too little information on what you are trying to do in the grand scale of things so I can't give you a full fledged answer. But you probably want to do something along these lines.
class CustomersController < ApplicationController
def update
#customer = Customer.find(params[:id])
if #customer.update_attributes(params[:customer])
flash[:notice] = "Customer updated"
end
redirect_to customer_path(#customer)
end
end
The view could look something like this:
<%= form_for(:customer) do |f| %>
<%= f.text_field :fname %>
<%= f.text_field :email %>
<%= f.text_field :add %>
<%= f.submit_tag "Update" %>
<% end %>
Good luck!

validates_presence_of not working properly...how to debug?

In my Review model, I have the following:
class Review < ActiveRecord::Base
belongs_to :vendor
belongs_to :user
has_many :votes
validates_presence_of :summary
end
I submit a new entry as follows in the URL:
vendors/9/reviews/new
The new.html.erb contains a form as follows:
<%= error_messages_for 'review' %>
<h1>New review for <%= link_to #vendor.name, #vendor%></h1>
<% form_for(#review, :url =>vendor_reviews_path(#vendor.id)) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :summary %><br />
<%= f.text_area :summary, :rows=>'3', :class=>'input_summary' %>
<%= f.hidden_field :vendor_id, :value => #vendor.id %>
</p>
<p>
<%= f.submit 'Submit Review' %>
</p>
<% end %>
When I leave the field for :summary blank, I get an error, not a validation message:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.name
Extracted source (around line #3):
1: <%= error_messages_for 'review' %>
2:
3: <h1>New review for <%= link_to #vendor.name, #vendor%></h1>
I don't understand what is happening, it works if :summary is populated
def new
#review = Review.new
#vendor = Vendor.find(params[:vendor_id])
#review = #vendor.reviews.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #review }
end
end
def create
#review = Review.new(params[:review])
##vendor = Vendor.find(params[:vendor_id]) #instantiate the vendor from the URL id -- NOT WOKRING
##review = #vendor.reviews.build #build a review with vendor_id -- NOT working
#review = #current_user.reviews.build params[:review]#build a review with the current_user id
respond_to do |format|
if #review.save
flash[:notice] = 'Review was successfully created.'
format.html { redirect_to review_path(#review) }
format.xml { render :xml => #review, :status => :created, :location => #review }
else
format.html { redirect_to new_review_path(#review) }
format.xml { render :xml => #review.errors, :status => :unprocessable_entity }
end
end
end
My guess is that when it fails it is going to redirect_to new_review_path(#review) and so doesn't know the vendor it. How can I redirect to vendor/:vendor_id/reviews/new instead?
You probably don't have #vendor member variable set - but to fix this, it would be more correct to use not #vendor directly, but through your #review variable instance.
If you are creating new review, you already have #review member variable created, and you simply are populating fields in it - so, you need to set the vendor for #review (unless it's optional)... it would be more correct to use #review.vendor.name instead.
(If vendor is optional, then you obviously must catch all vendor.nil? cases.)
What code do you have in the new and create actions in your ReviewsController?
I suspect that your new Review is failing validation because the summary field is blank and then when the form is redisplayed on validation failure, the #vendor instance variable is nil.
You need to make sure that #vendor is assigned a value for both code paths.
I think you need to render :action => 'new' instead of your redirect_to new_review_path(#review). This will keep your error_messages on the #review object. By redirecting you are losing the old object and creating a new one.
As others has said, you also need to make sure you re-populate the #vender variable in your create method before rendering the view.
PS. I like to use the ardes resources_controller plugin for bog standard controller actions like these, makes life a lot easier for me and it handles nested resources really well.

Resources