I have a page (mytestpage.html.erb) located at app/views/users/mytestpage.html.erb.
I've created a new table in my database called "Categories".
I'm trying to create a dropdown menu that lists all of the categories (:title string) on my mytestpage.html.erb page and I'm getting this error:
uninitialized constant UsersController::Categories
Here is my users_controller.rb
def mytestpage
#user = User.new
#categories = Categories.new
end
And here is my users/mytestpage.html.erb file:
<%= form_for categories do |f| %>
<%= f.collection_select(:title, Categories.all, :title, :description) %>
<% end %>
I assume that I wouldn't define #categories in my user_controller.rb (even though the file is located in my users folder - while I tried it, doing so throws me the same error).
It looks like you haven't created a model file for your Categories table. Something as simple as this:
class Category < ActiveRecord::Base
end
Should do the trick.
You will notice that I have named the class Category instead of Categories and this is due to the fact that Rails expects class names in singular form (eg User instead of Users) as it can infer the pluralised table name from the singular class name.
1. Use the instance variable instead of a constant in your view at app/views/users/mytestpage.html.erb:
<%= form_for categories do |f| %>
<%= f.collection_select(:title, #categories, :title, :description) %>
<% end %>
You're currently referencing a constant in the view. What you should be doing is referencing the instance variable that you've created in users_controller.rb
Instance variables are prefixed with # and are made available in the view that corresponds with the controller action that they are defined in.
2. Change your instance variable definition to this:
#categories = Category.new
or
#categories = Category.all
Right now you've got it defined as Categories.new and I don't think that's what you want to do - that is likely also why you're getting the error.
(depending on what you want to do - I feel like you might want to show all of the categories in you select tag? correct me if I'm wrong.)
Related
I am trying to create associated models with Rails. My models are Financial which have many documents and Document which belongs to Financial. A working code when creating the associated model could be
def create
#financial = Financial.find(2)
#document = #financial.documents.create(document_params)
...
end
In my view I have a form which looks like this to select the right Financial
<%= form_for Document.new do |f| %>
<%= f.collection_select :financial_id, Financial.all, :id, :description %>
<%= f.submit %>
I do see the right parameters being transferred in the log when I submit the form
"financial_id"=>"3"
So I figured I would just need to change the initial code to:
def create
#financial = Financial.find(params[:financial_id])
#document = #financial.documents.create(document_params)
...
end
but I get a "Couldn't find Financial with 'id'=". I have tried other things including:
#financial = Financial.find_by(id: params[:financial_id])
Without much success. Could anyone give me the appropriate syntax please? Thanks.
Couldn't find Financial with 'id'=
Because, the params that are submitted are actually inside document hash. So params[:financial_id] won't work. Instead you need to use params[:document][:financial_id]
def create
#financial = Financial.find(params[:dcument][:financial_id])
#document = #financial.documents.create(document_params)
...
end
I stored all the tablename I've created to Menu table. And every time I add the table in Menu, it will automatically create a link under Menu list
see below.
I want each table in Menu to have a Listing, New, Edit, and Delete.
see below.
I have a controller prj_menus_controller, I will just pass the id of the table from Menu table.
here is the code for index and new in my controller.
Class PrjMenusController < ApplicationController
def index
#prj_menus = Menu.find(params[:id]).tablename.singularize.classify.constantize.all
end
def new
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new
end
def create
#prj_menu = Menu.find(params[:id]).tablename.singularize.classify.constantize.new(prj_menu_params)
if #prj_menu.save
redirect_to :action => 'index'
else
render :new
end
end
private
def prj_menu_params
params.require("HERE IS MY PROBLEM").permit(:name)
end
end
and in my
new.html.erb
<%= simple_form_for (#prj_menu),:url => prj_menus_path, :method => :post do |f| %>
<%= f.input :name %>
<%= f.submit 'Save', class: 'btn btn-primary' %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
I can get the list in my index.html.erb, it is working. My problem is that I don't know how to get all params when I click the submit in new.html.erb. I got this hash
{"sample1_table"=>{"name"=>"test 6"}, "commit"=>"Save","controller"=>"prj_menus", "action"=>"create"}
It is correct but I don't know what to put in my controller. I tried this params.require(["#{#prj_menu}"]).permit(:name), it creates new record but params[:name] does not save.
I am still a noob to Ruby On Rails and I don't know what to search for this.
I think you are mostly confused on what parameter whitelisting does and how parameters are passed from the form to the controller.
I does not really matter if the name of the form hash matches the name of the database table. It just does in most cases since that makes the most sense. It's simply representative of the REST interface of your app.
Let's say you have a action which creates Pets:
POST /pets
And in our form we have a bunch of inputs like so:
<input name="pet[name]">
Rails will map create a params[:pet] hash { name: 'Spot' }. But we want to save the pets as an Animal.
class PetsController < ApplicationController
def new
#pet = Animal.new()
end
def create
#pet = Animal.new(pet_params)
if #pet.save
# ...
end
def pet_params
params.require(:pet).permit(:name)
end
end
Animal does not care what the params key is, it just gets a hash. But we also need to tell simple_form what parameter key we want to use since it looks at the model_name attribute.
simple_form_for(#pet, as: :pet)
Gives us pet[name] instead of animal[name].
I don't get why you are so adamant about making things so difficult for yourself though unless you are creating a database administration tool in the vein of PHP_MyAdmin. And even that case you don't even want to be altering the schema of the app database at runtime.
You are going to run into huge problems when it comes to creating effective queries for getting all the menus.
I have difficulties with ruby on rails syntax.
I got this error
First argument in form cannot contain nil or be empty
class PersonalsController
def index
end
def create
#personal = Personal.new
end
def new
#personal = Personal.new
end
def show
#personal = Personal.find([:id])
end
end
index.html.erb
<%= form_for #personal do |f| %>
<%= f.label :title %><br>
<%= f.text_field :title %>
<%= f.submit %>
<% end %>
the value of #personal is nil that's why you are getting error.
Change you code like this
def index
#personal= Personal.all
end
form_for is helper method
check with this link form_helper
The error is generated since #personal was not set in the controller. So either you add a #personal = Personal.new to the index method, or set it to a specific database entry, e.g., #personal = Personal.find(1)
However, it seems strange that you have a form displaying a single record in the index view.
More likely, you want to have the form in your new or edit views (in the former case you typically use new, while in the latter case you would use the find method to find a specific record and let the user edit it).
In the index method, you usually use the controller to select a group of records (e.g., #ps = Personal.all to get all the records) and iterate over them in the view (#ps.each do |person| .... end)
P.S. The show method should probably use Personal.find(params[:id]) instead of Personal.find([:id])
I am trying to upload a photo but after I press the upload button, I get this error. I am new to rails 4 so I'm not sure what I am missing.
My logic is when I click the submit button. This will cause the create action to fire and create a IncomePicture object and store it in my database.
No route matches [POST] "/income_pictures/new"
Routes:
root_path GET / static_pages#home
income_pictures_path GET /income_pictures(.:format) income_pictures#index
POST /income_pictures(.:format) income_pictures#create
new_income_picture_path GET /income_pictures/new(.:format) income_pictures#new
edit_income_picture_path GET /income_pictures/:id/edit(.:format) income_pictures#edit
income_picture_path GET /income_pictures/:id(.:format) income_pictures#show
PATCH /income_pictures/:id(.:format) income_pictures#update
PUT /income_pictures/:id(.:format) income_pictures#update
DELETE /income_pictures/:id(.:format) income_pictures#destroy
Controller:
class IncomePicturesController < ApplicationController
def new
#income_picture = IncomePicture.new
end
def create
#income_picture = IncomePicture.new(IncomePicture_params)
if #income_picture.save
flash[:notice] = "Income picture successfully uploaded"
redirect_to #income_picture
end
end
def show
#income_picture = IncomePicture.find(params[:id])
end
def index
#income_picture = IncomePicture.all
end
private
def IncomePicture_params
params.require(:income_picture).permit(:image, :name)
end
end
View:
<%= form_for :income_picture, :html => { :multipart => true } do |f| %>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :image %>
<%= f.file_field :image %>
</p>
<p><%= f.submit %></p>
<% end %>
I think you want form_for #income_picture rather than form_for :income_picture.
From the form guide: Using a symbol creates a form to new_income_picture_path, i.e. /income_picture/new whereas using a populated instance variable creates a form to income_pictures_path, i.e. income/pictures. Both set the form's method to POST. However, there's no such route as POSTing to /income_picture/new/, which is what caused the error.
form_for
To elaborate on the accepted answer, you have to remember that when calling form_for, Rails does some pretty amazing things:
It takes an ActiveRecord object and builds a "route" out of it (from the model)
It populates the form with the ActiveRecord object's data
It allows you to retain a perceived persistent state on the form (by perpetuating the data)
The problem you have is you're passing a simple symbol to the form - which prevents Rails from being able to accurately access the data required to make the 3 "magic" steps above possible.
This means you'll get random errors like the one you're seeing (IE in the absence of an ActiveRecord object, Rails will just use the same URL that you have on your page - /new)
--
ActiveRecord
The way to fix the issue you have is to replace the symbol with an ActiveRecord object, which was suggested in the accepted answer.
The reason why using an ActiveRecord object (#instance_variable) works is because of Ruby's core functionality -- it's a object orientated language. Being object orientated, it means that each time you populate an ActiveRecord object, you'll basically give Rails a series of other information, such as model_name etc.
This means when you pass the #instance_variable to the form_for method, Rails will be able to take the data from ActiveRecord & process it on screen for you
I'm building a martial arts related database, currently I have the following associations set up:
Student has_and_belongs_to_many :styles
Style has_many :ranks
Student has_many :ranks, through: :gradings (and vice versa)
I'm generating a form as follows, depending on the student's styles:
So the headings are generated by the Style model (Tai Chi, Karate...), then their rankings listed below (taken from the Rank model), and the "Dojo" and "Date" fields should belong to the Grading model once created.
The question: I know how to build a form that creates one association (or one association + its children), but how do I build a form that creates multiple associations at once?
Also, what would be a clean way to implement the following:
Only lines which are ticked become associations
Dojo and date must be filled in for ticked lines to save successfully
If a line is unticked it will destroy any previously created associations
This is what I've currently implemented to retrieve the correct records:
class GradingsController < ApplicationController
before_filter :authenticate_sensei!
def index
#student = Student.includes(:styles).find(params[:student_id])
#ranks = Rank.for_student_styles(#student)
split_ranks_by_style
end
private
def split_ranks_by_style
#karate = #ranks.select_style("Karate")
#tai_chi = #ranks.select_style("Tai Chi")
#weaponry = #ranks.select_style("Weaponry")
end
end
# Rank model
def self.for_student_styles(student)
includes(:style).where("styles.id in (?)", student.styles.map(&:id))
end
def self.select_style(style)
all.map { |r| r if r.style.name == style }.compact
end
Complicated forms like this are best handled in a service object initiated in the primary resource's create or update action. This allows you to easily find where the logic is happening afterwards. In this case it looks like you can kick off your service object in your GradingsController. I also prefer formatting a lot of the data in the markup, to make the handling easier in the service object. This can be done a'la rails, by passing a name like "grade[style]" and "grade[rank]". This will format your params coming in as a convenient hash: {grade: {style: "karate", rank: "3"}}. That hash can be passed to your service object to be parsed through.
Without really grasping the full extent of your specific requirements, let's put together an example form:
<%= form_for :grading, url: gradings_path do |f| %>
<h1><%= #rank.name %></h1>
<%- #grades.each do |grade| %>
<div>
<%= hidden_field_tag "grade[#{grade.id}][id]", grade.id %>
<%= check_box_tag "grade[#{grade.id}][active]" %>
...
<%= text_field_tag "grade[#{grade.id}][date]" %>
</div>
<%- end %>
<%= submit_tag %>
<%- end %>
With a form like this, you get your params coming into the controller looking something like this:
"grade"=>{
"1"=>{"id"=>"1", "active"=>"1", "date"=>"2013-06-21"},
"3"=>{"id"=>"3", "date"=>"2013-07-01"}
}
Nicely formatted for us to hand off to our service object. Keeping our controller nice and clean:
class GradingsController < ApplicationController
def index
# ...
end
def create
builder = GradeBuilder.new(current_user, params['grade'])
if builder.run
redirect_to gradings_path
else
flash[:error] = 'Something went wrong!' # maybe even builder.error_message
render :action => :index
end
end
end
So now we just need to put any custom logic into our builder, I'd probably recommend just making a simple ruby class in your /lib directory. It could look something like this:
class GradeBuilder
attr_reader :data, :user
def self.initialize(user, params={})
#user = user
#data = params.values.select{|param| param['active'].present? }
end
def run
grades = data.each{|entry| build_grade(entry)}
return false if grades.empty?
end
private
def build_grade(entry)
grade = Grade.find(entry['id'])
rank = grade.rankings.create(student_id: user, date: entry['date'])
end
end
There will obviously need a lot more work to pass all the specific data you need from the form, and extra logic in the GradeBuilder to handle edge cases, but this will give you a framework to handle this problem in a maintainable and extensible way.