carrierwave multiple uploader for single model - ruby-on-rails

I have created a gallery module with the help of this tutorial https://kolosek.com/carrierwave-upload-multiple-images/ and now I want to add one more image attribute has a master image to the same model. So I thot of creating one more uploader but I'm bit confused about how to call that the controller can anybody help me out?
My code looks like this:
MOdel Code:
class Image < ApplicationRecord
belongs_to :gallery
mount_uploader :image, ImageUploader
mount_uploader :avatar, AvatarUploader
end
class Gallery < ApplicationRecord
has_many :images
accepts_nested_attributes_for :images
end
gallery_controller.rb
class GalleriesController < AdminController
def index
#galleries = Gallery.all
end
def show
#images = #gallery.images.all
end
def new
#gallery = Gallery.new
#image = #gallery.images.build
end
def create
#gallery = Gallery.new(gallery_params)
respond_to do |format|
if #gallery.save
params[:images]['image'].each do |a|
#images = #gallery.images.create!(:image => a, :gallery_id => #gallery.id)
end
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render :show, status: :created, location: #gallery }
else
format.html { render :new }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
def gallery_params
params.require(:gallery).permit(:title, :details, :status, images_attributes:[:id, :gallery_id, :image, :avatar])
end
Image Controller code:
class ImagesController < AdminController
def image_params
params.require(:image).permit(:gallery_id, :slideshow_id,:image, :avatar)
end
end
form.html.erb
<%= form.fields_for :images do |p| %>
<div class="field">
<%= form.label :master_image, class: "col-2 col-form-label" %>
<%= form.file_field :avatar, :multiple => true, name: "images[avatar][]" %>
</div>
<% end %>
<%= form.fields_for :images do |p| %>
<div class="field">
<%= form.label :image, class: "col-2 col-form-label" %>
<%= form.file_field :image, :multiple => true, name: "images[image][]" %>
</div>
<% end %>
I am confused in modifing the gallery controller create part.

You can add the images creation 2 times in the create method if you have 2 uploaders
params[:images]['image'].each do |a|
#gallery.images.create!(:image => a, :gallery_id => #gallery.id)
end
params[:images]['avatar'].each do |a|
#gallery.images.create!(:avatar => a, :gallery_id => #gallery.id)
end
but i think it's a little bit verbose and it not really prevents if you don't want to upload any image in your form (return params[:images]['...'] nil)
Btw your form is not correct for the form.fields_for. It's :
<%= form.fields_for :images_attributes do |p| %>
<div class="field">
<%= p.label :master_image, class: "col-2 col-form-label" %>
<%= p.file_field :avatar, :multiple => true, name: "images[avatar][]" %>
</div>
<% end %>
<%= form.fields_for :images_attributes do |p| %>
<div class="field">
<%= p.label :image, class: "col-2 col-form-label" %>
<%= p.file_field :image, :multiple => true, name: "images[image][]" %>
</div>
<% end %>

Related

collection_select not inserting value from other model

