Ruby on Rails 4.1
The form has an option to select the table column name. I want to input text into the table column selected by the form. To do this I am trying to make temporary attributes that the form can use to store the value and examine in the create method. Then assign the text to the correct column, then save.
Controller:
def new
#word = Word.new
#language = Word.new(params[:language])
#translation = Word.new(params[:translation])
#language_options = Word.column_names
end
def create
#word = Word.new(word_params)
if #language == "arabic"
#word.arabic == #translation
end
respond_to do |format|
if #word.save
format.html { redirect_to #word, notice: 'Word was successfully created.' }
format.json { render :show, status: :created, location: #word }
else
format.html { render :new }
format.json { render json: #word.errors, status: :unprocessable_entity }
end
end
end
The form:
<%= simple_form_for(#word) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :name, placeholder: 'English String' %>
<%= f.input :language, collection: #language_options %>
<%= f.input :translation, placeholder: 'Translated String' %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
This is the error I get:
undefined method `language' for #<Word:0x007f6116b1bcb8>
Which is because there is not a language attribute for the form to use. So I was trying to make a temporary one in the controller new().
Is there a way to do this or do I have to make :language and :translation in a database table to reference in the form?
Virtual Attribute
You may benefit from using an attr_accessor in your model
This creates a virtual attribute which works the same as the "real" attributes in your model:
#app/models/word.rb
Class Word < ActiveRecord::Base
attr_accessor :column_name
end
This will allow you to assign values to this attribute which won't be saved into the db, which sounds like what you want:
#app/views/words/new.html.erb
<%= simple_form_for(#word) do |f| %>
<%= f.input :column_name do %>
<%= f.select :column_name, #language_options %>
<% end %>
<% end %>
When you submit this form, it will then give you the column_name attribute to edit:
#app/controllers/words_controller.rb
Class WordsController < ApplicationController
def create
# ... you'll have "column_name" attribute available
end
end
Related
I am building a small marketplace app using rails and currently am stuck on trying to get my product edit and create pages to work. I have added image upload capability and have utilised simple forms to add details for the products but any time I try to proceed with editing I get the following error:
The action 'update' could not be found for ProductsController
Meanwhile if I try to create a new product I get a different error:
{"seller":["must exist"]}
Please see my code below:
-products_controller.rb
def create
#product = Product.create
#product_id = #product.id
if #product.save
render :show, status: :created
else
render json: #product.errors, status: :unprocessable_entity
end
end
end
# PATCH/PUT /products/1 or /products/1.json
def update
#product = Product.update (product_params)
if #product.save
render products:id, status: :created
else
render json: #product.errors, status: :unprocessable_entity
end
end
def product_params
params.require(:product).permit(:title, :description, :price, :buyer_id, :seller_id, :category, :image_url)
end
edit.html.erb
<%= simple_form_for edit_product_path, url: {action: "update"} do |f| %>
<h1 class="heading">Edit Product</h1>
<%= render 'form', product: #product %>
<% end %>
-form.html.erb
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :description %>
<%= f.input :price %>
<%= f.input :category, collection: ["footwear", "accessories", "menswear", "womenswear"] %>
<div class="form-group">
<% if product.picture.attached? %>
<%= image_tag product.picture, style: "width: 200px; display: block" %>
<% end %>
<%= f.file_field :picture %>
</div>
</div>
Any help I can get is greatly appreciated.
#product = Product.create
#product_id = #product.id
if #product.save
render :show, status: :created
else
render json: #product.errors, status: :unprocessable_entity
end
end <--- extra end
end
It looks like you have an extra hanging end in the middle of your create action. This would probably explain what's going on with it.
If this doesn't fix the issue, ensure that you have the proper routes defined in routes.rb.
If you keep getting the seller must exist error, Rails 5/6 automatically assumes that a belongs_to association will have a model present to associate to when the record is saved. You can turn this off by adding optional: true to your relationship definition like this:
class Product
belongs_to :seller, optional: true
end
You can include a seller_id in your SimpleForm on page load and associate it with that as long as you add the appropriate filter in product_params. It might look something like:
<%= f.input :seller_id, :input_html => { :value => #seller.id } %>
Goal: Update existing records with a modal without needing to link_to a new page.
Issue: I assume my issue is that I am unable to identify the exact record on the page with the form because I can't know this until the form is submitted.
ShopProduct Controller:
def new
#shop_product = ShopProduct.new
end
def create
#shop_product = ShopProduct.new(shop_product_params)
#shop = Shop.find_by(params[:shop_id])
product = Product.find_by(params[:product_id])
#shop_product.product_id = product.id
#shop_product.shop_id = #shop.id
if #shop_product.save!
redirect_to '/'
flash[:notice] = "saved"
else
redirect_to '/'
flash[:notice] = "no saved"
end
end
def update
#shop_product = ShopProduct.find_by(store_variant_id: params[:store_variant_id])
respond_to do |format|
if #shop_product.update_attributes!(product_id: params[:product_id], sync: params[:sync])
format.html { redirect_to #shop_product, notice: 'Shop product was successfully updated.' }
format.json { render :show, status: :ok, location: #shop_product }
else
format.html { render :edit }
format.json { render json: #shop_product.errors, status: :unprocessable_entity }
end
end
end
Aside from linkingto a new page, I can only think of defining directly on the
I load the form from this ShopDashboardController:
def product_variants
#shop = Shop.find(params[:shop_id])
session = ShopifyAPI::Session.new(domain: #shop.shopify_domain, token: #shop.shopify_token, api_version: '2019-04')
ShopifyAPI::Base.activate_session(session)
#in_store_products = ShopifyAPI::Product.find(:all)
#in_store_product = ShopifyAPI::Product.find(params[:shopify_product_id])
#in_store_variants = ShopifyAPI::Variant.find(:all, params: { product_id: params[:shopify_product_id]})
#shop_products = ShopProduct.where(shop_id: #shop)
#products = Product.all
#shop_product = ShopProduct.find_or_create_by(store_variant_id: params[:store_variant_id])
end
Now, as mentioned above, the only unique record for any ShopProduct is the id and the store_variant_id... If i use find_by in the def product_variants, the page won't load due to not being able to identify the #shop_product. I am unable to pass those params through because there may be multiple store_variant_ids, so I pass the Shop.id and ShopProduct.store_product_id only. But the store_product_id isn't a unique identifier as multiple records can have the same one. The only unique records are the id and store_variant_id.
Form (the variant is from a do loop):
<% #in_store_variants.each do |variant| %>
...
<%= form_for #shop_product do |f| %>
<%= f.collection_select :product_id, #products, :id, :sku %>
<%= f.hidden_field :store_product_id, value: variant.product_id %>
<%= f.hidden_field :store_variant_id, value: variant.id %>
<%= f.hidden_field :shop_id, value: #shop.id %>
<%= f.hidden_field :sync, value: true %>
<%= f.submit "Sync" %>
...
<% end %>
I am able to create new records only.
When i use the form again to update I get:
ActiveRecord::RecordInvalid (Validation failed: Store variant has already been taken):
app/controllers/shop_products_controller.rb:61:in `create'
Model ShopProduct:
belongs_to :product
has_one :shop
has_one :order
validates :store_variant_id, uniqueness: true, on: :create
If the record exists, shouldn't it update? Or is there something I am missing here?
It is possible to pursue my goal with rails/ruby alone or is javascript needed?
UPDATE:
I tried defining the ShopProduct on the front-end like so:
<% #in_store_variants.each do |variant| %>
<% shop_product = #shop_products.find_by(store_variant_id: variant.id) %>
<%= form_for shop_product do |f| %>
<%= f.collection_select :product_id, #products, :id, :sku %>
<%= f.hidden_field :store_product_id, value: variant.product_id %>
<%= f.hidden_field :store_variant_id, value: variant.id %>
<%= f.hidden_field :shop_id, value: #shop.id %>
<%= f.hidden_field :sync, value: true %>
<%= f.submit "Sync" %>
<% end %>
When submitting:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"gaMboYCSE8v63TVzmgx4pZDMhoz205f1MV+VMhmFA/WWhVh5Pcu6u/qayU8lDmjeRXw==", "shop_product"=>{"product_id"=>"1", "store_product_id"=>"1965345", "store_variant_id"=>"19364273", "shop_id"=>"1", "sync"=>"true"}, "commit"=>"Sync", "id"=>"12"}
Error:
NoMethodError (undefined method `update' for nil:NilClass):
or with update attributes:
NoMethodError (undefined method `update_attributes!' for nil:NilClass):
If it's finding it, shouldn't it be working? The param is being passed
It's because that form only caters the create action. Usually, if you need to update a resource, you go to /shop_products/:id/edit.
But if you really wanted to reuse that form, it's a little bit complicated adding more conditions, but what you want is to send a PUT request to /shop_products/:id and it would call the #update action of your controller. A form, by default, sends a POST request so consider that.
I have a rails 4 application that has an add page and and a edit page. You can add elements easily (there is no issues), but then when you go to edit those and click save, it adds the fields you added initially a second time.
Here is my _form.html.erb
<%= nested_form_for #store do |f| %>
<%= f.fields_for :products do |product_form| %>
<div class='field'>
<%= product_form.text_field :name %>
<%= product_form.hidden_field :_destroy %>
<%= link_to "REMOVE PRODUCT", '#', class: "remove_fields" %>
</div>
<% end %>
<p><%= f.link_to_add "Add PRODUCT", :products %></p>
<%= f.submit 'Save', :class => "primary small" %>
<% end %>
and my store.rb model:
class Store < ActiveRecord::Base
has_many :products, class_name: "StoreProduct"
accepts_nested_attributes_for :products, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end
my update action in my controller looks like:
def update
respond_to do |format|
if #store.update(store_params)
format.html { redirect_to store_products_path(#store), notice: 'Store was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #store.errors, status: :unprocessable_entity }
end
end
end
What does store_params look like in your controller? If id isn't one of the permitted values, then you can start to see the nested models created as new records each time the update action occurs. You would want to have something like:
params.require(:store).permit(products_attributes: [:id, :name, :_destroy])
See the documentation on strong parameters for the nested_form gem.
I'm trying to get the text from a text_area field in a form to save to a database in a different Model with the current Model's ID.
Currently, this works but only will save integers. If I put text into the 'Notes' field, then its saves it as a '0'. I suspect this is working correctly but I'm missing a piece to my puzzle. This is because I only want the 'Ticket' to save the note_id because I will have multiple 'Notes' per 'Ticket.
How can I get the Note to save in the Note Model, with an ID, and associate that note_id with this specific ticket?
Form - /app/views/tickets/_form.html.erb
<%= form_for(#ticket) do |f| %>
<% if #ticket.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#ticket.errors.count, "error") %> prohibited this ticket from being saved:</h2>
<ul>
<% #ticket.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.fields_for :notes do |u|%>
<%= u.label :note %>
<%= u.text_area :note, :size => "101x4", :placeholder => "Leave notes here." %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Tickets_controller.rb
class TicketsController < ApplicationController
# GET /tickets
# GET /tickets.json
def index
#tickets = Ticket.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #tickets }
end
end
# GET /tickets/1
# GET /tickets/1.json
def show
#ticket = Ticket.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #ticket }
end
end
# GET /tickets/new
# GET /tickets/new.json
def new
#ticket = Ticket.new
#ticket.notes.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #ticket }
end
end
# GET /tickets/1/edit
def edit
#ticket = Ticket.find(params[:id])
end
# POST /tickets
# POST /tickets.json
def create
#ticket = Ticket.new(params[:ticket])
respond_to do |format|
if #ticket.save
format.html { redirect_to #ticket, notice: 'Ticket was successfully created.' }
format.json { render json: #ticket, status: :created, location: #ticket }
else
format.html { render action: "new" }
format.json { render json: #ticket.errors, status: :unprocessable_entity }
end
end
end
# PUT /tickets/1
# PUT /tickets/1.json
def update
#ticket = Ticket.find(params[:id])
respond_to do |format|
if #ticket.update_attributes(params[:ticket])
format.html { redirect_to #ticket, notice: 'Ticket was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #ticket.errors, status: :unprocessable_entity }
end
end
end
# DELETE /tickets/1
# DELETE /tickets/1.json
def destroy
#ticket = Ticket.find(params[:id])
#ticket.destroy
respond_to do |format|
format.html { redirect_to tickets_url }
format.json { head :no_content }
end
end
end
Note.rb
class Note < ActiveRecord::Base
belongs_to :ticket
attr_accessible :note, :ticket_id
end
Ticket.rb
class Ticket < ActiveRecord::Base
attr_accessible :notes_attributes
accepts_nested_attributes_for :notes
end
It is because note_id is an integer type.
Use nested models:
Refer this for Nested Models
Model:
class Ticket < ActiveRecord::Base
has_many :notes
attr_accessible :note_id, :notes_attributes
accepts_nested_attributes_for :notes
end
View:
<%= form_for(#ticket) do |f| %>
<%= f.fields_for :notes do |u|%>
<%= u.label :note %>
<%= u.text_area :note %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
What you have is a nested association, with Ticket as the "parent". The association is governed by the link from note_id in the Note model to the id (primary key) of the Ticket. What you're presently doing right now is manually manipulating that numeric association. Rails, knowing that the note_id column is supposed to be an integer, is taking the text you're trying to insert and turning it in to a number (zero in this case). You've probably got a bunch of orphaned rows right now because of this.
Ultimately, in order to accomplish what you're trying to do, your form will need to provide fields for that associated model. One way you can handle this is by using the accepts_nested_attributes_for in your Ticket model. Like so:
class Ticket < ActiveRecord::Base
has_many :notes
accepts_nested_attributes_for :notes
end
And in your form, you can easily create a nested form like so:
<%= form_for(#ticket) do |f| %>
<div class="field">
<%= f.fields_for :notes do |f_notes|%>
<%= f_notes.label :note %><br />
<%= f_notes.text_area :note, :size => "101x4", :placeholder => "Please leave notes here."%>
<% end %>
</div>
<% end %>
Edit Almost forgot: Check out this Railscast from Ryan Bates dealing with Nested Attributes
Edit 2 As codeit pointed out, you don't need the attr_accessible :note_id in Ticket. Since you've indicated that a Ticket has many Notes, and that Note belongs to Ticket, the foreign key column will appear in the Note model as ticket_id, which you already have. Having note_id in the ticket model is useless, and also nonsensical since has_many describes a plural relationship (which can't be expressed with a single column).
I have two "CRUD" forms generated by the "rails g scaffold ModelName type:attribute1, type2:attribute2" command, which is quite powerful.
I'll try to just show what is relevant. First here are my models (attr_accessible tells the db migration stories for the most part)
class Area < ActiveRecord::Base
attr_accessible :description, :frequency, :name, :area_id
has_many :stations
end
class Station < ActiveRecord::Base
attr_accessible :description, :frequency, :name, :area_id
belongs_to :area
end
Next, here is the _form.html.erb for a Station object (currently I'm using a simple drop down which is OK, but I want those :area_id tags to somehow be able to pull the Area.find(params[:area_id]).name, or something like that. If '1' is Denver and '2' is Boulder, I want '1` to pull 'Denver' and so on on the Station _form.
So here is stations_controller.rb, which was generated by the "generate scaffold" command for the most part.
class StationsController < ApplicationController
def index
#stations = Station.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #stations }
end
end
def show
#station = Station.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #station }
end
end
def new
#area_count = Area.count
#station = Station.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #station }
end
end
def edit
#station = Station.find(params[:id])
end
def create
#station = Station.new(params[:station])
respond_to do |format|
if #station.save
format.html { redirect_to #station, notice: 'Station was successfully created.' }
format.json { render json: #station, status: :created, location: #station }
else
format.html { render action: "new" }
format.json { render json: #station.errors, status: :unprocessable_entity }
end
end
end
def update
#station = Station.find(params[:id])
respond_to do |format|
if #station.update_attributes(params[:station])
format.html { redirect_to #station, notice: 'Station was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #station.errors, status: :unprocessable_entity }
end
end
end
def destroy
#station = Station.find(params[:id])
#station.destroy
respond_to do |format|
format.html { redirect_to stations_url }
format.json { head :no_content }
end
end
end
Lastly, here is the _form.html.erb for Station
<%= form_for(#station) do |f| %>
<% if #station.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#station.errors.count, "error") %> prohibited this station from being saved:</h2>
<ul>
<% #station.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :frequency %><br />
<%= f.text_field :frequency %>
</div>
<div class="field">
<%= f.label :description %><br />
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label :area_id %><br />
<%= f.select(:area_id, 1..#area_count) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
So to reiterate my goal/question, what I have currently for the :area_id selector is a dropdown bar which allows me to select from 1.."Area.count". I would like that drop down bar to list the names of the the different areas given the area_id.
Also, I'd like to be able to see a list of Station objects which are "owned" by a given area on the list of areas or perhaps just on the show.html.erb for a given Area.
For the select box:
You should use the collection_select form helper.
Replace
<%= f.select(:area_id, 1..#area_count) %>
With
<%= f.collection_select :area_id, Area.all, :id, :name %>
For showing the list of stations on the show view of an area:
There are several ways to do this, depending on how you want to display them in the HTML.
Here's one way to obtain a comma separated list of the station names:
<%= #area.stations.pluck(:name).join(", ") %>
If you need additional attributes of the stations besides just :name, you could iterate over the collection rather than using pluck:
<ul>
<% #area.stations.each do |s| %>
<li><%= s.name %> - <%= s.frequency %></li>
<% end %>
</ul>