I have about 3 models that all relate to each other.
Class Invoice < ActiveRecord::Base
belongs_to :shipperline, foreign_key: 'receiver_quantity'
def shippedqty
Shipperline.getshippqty(#customerid).to_a.split
end
Then i have a shipperline model
Class Shipperline < Activerecord::Base
has_many :invoices
def self.getshippqty(customerid)
Shipperline.joins(:customerorderline, :shipper).select("cust_order_id").where(cust_order_id: customerid).order("shipped_qty").pluck(:shipped_qty).uniq
end
However in my form (which is on my main view because this query will usually return 4 to 5 results per invoice which is why i need a user to select which one it really belongs to.
So i have been trying
<%= form_for :invoice do |f| %>
<td id="putcolor"><%= f.select :shippedqty, Shipperline.getshippqty(#customerid).to_a.split %> </td>
<% end %>
but it becomes empty. I have also tried collection_select and that dose not work either.
<td id="putcolor"><%= f.collection_select :invoice_id, #shipqty, :id, :shippedqty %></td>
but then i get undefined method 'map' for nil:NilClass
Can someone explain what im doing wrong or please point me in the right direction? I am fairly new to rails.
Also is there a better approach to trying to get a select box with these various results in it for the user to pick from?
Got this to work much easier this way.
My Invoice_helper.rb
def options_for_invoices(customerid)
Invoice.shippedqty2(customerid)
end
def options_for_packlistid(customerid, shipqty)
Shipperline.packlist(customerid, shipqty)
end
Then in my form i did:
<%= form_for(#invoice) do |f| %>
<%= f.label :shippqty %>
<%= f.select :shippedqty, options_for_invoices(#invoice.customerorderid) %>
<br />
<%= f.label(:packlistid %>
<%= f.select :packlistid, options_for_packlistid(#invoice.customerorderid, #invoice.shippedqty) %>
<%= f.submit %>
<% end %>
Now when it shows a drop down collection with the correct values and inserts the correct values into the database for each record.
Related
When I edit a model with nested attributes everything works perfectly. If I try to create a new record with nested attributes, the nested attributes don't appear on the form that has the nested attributes. I have two models as follows
class JobSpec < ActiveRecord::Base
has_many :availabilities
accepts_nested_attributes_for :availabilities, allow_destroy: true
end
and
class Availability < ActiveRecord::Base
belongs_to :job_spec
end
in the job_specs_controller I have
class JobSpecsController < ApplicationController
def new
#job_spec = JobSpec.new
#availabilities = Availability.none
availability = Availability.new
availability.job_spec = #job_spec
#availabilities << availability
end
def edit
#job_spec = JobSpec.find(params[:id])
#availabilities = #job_spec.availabilities
end
def job_spec_params
params.require(:job_spec).permit(
... all of the job_spec attributes,
availabilities_attributes: [:id, ... all of the availability attributes],
)
end
end
In _form for job_spec I have
<% #availabilities.each do |availability| %>
<%= f.fields_for availability do |builder| %>
<%= render partial: 'availabilities/form_mini',
locals: {f: builder, availability: availability} %>
<% end %>
<% end %>
Since editing works, I assume there's not much wrong. Any ideas?
Update
I've almost sorted out all the problems but still have one. Here's what I've done.
1) I had to build the job_spec availabilities. The new action of the JobSpecsController now looks like this
def new
#job_spec = JobSpec.new
#job_spec.client_id = params[:client_id]
#job_spec.availabilities.build
end
2) I wanted to be able the destroy the nested models, so I had to pass :_destroy in with the nested attributes to job_spec_params. That method looks like this
def job_spec_params
params.require(:job_spec).permit(
... all of the job_spec attributes,
availabilities_attributes: [:id, :_destroy, ... all of the availability attributes],
)
end
end
3) Finally, I had to change _form for job_spec. I have
<%= f.fields_for :availabilities do |builder| %>
<%= render partial: 'availabilities/form_mini', locals: {f: builder} %>
<% end %>
The only issue I have now is that I want to pass and instance variable through to my availability partials. Something like this
<%= render partial: 'availabilities/form_mini', locals: {f: builder, a: availability} %>
How should that be done? I'm not sure how to iterate over all the #availabilities while rendering all the partials.
Glad you worked some of the issues out. First one small improvement
#job_specs_controller.rb
def new
#job_spec = JobSpec.new(client_id: params[:client_id])
#job_spec.availabilities.build
end
And to overcome your last issue, the solution is a mix of what you had before and after. You can pass the actual object in the fields_for method. It's like that:
#_form.html.erb
<% #job_spec.availabilities.each do |availability| %>
<%= f.fields_for :availabilities, availability do |builder| %>
<%= render partial: 'availabilities/form_mini',
locals: {f: builder, a: availability} %>
<% end %>
<% end %>
So I have a route, which I've created a form_for
new_offer_lead GET /offers/:offer_id/leads/new(.:format) leads#new
The leads#new action works.
However, let's say I submit through /offers/1/leads/new The offer_id is not being passed to my Lead model.
Lead.column_names
Lead.column_names
=> ["id", "name", "email", "zip", "sport", "created_at", "updated_at", "offer_id"]
When I create a new lead, I can clearly see the new value in the Lead model, but the offer_id is nil.
Models
class Lead < ActiveRecord::Base
has_many :coupons
belongs_to :offer
class Offer < ActiveRecord::Base
has_many :leads
Form for
<%= bootstrap_form_for(#lead, layout: :horizontal) do |f| %>
<div class="col-xs-4">
<p>
<%= f.text_field :name, :id => "myclass" %>
</p>
<p>
<%= f.text_field :email %>
</p>
<p>
<%= f.text_field :sport %>
</p>
<p>
<%= f.text_field :zip %>
</p>
<p id= "get-coupon-btn">
<%= f.submit "Get Coupon!", :class => "btn btn-primary" %>
</p>
</div>
Routes:
Rails.application.routes.draw do
resources :offers do
resources :leads
end
devise_for :users
resources :offers
resources :coupons
resources :leads
My lead#create controller action
def create
#lead = Lead.new(lead_params)
#offer = Offer.find(params[:offer_id])
if #lead.save
Coupon.assign_coupon(#lead)
redirect_to(#lead) #print the notice
else
render "new"
end
end
Ignore the assign_coupon thing. Any ideas on how I can pass the offer_id into the lead model on create of the lead? Trying to get my head around nested resources and accepts resources for, but can't seem to analogize it to my current situation. Thank you!
Update 1: now this is erroring out: Couldn't find Offer with 'id'=
#offer = Offer.find(params[:offer_id])
#offer = Offer.find(params[:offer_id])
#lead = #offer.leads.build lead_params
if #lead.save
.....
Calling the "build" funcion on a relational attribute, it'll return an instance of that relational object (not persisted) with the id of the object that's referencing it. In this case it returns an instance of "Lead" with the value of offer_id.
I think you need to change your form_tag as well:
<%= bootstrap_form_for([#offer,#lead], layout: :horizontal) do |f| %>
This should give you and offer_id in your controller.
I want to put a search form on my homepage at pages#home. I'd like to be able to search my model by category. I haven't been able to find a solution where you're actually putting the logic on a different controller than the models you're searching on. I was wondering if someone could help with the syntax and where it needs to go. Here are my relations:
vendor.rb
class Vendor < ActiveRecord::Base
validates :category_id, presence: true
belongs_to :category
end
category.rb
class Category < ActiveRecord::Base
has_one :vendor
end
pages_controller.rb
def home
#vendors = Vendor.all
end
routes.rb
root 'pages#home'
I'm trying to put the search form on home.html.erb, which is under the pages layouts. Was hoping someone could help with how I can accomplish this. This being (seemingly) a simple type of search, I'd hopefully not have to use a gem.
EDIT: ANSWER
For those searching, here's what worked. Thank you #Vla
vendors_controller.rb
def search
#vendors = Vendor.search(params)
end
pages/home.html.erb (this is my root 'pages#home')
<%= form_tag vendors_search_path, method: :get do |f| %>
<%= text_field_tag :search, params[:search], placeholder: "Search Vendors" %>
<%= select_tag 'category', options_for_select(Category.all.map{|el| [el.name, el.id]}) %>
<%= submit_tag 'Search' %>
<% end %>
routes.rb (make sure to put this near the top)
get 'vendors/search'
vendors/search.html.erb
<% #vendors.each do |vendor| %>
<%= vendor.name %>
<%= vendor.phone %>
<%= vendor.email %>
<% end %>
Ok, You can do it this way:
Go to Your vendor.rb file and add search method like this:
def self.search params
vendors = Vendor.where(category_id: params[:category].to_i) unless params[:category].blank?
vendors
end
Then in Your vendors_controller create search method:
def search
#vendors = Vendor.search(params)
end
Then create form at homepage similar to:
= form_tag search_vendors_path, method: :get, role: 'form' do
.form-group
%label Category
= select_tag 'category', options_for_select(Category.all.map{|el| [el.name, el.id]}), class: 'form-control'
= submit_tag 'Search'
And do not forget to put
get 'vendors/search'
into Your routes and add "search" view.
You can still do the same without search action, with result on Your homepage. Anyway I hope You got the idea.
Update: Zishe figured it out. Correct params.require code should be:
def adventure_params
params.require(:adventure).permit(:story, :choice, :parent_id, :user_id)
end
with parenthesis instead of a bracket, of course.
Original question:
I am making a form_for that should be submitting 4 attributes to the Adventure model. The adventure model has ancestry. I keep getting wrong number of arguments (4 for 1) based on my params.require method. When I change to requirements down to 1, I see that all the attributes are blank in my database. I know they are in the params but for some reason they are not being saved. Here is my code:
Form
<div class="form">
<%= form_for #adventure do |f| %>
<%= f.label :choice %>
<%= f.text_area :choice %>
<%= f.label :story %>
<%= f.text_area :story %>
<%= f.label :parent_id %>
<%= f.text_field :parent_id %>
<%= f.submit "Post"%>
<% end %>
</div>
Controller
class AdventuresController < ApplicationController
before_filter :authenticate_user!
def home
end
def index
end
def new
#parent_id = params[:parent_id]
#adventure = Adventure.new
end
def show
#adventure = Adventure.find(params[:id])
end
def create
#user = current_user
#adventure = current_user.adventures.build(adventure_params)
if #adventure.save
flash[:success] = "Adventure created!"
redirect_to #adventure
else
flash[:error] = "There was an error"
redirect_to adventures_path
end
end
private
def adventure_params
params.require(:adventure).permit[:story, :choice, :parent_id, :user_id]
end
end
Model
class Adventure < ActiveRecord::Base
has_ancestry
belongs_to :user
validates :user_id, presence: true
validates :parent_id, presence: true
end
I have no idea why I am getting wrong number of arguments since the attributes show up in the params.
Change permit[...] to:
.permit(:story, :choice, :parent_id, :user_id)
How can I upload multiple images from a file selection window using Rails 4 and CarrierWave? I have a post_controller and post_attachments model. How can I do this?
Can someone provide an example? Is there a simple approach to this?
This is solution to upload multiple images using carrierwave in rails 4 from scratch
Or you can find working demo :
Multiple Attachment Rails 4
To do just follow these steps.
rails new multiple_image_upload_carrierwave
In gem file
gem 'carrierwave'
bundle install
rails generate uploader Avatar
Create post scaffold
rails generate scaffold post title:string
Create post_attachment scaffold
rails generate scaffold post_attachment post_id:integer avatar:string
rake db:migrate
In post.rb
class Post < ActiveRecord::Base
has_many :post_attachments
accepts_nested_attributes_for :post_attachments
end
In post_attachment.rb
class PostAttachment < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
belongs_to :post
end
In post_controller.rb
def show
#post_attachments = #post.post_attachments.all
end
def new
#post = Post.new
#post_attachment = #post.post_attachments.build
end
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
params[:post_attachments]['avatar'].each do |a|
#post_attachment = #post.post_attachments.create!(:avatar => a)
end
format.html { redirect_to #post, notice: 'Post was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
private
def post_params
params.require(:post).permit(:title, post_attachments_attributes: [:id, :post_id, :avatar])
end
In views/posts/_form.html.erb
<%= form_for(#post, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<%= f.fields_for :post_attachments do |p| %>
<div class="field">
<%= p.label :avatar %><br>
<%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
To edit an attachment and list of attachment for any post.
In views/posts/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<% #post_attachments.each do |p| %>
<%= image_tag p.avatar_url %>
<%= link_to "Edit Attachment", edit_post_attachment_path(p) %>
<% end %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Back', posts_path %>
Update form to edit an attachment views/post_attachments/_form.html.erb
<%= image_tag #post_attachment.avatar %>
<%= form_for(#post_attachment) do |f| %>
<div class="field">
<%= f.label :avatar %><br>
<%= f.file_field :avatar %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Modify update method in post_attachment_controller.rb
def update
respond_to do |format|
if #post_attachment.update(post_attachment_params)
format.html { redirect_to #post_attachment.post, notice: 'Post attachment was successfully updated.' }
end
end
end
In rails 3 no need to define strong parameters and as you can define attribute_accessible in both the model and accept_nested_attribute to post model because attribute accessible is deprecated in rails 4.
For edit an attachment we cant modify all the attachments at a time. so we will replace attachment one by one, or you can modify as per your rule, Here I just show you how to update any attachment.
If we take a look at CarrierWave's documentation, this is actually very easy now.
https://github.com/carrierwaveuploader/carrierwave/blob/master/README.md#multiple-file-uploads
I will use Product as the model I want to add the pictures, as an example.
Get the master branch Carrierwave and add it to your Gemfile:
gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
Create a column in the intended model to host an array of images:
rails generate migration AddPicturesToProducts pictures:json
Run the migration
bundle exec rake db:migrate
Add pictures to model Product
app/models/product.rb
class Product < ActiveRecord::Base
validates :name, presence: true
mount_uploaders :pictures, PictureUploader
end
Add pictures to strong params in ProductsController
app/controllers/products_controller.rb
def product_params
params.require(:product).permit(:name, pictures: [])
end
Allow your form to accept multiple pictures
app/views/products/new.html.erb
# notice 'html: { multipart: true }'
<%= form_for #product, html: { multipart: true } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
# notice 'multiple: true'
<%= f.label :pictures %>
<%= f.file_field :pictures, multiple: true, accept: "image/jpeg, image/jpg, image/gif, image/png" %>
<%= f.submit "Submit" %>
<% end %>
In your views, you can reference the images parsing the pictures array:
#product.pictures[1].url
If you choose several images from a folder, the order will be the exact order you are taking them from top to bottom.
Some minor additions to the SSR answer:
accepts_nested_attributes_for does not require you to change the parent object's controller. So if to correct
name: "post_attachments[avatar][]"
to
name: "post[post_attachments_attributes][][avatar]"
then all these controller changes like these become redundant:
params[:post_attachments]['avatar'].each do |a|
#post_attachment = #post.post_attachments.create!(:avatar => a)
end
Also you should add PostAttachment.new to the parent object form:
In views/posts/_form.html.erb
<%= f.fields_for :post_attachments, PostAttachment.new do |ff| %>
<div class="field">
<%= ff.label :avatar %><br>
<%= ff.file_field :avatar, :multiple => true, name: "post[post_attachments_attributes][][avatar]" %>
</div>
<% end %>
This would make redundant this change in the parent's controller:
#post_attachment = #post.post_attachments.build
For more info see Rails fields_for form not showing up, nested form
If you use Rails 5, then change Rails.application.config.active_record.belongs_to_required_by_default value from true to false (in config/initializers/new_framework_defaults.rb) due to a bug inside accepts_nested_attributes_for (otherwise accepts_nested_attributes_for won't generally work under Rails 5).
EDIT 1:
To add about destroy:
In models/post.rb
class Post < ApplicationRecord
...
accepts_nested_attributes_for :post_attachments, allow_destroy: true
end
In views/posts/_form.html.erb
<% f.object.post_attachments.each do |post_attachment| %>
<% if post_attachment.id %>
<%
post_attachments_delete_params =
{
post:
{
post_attachments_attributes: { id: post_attachment.id, _destroy: true }
}
}
%>
<%= link_to "Delete", post_path(f.object.id, post_attachments_delete_params), method: :patch, data: { confirm: 'Are you sure?' } %>
<br><br>
<% end %>
<% end %>
This way you simply do not need to have a child object's controller at all! I mean no any PostAttachmentsController is needed anymore. As for parent object's controller (PostController), you also almost don't change it - the only thing you change in there is the list of the whitelisted params (to include the child object-related params) like this:
def post_params
params.require(:post).permit(:title, :text,
post_attachments_attributes: ["avatar", "#original_filename", "#content_type", "#headers", "_destroy", "id"])
end
That's why the accepts_nested_attributes_for is so amazing.
Also I figured out how to update the multiple file upload and I also refactored it a bit. This code is mine but you get the drift.
def create
#motherboard = Motherboard.new(motherboard_params)
if #motherboard.save
save_attachments if params[:motherboard_attachments]
redirect_to #motherboard, notice: 'Motherboard was successfully created.'
else
render :new
end
end
def update
update_attachments if params[:motherboard_attachments]
if #motherboard.update(motherboard_params)
redirect_to #motherboard, notice: 'Motherboard was successfully updated.'
else
render :edit
end
end
private
def save_attachments
params[:motherboard_attachments]['photo'].each do |photo|
#motherboard_attachment = #motherboard.motherboard_attachments.create!(:photo => photo)
end
end
def update_attachments
#motherboard.motherboard_attachments.each(&:destroy) if #motherboard.motherboard_attachments.present?
params[:motherboard_attachments]['photo'].each do |photo|
#motherboard_attachment = #motherboard.motherboard_attachments.create!(:photo => photo)
end
end
Here is my second refactor into the model:
Move private methods to model.
Replace #motherboard with self.
Controller:
def create
#motherboard = Motherboard.new(motherboard_params)
if #motherboard.save
#motherboard.save_attachments(params) if params[:motherboard_attachments]
redirect_to #motherboard, notice: 'Motherboard was successfully created.'
else
render :new
end
end
def update
#motherboard.update_attachments(params) if params[:motherboard_attachments]
if #motherboard.update(motherboard_params)
redirect_to #motherboard, notice: 'Motherboard was successfully updated.'
else
render :edit
end
end
In motherboard model:
def save_attachments(params)
params[:motherboard_attachments]['photo'].each do |photo|
self.motherboard_attachments.create!(:photo => photo)
end
end
def update_attachments(params)
self.motherboard_attachments.each(&:destroy) if self.motherboard_attachments.present?
params[:motherboard_attachments]['photo'].each do |photo|
self.motherboard_attachments.create!(:photo => photo)
end
end
When using the association #post.post_attachments you do not need to set the post_id.