I have two models, Roaster and Roast
I want to have the user select the value of :roaster in the new roast form, from the Roaster model. I am using a collection_select which displays the list of roasters in the dropdown ok, but it doesn't insert the value into the table. From the console, it actually looks like it's trying to pass the roaster_id
"roast"=>{"roaster_id"=>"1", "name"=>"Rugby", "beans"=>"", "countries_attributes"=>{"0"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}, "1"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}, "2"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}}, "bestfor"=>"", "roast"=>"", "tastingnotes"=>""}, "commit"=>"Create Roast"}
My select:
<%= form.collection_select(:roaster_id, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
I've tried
<%= form.collection_select(:roaster_name, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
but this gives and undefined method error.
My roast_params
params.require(:roast).permit(:roaster, :roaster_id, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:country_id, :country_name, regions_attributes: [:id, :region_name]])
Adding in :roaster_name doesn't solve either.
As requested full form:
<%= form_with(model: roast, local: true, multipart: true) do |form| %>
<% if roast.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger" role="alert">
<h2><%= pluralize(roast.errors.count, "error") %> prohibited this roast from being saved:</h2>
<ul>
<% roast.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<form>
<div class="row">
<div class="col-6">
<div class="field form-group">
<%= form.label :roaster, class: 'control-label' %>
<%= form.collection_select(:roaster_id, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
</div>
</div>
<div class="col-6">
<div class="form-group">
<%= form.label :name, class: 'control-label' %>
<%= form.text_field :name, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= form.label :beans, "Blend", class: 'control-label' %><br />
<%= form.select :beans, [ 'Single Origin','Two Country Blend', 'Three Country Blend' ], :prompt => 'Select One', id: :roast_beans, class: "form-control" %>
</div>
<div class="row">
<%= form.fields_for :countries do |countries_form| %>
<div class="col-6">
<div class="form-group">
<%= countries_form.label :country %>
<%= countries_form.text_field :country_name, class: "form-control" %>
</div>
</div>
<div class="col-6">
<!-- note the appending of `countries_` to form.fields to allow for deeper nested to work-->
<%= countries_form.fields_for :regions do |regions_form| %>
<%= regions_form.label :region %>
<%= regions_form.text_field :region_name, class: "form-control" %>
<% end %>
<br />
</div>
<% end %>
</div>
<div class="form-group">
<%= form.label :bestfor, "Style", class: 'control-label' %><br />
<%= form.select :bestfor, [ 'Espresso','Filter' ], :prompt => 'Select One', id: :roast_bestfor, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :roast, "Strength", class: 'control-label' %><br />
<%= form.select :roast, [ 'Light','Medium','Dark' ], :prompt => 'Select One', id: :roast_roast, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :tastingnotes, "Tasting Notes (separate with commas, e.g chocolate, citrus)", class: 'control-label' %><br />
<%= form.text_area :tastingnotes, id: :roast_tastingnotes, class: "form-control" %>
</div>
<br />
<div class="form-group">
<%= form.label :avatar, "Upload image...", class: 'control-label' %>
<%= form.file_field :avatar %>
</div>
<div class="actions">
<%= form.submit class: "btn btn-success" %> <%= link_to "Cancel", "/roasts", class: "btn btn-secondary"%>
</div>
<% end %>
</form>
roast_controller.rb
class RoastsController < ApplicationController
before_action :set_roast, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:create, :edit, :update, :destroy]
before_action :set_search
# GET /roasts
# GET /roasts.json
def index
#q = Roast.ransack(params[:q])
#roastsalpha = #q.result.order(:name)
#roastcount = Roast.count(:country)
#roasts = Roast.order(:name).count
#countroastschart = Roast.order("roaster DESC").all
end
# GET /roasts/1
# GET /roasts/1.json
def show
#roast = Roast.friendly.find(params[:id])
#commentable = #roast
#comments = #commentable.comments
#comment = Comment.new
#sameroaster = Roast.where(roaster: #roast.roaster)
#samecountry = Roast.where(country: #roast.country)
#roastcount = Roast.where(roaster: #roast.roaster)
end
# GET /roasts/new
def new
#roast = Roast.new
3.times {#roast.countries.build.regions.build}
end
# GET /roasts/1/edit
def edit
3.times {#roast.countries.build.regions.build}
end
# POST /roasts
# POST /roasts.json
def create
#roast = Roast.new(roast_params)
respond_to do |format|
if #roast.save
format.html { redirect_to #roast, notice: 'Roast was successfully created.' }
format.json { render :show, status: :created, location: #roast }
else
format.html { render :new }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /roasts/1
# PATCH/PUT /roasts/1.json
def update
respond_to do |format|
if #roast.update(roast_params)
format.html { redirect_to #roast, notice: 'Roast was successfully updated.' }
format.json { render :show, status: :ok, location: #roast }
else
format.html { render :edit }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
# DELETE /roasts/1
# DELETE /roasts/1.json
def destroy
#roast.destroy
respond_to do |format|
format.html { redirect_to roasts_url, notice: 'Roast was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_roast
#roast = Roast.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def roast_params
params.require(:roast).permit(:roaster, :roaster_id, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:country_id, :country_name, regions_attributes: [:id, :region_name]])
end
end
I think you are doing many wrong things. By looking your other questions I saw your models. I put some important things:
class Roast < ApplicationRecord
has_many :countries
accepts_nested_attributes_for :countries
end
class Country < ApplicationRecord
has_many :regions, inverse_of: :country
accepts_nested_attributes_for :regions
belongs_to :roast
end
class Region < ApplicationRecord
belongs_to :country, inverse_of: :regions
end
In these models I didn't see the Roaster. I assume a Roast belongs_to :roaster.
So: your Roast has many countries and each country has many regions. But you are passing country names and region names in your view to the create controller. You need to pass the ids, so that you save references to these models.
You have many unnecessary field in params, and some missing ones. This is how it should be:
def roaster_params
params.require(:roast).permit(:roaster_id, :name, :bestfor, :beans, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:id, regions_attributes: [:id]])
end
You don't need roast, roaster, country_name, region_name. You need the id of the country (and not the country_id), and the id of the region (and not the region_id)
In your form you should ask for country and region ids:
<%= countries_form.collection_select(:id, Country.all, :id, :name, :prompt => 'Select Country') %>
<%= regions_form.collection_select(:id, Region.all, :id, :name, :prompt => 'Select Region') %>
In fact this is more difficult, because a region belongs to a country, but here you are showing all regions. You should only show regions for the selected country (which is dynamic).

Relationship models in rails

Im associating models and im stuck i have my Restaurant controller
before_action :set_restaurant, only: [:show, :edit, :update, :destroy]
def new
#restaurant = Restaurant.new
#restaurant.build_chef
end
def create
#restaurant = Restaurant.new(restaurant_params)
respond_to do |format|
if #restaurant.save
format.html { redirect_to #restaurant, notice: 'Restaurant was successfully created.' }
format.json { render action: 'show', status: :created, location: #restaurant }
else
format.html { render action: 'new' }
format.json { render json: #restaurant.errors, status: :unprocessable_entity }
end
end
end
def set_restaurant
#restaurant = Restaurant.find(params[:id])
end
def restaurant_params
params.require(:restaurant).permit(:avatar, :name, :description,
chef_attributes: [ :avatar, :name ]
)
end
And this is my _form that im rendering in create.html.erb
<%= form_for(#restaurant, multipart: true) do |f| %>
<div class="field">
<%= f.label :restaurant_name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label :image %>
<%= f.file_field :avatar %>
</div>
<%= f.fields_for :chef do |f| %>
<div class="field">
<%= f.label :chef_name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :image %>
<%= f.file_field :avatar %>
</div>
<% end %>
<div class="actions">
<%= f.submit 'Add new restaurant' %>
<%= link_to 'Nevermind', restaurants_path, class: 'button' %>
</div>
<% end %>
then this is my Chef model
class Chef < ApplicationRecord
belongs_to :restaurant
validates :name, presence: true
validates :avatar,
attachment_content_type: { content_type: /\Aimage\/.*\Z/ },
attachment_size: { less_than: 5.megabytes }
has_attached_file :avatar, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>'
}
end
and this is my Restaurant model
class Restaurant < ApplicationRecord
has_one :chef
accepts_nested_attributes_for :chef
validates :name, presence: true
validates :avatar,
attachment_content_type: { content_type: /\Aimage\/.*\Z/ },
attachment_size: { less_than: 5.megabytes }
has_attached_file :avatar, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>'
}
end
My problem is that when i save a new restauran, nothing happens just reloads and stay in the form, i dont know whats happening im stuck here, probably its just a dumb thing to do but im learning, thanks for the support.
It looks like your problem is related to this question. In Rails 5, there is an implicit validation on belongs_to associations, so you need to add required: false in order to build it from nested attributes.
class Chef < ApplicationRecord
belongs_to :restaurant, required: false
...
end

Nested forms not storing data into database in rails 4

I have a vendor model, a product model, and a vendor_product model. In my vendors form, I have used nested form to create vendor_products with attributes as vendor_id, product_id and copies.On creating a new vendor, it also creates a vendor_product. But for some reason, it does not stores the vendor_id and product_id in vendor_products table but only stores the copies
My associations are as follows
A vendor ->
has_many :vendor_products
has_many :products, through: :vendor_products
A product ->
has_many :vendor_products
has_many :vendors, through: :vendor_products
A vendor_product
belongs_to :vendor
belongs_to :product
Vendor.rb
class Vendor < ActiveRecord::Base
has_many :vendor_products
has_many :products, through: :vendor_products
accepts_nested_attributes_for :vendor_products, :products,
:allow_destroy => true
end
My vendors/_form.html.erb
<%= form_for(#vendor) do |f| %>
<% if #vendor.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#vendor.errors.count, "error") %> prohibited this
vendor from being saved:</h2>
<ul>
<% #vendor.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
:
:
:
<%= f.fields_for :vendor_products do |vproducts| %>
<div class="field">
<%= vproducts.label :product %><br>
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>
<div class="field">
<%= vproducts.label :copies %><br>
<%= vproducts.number_field :copies %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My vendors_controller.rb
class VendorsController < ApplicationController
before_action :set_vendor, only: [:show, :edit, :update, :destroy]
respond_to :json
def index
#vendors = Vendor.all.limit(20)
end
def show
end
def new
#vendor = Vendor.new
#vendor.products.build
#vendor.vendor_products.build
end
def edit
end
def create
#vendor = Vendor.new(vendor_params)
respond_to do |format|
if #vendor.save
format.html { redirect_to #vendor, notice: 'Vendor was successfully
created.' }
format.json { render :show, status: :created, location: #vendor }
else
format.html { render :new }
format.json { render json: #vendor.errors, status:
:unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #vendor.update(vendor_params)
format.html { redirect_to #vendor, notice: 'Vendor was successfully
updated.' }
format.json { render :show, status: :ok, location: #vendor }
else
format.html { render :edit }
format.json { render json: #vendor.errors, status:
:unprocessable_entity }
end
end
end
private
def set_vendor
#vendor = Vendor.find(params[:id])
end
def vendor_params
params.require(:vendor).permit(:name, :email, :phone_no, :addressline1,
:addressline2, :landmark,
:city, :state, :country, :pincode, :latitude, :longitude, :status,
product_attributes: [:product_id, :product_name, :price ],
vendor_products: [:vendor_product_id, :vendor_id, :product_id,
:copies])
end
end
Now a vendor and VendorProduct is created but my vendor_product looks lik this
{"id":3,
"vendor_id":null,
"product_id":null,
"copies":4,
}
Can any one point out how to fix this. What am I doing wrong. Please bear in mind that I am a rails newbie.
change once vendor_products to vendor_products_attributes and look
In your view
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
replace with
<%= vproducts.select :product_id, options_from_collection_for_select(Product.all, "id", "name"), prompt: "Select something" %>
Change this line:
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>
to
<%= collection_select(:product, :product_id, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>

Rails 4, Cocoon, ERB Template, how to make selected an options_from_collection_for_select in Edit Action?

This is my first time with Cocoon, and maybe this is a really dumb question, but I already spend many time looking how to do this with ERB template and avoid using simple_form or other helper.
Take a look of my models:
models/loot.rb
class Lot < ActiveRecord::Base
has_many :colindanciums, dependent: :destroy
has_many :cardinal_points, through: :colindanciums
accepts_nested_attributes_for :colindanciums, allow_destroy: true
end
models/colindancium.rb
class Colindancium < ActiveRecord::Base
belongs_to :cardinal_poing
belongs_to :lot
end
models/cardinal_point.rb
class CardinalPoint < ActiveRecord::Base
has_many :colindanciums
has_many :lots, through: :colindanciums
end
The form:
views/lots/_form.html.erb
<%= form_for(#lot, remote: true) do |f| %>
<%= render 'shared/error_messages', object: #lot %>
...
...
...
<fieldset id="colindancium-orientation">
<ol>
<%= f.fields_for :colindanciums do |colindancium| %>
<%= render 'colindancium_fields', f: colindancium %>
<% end %>
</ol>
<%= link_to_add_association 'Nueva Colindancia', f, :colindanciums, 'data-association-insertion-node' => "#colindancium-orientation ol", 'data-association-insertion-method' => "append" %>
</fieldset>
...
...
...
<% end %>
The partial:
views/lots/_colindancium_fields.html.erb
<li class="control-group nested-fields">
<div class="controls">
<%= f.label :description, "Descripcion:" %>
<%= f.text_field :description %>
<%= f.label :linear_meters, "Metros Lineales:" %>
<%= f.text_field :linear_meters %>
<%= f.label :cardinal_point_id, "Orientacion:" %>
<%= f.select :cardinal_point_id,
options_from_collection_for_select(CardinalPoint.all, :id, :name), { }, { :class => "form-control", :prompt => "Seleccione un Punto Cardinal" } %>
<%= link_to_remove_association "Eliminar", f %>
</div>
</li>
Everything works great when I insert new fields, it saves it in DB, it Update it in DB, my problem is in the options_from_collection_for_select when I open the form in Edit Action, the fourth parameter of this helper is the selected value... I can't find the way to make selected the value that is stored in my db, it always show the 1 index... I can't access the #... object from the _form, the other fields (:description, :linear_meters) are working quite good my problem is in the f.select, I don't know how to do it.
EDIT My controller:
# GET /lots/new
def new
#lot = Lot.new
#lot.colindanciums.build
authorize #lot
end
# PATCH/PUT /lots/1
# PATCH/PUT /lots/1.json
def update
authorize #lot
respond_to do |format|
if #lot.update(lot_params)
format.html { redirect_to #lot, notice: 'Lot was successfully updated.' }
format.json { render :show, status: :ok, location: #lot }
format.js
else
format.html { render :edit }
format.json { render json: #lot.errors, status: :unprocessable_entity }
format.js { render json: #lot.errors, status: :unprocessable_entity }
end
end
end
I change my logic in the select, i made it works in this way:
<div class="form-group">
<%= f.label :cardinal_point_id, "Orientacion:", :class => "control-label" %>
<%= f.select :cardinal_point_id , CardinalPoint.all.collect {|p| [ p.name, p.id ] }, { :include_blank => 'Seleccione un Punto Cardinal'}, :class => "form-control" %>
</div>
I post my answer in case anybody have the same issue.
You forgot to put the parenthesis correctly
<%= f.select (:cardinal_point_id,
options_from_collection_for_select(CardinalPoint.all, :id, :name), { }, { :class => "form-control", :prompt => "Seleccione un Punto Cardinal" }) %>

Can't mass-assign protected attributes when using accepts_nested_attributes_for and polymorphic

I've read through lots of posts here and still cant figure this one out.
I have a forum_post model and a links model. I want to nest the links form with the forum_post form but keep getting a Can't mass-assign protected attributes: links.
ForumPost Model
class ForumPost < ActiveRecord::Base
attr_accessible :content, :links_attributes
has_many :links, :as => :linkable, :dependent => :destroy
accepts_nested_attributes_for :links, :allow_destroy => true
end
Links Model
class Link < ActiveRecord::Base
attr_accessible :description, :image_url, :link_url, :linkable_id, :linkable_type, :title
belongs_to :linkable, :polymorphic => true
end
Forum_post View
<%= form_for(#forum_post) do |f| %>
<% if #forum_post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#forum_post.errors.count, "error") %> prohibited this forum_post from being saved:</h2>
<ul>
<% #forum_post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content, :rows => 5 %>
</div>
<%= f.fields_for :link do |link| %>
<%= render :partial => 'links/link', :locals => { :f => link} %>
<% end%>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Link View Partial
<div class="field">
<%= f.label :link_url %><br />
<%= f.text_field :link_url, :id => "url_field" %>
</div>
<div id="link_preview">
</div>
ForumPosts Controller
class ForumPostsController < ApplicationController
def new
#forum_post = ForumPost.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #forum_post }
end
def create
#forum_post = ForumPost.new(params[:forum_post])
respond_to do |format|
if #forum_post.save
format.html { redirect_to #forum_post, notice: 'Forum post was successfully created.' }
format.json { render json: #forum_post, status: :created, location: #forum_post }
else
format.html { render action: "new" }
format.json { render json: #forum_post.errors, status: :unprocessable_entity }
end
end
end
Links Controller
class LinksController < ApplicationController
def find_linkable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def index
#linkable = find_linkable
#links = #linkable.links
end
def create
#linkable = find_linkable
#link = #linkable.links.build(params[:link])
if #link.save
flash[:notice] = "Successfully saved link."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
Well, according to your question the protected attributes that you can't mass-assign is :links.
Not sure how that happened, but have you tried attr_accessible :links?
As for the security implications, it is the reason github got hacked once https://gist.github.com/1978249, and I would highly discourage setting whitelist_attributes to false.

Resources