Im currently trying to convert my price_in_cents field to the virtual attribute of price_in_dollars. I did some research and basically implemented everything from the railscast virtual attributes video, here is the link below.
https://www.youtube.com/watch?v=Tr7tD2GPiXU
At first, the column in my db was 'amount' for the money field. So I ran a
$ rails g migration add_price_in_cents_to_payments price_in_cents:integer
then,
$ rake db:migrate
My application is just a basic CRUD scaffold for creating new payments.
I added the getter setter methods into the payment.rb file, like so.
class Payment < ActiveRecord::Base
attr_accessible :amount, :price_in_dollars, :from, :to
# The price_in_dollars attribute is taking the place of :amount
def price_in_dollars
price_in_cents.to_d/100 if price_in_cents
end
def price_in_dollars=(dollars)
self.price_in_cents = dollars.to_d*100 if dollars.present?
end
end
And changed the form field to represent the new price_in_dollars attribute.
<div class="field">
<%= f.label :price_in_dollars %><br />
<%= f.text_field :price_in_dollars %>
</div>
But now when I submit a new payment, it returns the NoMethodError for my "new" and "create" methods in the payments controller. In the video, Bates does not even touch his controller. Here is my controller.
def new
#payment = Payment.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #payment }
end
end
# GET /payments/1/edit
def edit
#payment = Payment.find(params[:id])
end
# POST /payments
# POST /payments.json
def create
#payment = Payment.new(params[:payment])
respond_to do |format|
if #payment.save
format.html { redirect_to #payment, notice: 'Payment was successfully created.' }
format.json { render json: #payment, status: :created, location: #payment }
else
format.html { render action: "new" }
format.json { render json: #payment.errors, status: :unprocessable_entity }
end
end
end
Do I need to specify to pass in the (params[:price_in_dollars]) in all my CRUD methods in the controller?
I'm a novice with only about 3 weeks of rails knowledge. Please help anyway possible.
Related
Hi I am currently working on a web marketplace app for an assignment that allows users to upload items for sale with images attached, and to edit those listings.
Currently i have utilised simple forms for the edit and add product pages and those work fine but when I click update or add product I get the below error:
ActiveSupport::MessageVerifier::InvalidSignature in ProductsController#create
the error pointed out that line 3 of the below code is the problem:
def create
#product = Product.new(product_params)
#product.user_id = current_user.id
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: "Product was successfully created." }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
My product.rb file looks as such:
belongs_to :user, :optional => true
has_one_attached :picture
end
The simple forms work but for reference the form html looks like such:
<%= simple_form_for#product do |f| %>
<h1 class="heading">Edit Product</h1>
<%= render 'form', product: #product %>
<% end %>
Any help I can get would be appreciated.
When you use the respond_to do |format| method, you need to supply the actual formats that the code should respond to. So, your controller probably needs to look something like this:
def create
#product = Product.new(product_params)
#product.user_id = current_user.id
respond_to do |format|
format.html do
if #product.save
...
end
end
end
But I'd also ask: why are you using respond_to here if you only expect to process the result of a web form? It's something you may need to do at some point, but isn't required initially and adds complication.
If that doesn't resolve the issue, we'll probably need to see your strong-params function (product_params), the Product model definition and your view, at least. For example, you may need to structure the line more like this, because your params may well not exactly match the fields in your model.
#product = Product.new(id: product_params[:id], name: product_params[:name])
Rails adds a special authentication code when it creates a form, to help stop bad actors from spamming or hacking your form. If your page is changed after the code is created, it will become out of date and be rejected, but I'd expect a more specific error if that was happening. Fingers crossed!
So I create two models, one "Course" and one "Section" with scaffold and need Section to display Courses in a drop down menu that reflects any courses that were created in the course model and use it to create in section. I've been able to get the drop down menu displaying the courses created from "Course", but when I create the new section is displays the course as blank. Course has a Name, Department, Number, and Credit Hours. Section has Semester, Number, Course, and Room Number.
What I modified to make the drop down menu was ( in _form.html.erb of views of section )
<div class="field">
<%= form.label "Courses", class: 'courses'%>
<%= form.collection_select(:section, Course.all, :id, :name) %>
</div>
This gives an error of "Courses must exist"
Previously I had:
<div class="field">
<%= form.label "Courses", class: 'courses'%>
<%= form.collection_select(:course_ids, Course.all, :id, :name) %>
This did not give an error and allowed me to create a section, just without adding the selected course to the section database.
https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select
From reading it appears :name should be defined in the models portion of Course, but when I try it gives an error. I also realize I do not have it set to record Course to a specific section ID which is why it isn't saving it when a new section is created. My question is, what do I add or modify to make that work? Is using collection select the wrong thing to do?
EDIT to include sections_controller.rb
class SectionsController < ApplicationController
before_action :set_section, only: [:show, :edit, :update, :destroy]
# GET /sections
# GET /sections.json
def index
#sections = Section.all
end
# GET /sections/1
# GET /sections/1.json
def show
end
# GET /sections/new
def new
#section = Section.new
end
# GET /sections/1/edit
def edit
end
# POST /sections
# POST /sections.json
def create
#section = Section.new(section_params)
respond_to do |format|
if #section.save
format.html { redirect_to #section, notice: 'Section was successfully created.' }
format.json { render :show, status: :created, location: #section }
else
format.html { render :new }
format.json { render json: #section.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /sections/1
# PATCH/PUT /sections/1.json
def update
respond_to do |format|
if #section.update(section_params)
format.html { redirect_to #section, notice: 'Section was successfully updated.' }
format.json { render :show, status: :ok, location: #section }
else
format.html { render :edit }
format.json { render json: #section.errors, status: :unprocessable_entity }
end
end
end
# DELETE /sections/1
# DELETE /sections/1.json
def destroy
#section.destroy
respond_to do |format|
format.html { redirect_to sections_url, notice: 'Section was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_section
#section = Section.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def section_params
params.require(:section).permit(:semester, :number, :course, :room_number)
end
end
I believe I need to relate them somehow with the last part:
def section_params
params.require(:section).permit(:semester, :number, :course, :room_number)
EDIT:
(source: rubyisonrails.com)
http://rubyisonrails.com/pictures/part2.PNG">
First, you should change courses to course in the Section model. The association name for the belongs_to should always be singular
class Section < ApplicationRecord
belongs_to :course #singular name
end
Second, you should have course_id column instead of course in the sections table. You can generate a migration which will reflect these changes in the table
rails g migration modify_sections
The above command should generate a file like xxxxxxxmodify_sections.rb under db/migrate folder. Open the file and add
def change
add_column :sections, :course_id, :integer
remove_column :sections, :course, :integer
end
and do rake db:migrate
Now change the collection_select like the below
<%= form.collection_select(:course_id, Course.all, :id, :name) %>
And in the sections_controller#create, add
#section.course_id = params[:section][:course_id]
before the respond_to do |format|
Finally, change course to course_id in the section_params method.
def section_params
params.require(:section).permit(:semester, :number, :course_id, :room_number)
end
Note:
As you are very new to the technology, I recommend you to follow the Guides to learn.
Im new on Rails and I feel a kind of stuck right now, I need to develop an app where you associate Congresses and Users, has_and_belong_to_many, the point is that I have this on my view:
<div class="field">
<%= f.label :administrators_id %>
<%= f.collection_check_boxes :administrators_id, #users,'id','user_id',{multiple: true, class: 'checkbox inline'} %>
this is on my controllers:
def new
#congress = Congress.new
#users = User.all
end
# GET /congresses/1/edit
def edit
#users = User.all
end
# POST /congresses
# POST /congresses.json
def create
#congress = Congress.new(congress_params)
respond_to do |format|
if #congress.save
format.html { redirect_to #congress, notice: 'Congress was successfully created.' }
format.json { render :show, status: :created, location: #congress }
else
format.html { render :new }
format.json { render json: #congress.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /congresses/1
# PATCH/PUT /congresses/1.json
def update
respond_to do |format|
if #congress.update(congress_params)
format.html { redirect_to #congress, notice: 'Congress was successfully updated.' }
format.json { render :show, status: :ok, location: #congress }
else
format.html { render :edit }
format.json { render json: #congress.errors, status: :unprocessable_entity }
end
end
end
But I dont get it to work, checked vales adoes not store at all.
Thanks for your help!
class User < ApplicationRecord
has_many :adminstrations
has_many :congresses, through: :adminstrations
end
class Congress < ApplicationRecord
has_many :adminstrations
has_many :adminstrators, through: :adminstrations,
source: :user
end
class Administration < ApplicationRecord
belongs_to :user
belongs_to :congress
end
See Why You Don’t Need Has_and_belongs_to_many Relationships. You can call the join model whatever you want. But in 99% of cases you'll want to have it sooner or later.
To assign associations from an array (created by a select or checkboxes) you need to use the _ids setter. Not _id. _id is singular and will only work if the relation is one to one.
<div class="field">
<%= f.label :administrators_ids %>
<%= f.collection_check_boxes :administrators_ids, #users, :id, :email, {multiple: true, class: 'checkbox inline'} %>
</div>
The two methods should be the value and the label method to be used on the user instance when generating the option tags. Use symbols here instead of strings for readability.
Make sure you are whitelisting the correct param as well:
def congress_params
params.require(:congress)
.permit(:foo, :bar, :administrators_ids)
end
I am a rails newbie and building a little application to help with my work.
I have client, site and quote models and controllers with views set up.
I have created a form on the quote model that pulls data from the other two models in a collection_select field. The documentation on collection_select for rails that I have found is pretty bad. I want to take a client name and site name and associate/ display the name on the quote.
I have set this up in the form, but it does not save the data or show it.
I really want to understand the inputs for the collection_select as I am sure mine are probably wrong and causing the issue.
<%= f.collection_select :client, Client.all, :quote_client, :client_name , {:prompt => "Please select a client for the site"} %>
I did some research and learned this from #juanpastas here
My form looks like so
quotes/views/_form.html
<%= form_for(quote) do |f| %>
<% if quote.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(quote.errors.count, "error") %> prohibited this quote from being saved:</h2><ul>
<% quote.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %><div class="field">
<%= f.label :client %>
<%= f.collection_select :client, Client.all, :quote_client, :client_name , {:prompt => "Please select a client for the site"} %>
</div><div class="field">
<%= f.label :site_name %>
<%= f.collection_select :site, Site.all, :quote_site, :site_name , {:prompt => "Please select a site for the quote"} %>
</div><div class="field">
<%= f.label :quote_contact %>
<%= f.text_field :quote_contact %>
</div><div class="field">
<%= f.label :quote_value %>
<%= f.text_field :quote_value %>
</div><div class="field">
<%= f.label :quote_description %>
<%= f.text_field :quote_description %>
</div><div class="actions">
<%= f.submit %>
</div>
<% end %>
EDIT
Answers/clarifications
Quotes can only have one client and one site. The site would also have to belong to the client.
I have a list of clients called from the Client model via Client.all and a list of sites via the Site Model called via Site.all. I only need the name of one Client and one Site for each quote but want to be able to select in a cascading fashion. Select Client, then selects Site from those available for the Client.
Relations are set up between the three models like so:
class Quote < ApplicationRecord
belongs_to :site, optional: true
belongs_to :client, optional: true
has_and_belongs_to_many :assets
end
class Site < ApplicationRecord
has_attached_file :site_image, styles: { small: "64x64", med: "100x100", large: "200x200" }
do_not_validate_attachment_file_type :site_image
belongs_to :client , optional: true
has_and_belongs_to_many :assets
has_and_belongs_to_many :quotes
end
class Client < ApplicationRecord
has_and_belongs_to_many :sites
has_and_belongs_to_many :assets
has_and_belongs_to_many :quotes
end
Controllers
class QuotesController < ApplicationController
before_action :set_quote, only: [:show, :edit, :update, :destroy]
# GET /quotes
# GET /quotes.json
def index
#quotes = Quote.all
end
# GET /quotes/1
# GET /quotes/1.json
def show
end
# GET /quotes/new
def new
#quote = Quote.new
end
# GET /quotes/1/edit
def edit
end
# POST /quotes
# POST /quotes.json
def create
#quote = Quote.new(quote_params)
respond_to do |format|
if #quote.save
format.html { redirect_to #quote, notice: 'Quote was successfully created.' }
format.json { render :show, status: :created, location: #quote }
else
format.html { render :new }
format.json { render json: #quote.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /quotes/1
# PATCH/PUT /quotes/1.json
def update
respond_to do |format|
if #quote.update(quote_params)
format.html { redirect_to #quote, notice: 'Quote was successfully updated.' }
format.json { render :show, status: :ok, location: #quote }
else
format.html { render :edit }
format.json { render json: #quote.errors, status: :unprocessable_entity }
end
end
end
# DELETE /quotes/1
# DELETE /quotes/1.json
def destroy
#quote.destroy
respond_to do |format|
format.html { redirect_to quotes_url, notice: 'Quote was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_quote
#quote = Quote.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def quote_params
params.require(:quote).permit(:quote_client, :quote_site, :client_name, :site_name, :quote_contact, :quote_value, :quote_description)
end
end
class SitesController < ApplicationController
before_action :set_site, only: [:show, :edit, :update, :destroy]
# GET /sites
# GET /sites.json
def index
#sites = Site.all
#clients = Client.all
end
# GET /sites/1
# GET /sites/1.json
def show
#sites = Site.all
#clients = Client.all
end
# GET /sites/new
def new
#site = Site.new
end
# GET /sites/1/edit
def edit
end
# POST /sites
# POST /sites.json
def create
#site = Site.new(site_params)
respond_to do |format|
if #site.save
format.html { redirect_to #site, notice: 'Site was successfully created.' }
format.json { render :show, status: :created, location: #site }
else
format.html { render :new }
format.json { render json: #site.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /sites/1
# PATCH/PUT /sites/1.json
def update
respond_to do |format|
if #site.update(site_params)
format.html { redirect_to #site, notice: 'Site was successfully updated.' }
format.json { render :show, status: :ok, location: #site }
else
format.html { render :edit }
format.json { render json: #site.errors, status: :unprocessable_entity }
end
end
end
# DELETE /sites/1
# DELETE /sites/1.json
def destroy
#site.destroy
respond_to do |format|
format.html { redirect_to sites_url, notice: 'Site was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_site
#site = Site.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def site_params
params.require(:site).permit(:site_client, :client_name, :site_name, :site_image, :site_address, :site_contact)
end
end
class ClientsController < ApplicationController
before_action :set_client, only: [:show, :edit, :update, :destroy]
# GET /clients
# GET /clients.json
def index
#clients = Client.all
#sites = Site.all
end
# GET /clients/1
# GET /clients/1.json
def show
#clients = Client.all
#sites = Site.all
end
# GET /clients/new
def new
#client = Client.new
end
# GET /clients/1/edit
def edit
end
# POST /clients
# POST /clients.json
def create
#client = Client.new(client_params)
respond_to do |format|
if #client.save
format.html { redirect_to #client, notice: 'Client was successfully created.' }
format.json { render :show, status: :created, location: #client }
else
format.html { render :new }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /clients/1
# PATCH/PUT /clients/1.json
def update
respond_to do |format|
if #client.update(client_params)
format.html { redirect_to #client, notice: 'Client was successfully updated.' }
format.json { render :show, status: :ok, location: #client }
else
format.html { render :edit }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
# DELETE /clients/1
# DELETE /clients/1.json
def destroy
#client.destroy
respond_to do |format|
format.html { redirect_to clients_url, notice: 'Client was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_client
#client = Client.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def client_params
params.require(:client).permit(:client_name, :client_address, :client_phone, :client_email, :client_website)
end
end
Additions
You may notice I have tried to scale so that a client is called in a site and a site and a client is called in a quote.
First of all: I'm assuming you have relations set up between the three models! There has to be a has_many relation from quote to client and from quote to site.
There are two issues which could prevent your form from saving.
Firstly it is in how you create your collection_select. The third parameter in collection select is what will be sent to the controller. This should be an array of IDs (I assume a quote can have more than ONE client). I see you call it :quote_client. I'd rename it to :client_ids. In the end that's what you want to send to your controller: an array of IDs.
The second thing you have to take care of is your controller. It would be nice if you shared your controller code, but I assume you have a quotes_controller with a quote_params method inside it. It will probably look like this:
def quote_params
params.require(:quote).permit(:quote_contact, etc., etc.)
end
This controller method has to respond with your form_for, so every field in your form_for (like quote_contact) should be in the permit, otherwise it won't get saved. If you want to save an array of IDs, you have to tell this method you're expecting an array of IDs. You can do that like so: client_ids: [].
So your new quote_params method should look like this:
def quote_params
params.require(:quote).permit(:quote_contact, client_ids: [], site_ids: [], all_other_fields...)
end
I hope this answer provides you with your much needed help. If I need to clarify more: just ask :)
Cheers
EDIT: the answer above is still relevant for those who do want to save multiple records, but because you stated you do only want to save one record here is my updated answer:
The logic I summed up above stays roughly the same.
What you do not seem to understand at the moment, and what is (IMO) quite vital to understanding Rails applications is the way forms map to controllers and controllers map to the database. The method quote_params, as stated above, should permit all fields from forms you want to save to the database. This means all fields in your permit-part should BE in your database, otherwise they can't be saved. If you look closely at your quote table in the database, you will see that it has fields for client_id and site_id. These two fields hold the reference for your quote/client and quote/site associations. That is why your permit currently is not working, because you have quote_client and quote_site in place. The database does not have a quote_client or quote_site and hence when trying to save, doesn't update associations. The database does have client_id and site_id, so that's what you should pass into your quote params method.
This should of course correspond to the fields in your form_for. So you need change two things to make this work:
Change your two collection_selects and swap :quote_client for :client_id and :quote_site for :site_id.
Change your controller method to reflect the changes in your form_for. Here also you have to swap quote_site and quote_client for quote_id and site_id, like this:
def quote_params
params.require(:quote).permit(:client_id, :site_id, etc.)
end
The important thing to remember when using Rails MODELNAME_params methods (which we call strong parameters -> READ IT! http://edgeguides.rubyonrails.org/action_controller_overview.html)
is that both your form and your permit action should list the fields EXACTLY like they are in the database, otherwise the database won't understand and your record won't be properly saved.
I hope with this edit you'll figure it out.
Cheers
I am trying to learn how to use namespaces in my Rails 5 app.
I have an organisation model and I have also made a series of nested models under the folder name "stance". One of those models is called overview.
The associations are:
Organisation.rb
has_one :overview, class_name: Stance::Overview
accepts_nested_attributes_for :overview, reject_if: :all_blank, allow_destroy: true
Stance::Overview
class Stance::Overview < ApplicationRecord
belongs_to :organisation, inverse_of: :overview
My controllers for stance resources are nested under a folder called stance.
My routes are:
namespace :stance do
resources :overviews
end
In my stance view partial, I am trying to render the attributes from the overview table.
I have tried:
<p><%= #overview.internal_explanation %></p>
<p><%= #stance_overview.internal_explanation %></p>
<p><%= #stance.overview.internal_explanation %></p>
<p><%= #stance::overview.internal_explanation %></p>
I want to display this partial in my organisation show. I am trying to do that with:
<%= render 'stance/overviews/internal', overview: #overview %>
But I can't figure out how to access the overview table. Do I need to add a reference to 'stance' in the associations?
I can see that in the console I need to write:
o = Stance::Overview.create(internal_explanation: "test")
o = Stance::Overview.first
but I can't see how to use that in the code itself.
I can see in the console that there is a record for this attribute.
The name of the table in the schema is "stance_overview".
My organisation controller has:
class OrganisationsController < ApplicationController
before_action :set_organisation, only: [:show, :edit, :update, :destroy]
def index
#organisations = Organisation.all
end
def show
end
def new
#organisation = Organisation.new
#organisation.build_overview
end
def edit
#organisation.build_overview unless #organisation.overview
end
def create
#organisation = Organisation.new(organisation_params)
respond_to do |format|
if #organisation.save
format.html { redirect_to #organisation, notice: 'Organisation was successfully created.' }
format.json { render :show, status: :created, location: #organisation }
else
format.html { render :new }
format.json { render json: #organisation.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #organisation.update(organisation_params)
format.html { redirect_to #organisation, notice: 'Organisation was successfully updated.' }
format.json { render :show, status: :ok, location: #organisation }
else
format.html { render :edit }
format.json { render json: #organisation.errors, status: :unprocessable_entity }
end
end
end
def destroy
#organisation.destroy
respond_to do |format|
format.html { redirect_to organisations_url, notice: 'Organisation was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_organisation
#organisation = Organisation.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def organisation_params
params.fetch(:organisation, {}).permit(:title, :comment,
overview_attributes: [:internal_explanation, :external_explanation ]
)
end
end
I have also tried defining the strong params for organisation as:
stance_overview_attributes: [:internal_explanation, :external_explanation ]
I keep getting an error that says:
undefined method `internal_explanation' for nil:NilClass
Can anyone refer me to materials to help me learn how to use namespaces in my app. I am trying to understand the fundamentals of this so that I can bank some knowledge. I am finding things through trial and error but not understanding what's actually required (although in this case, none of my attempts are working out).
To access Overview model(table) when you working not in Stance namespace you have to use Stance::Overview. If working for example in a controller that in Stance namespace you can use just Overview for access.
To get access from the relation you don't need any additional notation just #organisation.overview.
If I understand correctly in you case you have to declare your partial as
<%= render 'stance/overviews/internal', overview: #organisation.overview %>
and in the partial you have to use overview without #.