Carrierwave not saving the file on disk - ruby-on-rails

everyone. Please, help me with carrierwave and Rails 4.
Model:
class MediaItem < ActiveRecord::Base
mount_uploader :media_item, MediaItemUploader
end
Schema:
create_table "media_items", force: true do |t|
t.string "file"
t.text "description", default: "", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Uploader:
class MediaItemUploader < CarrierWave::Uploader::Base
def store_dir
"public/"
end
Controller:
class MediaItemsController < ApplicationController
def create
#media_item = MediaItem.new(media_item_params)
respond_to do |format|
if #media_item.save
format.html { redirect_to #media_item, notice: 'Media item was successfully created.' }
else
format.html { render :new }
end
end
end
Form:
= form_for #media_item, :html => {:multipart => true} do |f|
.field
= f.label :description
= f.text_field :description
.field
= f.label :file
= f.file_field :file
.actions
= f.submit 'Save'
The problem is that uploaded file does not appear in specified dir and "media_item.media_item_url" return empty string in a view. However, it save the recore into db:
select file, description from media_items;
#<ActionDispatch::Http::UploadedFile:0xba672374>|wetw

My mistake.
Instead
mount_uploader :media_item, MediaItemUploader
it must be
mount_uploader :file, MediaItemUploader
as "file" is a field name in db, not "media_item"

Related

Rails - Couldn't find Variant without an ID

I am facing this error Couldn't find Variant without an ID
I don't get what is wrong :(
Stock belongs_to :variant
Variant has_many :stocks and belongs_to :product
Product
has_many :variants, inverse_of: :product, dependent: :destroy
has_many :stocks, through: :variants
accepts_nested_attributes_for :variants
stock.html.erb
<%= simple_form_for(Stock.new, url: admin_stocks_path) do |f| %>
<%= f.select :variant_id, options_from_collection_for_select(#product.variants, :id, :size),required: true %>
<%= f.input :quantity, required: true %>
<%= f.submit %>
<% end %>
stocks_controller.rb
class StocksController < ApplicationController
def show
#stock = Stock.find(params[:id])
end
def index
#stocks = Stock.all
#variants = Variant.all
end
def new
#stock = Stock.new
#product = Product.find(params[:product_id])
#variants = #product.variants
end
def create
find_variant
#product = #variant.product
#stock = Stock.new(stock_params)
if #stock.save
redirect_to stock_product_path(#product)
else
redirect_to stock_product_path(#product), alert: "Woops"
end
end
private
def stock_params
params.require(:stock).permit(:id, :quantity, :variant_id )
end
def find_variant
#variant = Variant.find(params[:variant_id])
end
end
I did fake the find_variantmethod with Variant.find(2) and it was working...
Why I can't find the :variant_id?
here is my table stocks from my schema.rb
create_table "stocks", force: :cascade do |t|
t.integer "quantity"
t.bigint "variant_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["variant_id"], name: "index_stocks_on_variant_id"
end
params[:variant_id] will not be the value. You meant stock_params[:variant_id]

Error: has-many relationship in form, duplicate data when update

I am doing an e-commerce.
I have products which have many options of products and at the same time they only have one variant.
I try to make the view to create the product have the option of add a block where appears the fields of the model and the changes of the variant which is associated to it. The problem is, for example, when i create a product with 5 options, when i update it increases to 10, and if i update it again, there will be 20. I can't find the problem why they are duplicating. I leave the codes below.
Schema of Product, option-product and variant
create_table "options_products", force: :cascade do |t|
t.integer "product_id"
t.float "price"
t.integer "stock"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "products", force: :cascade do |t|
t.string "name", default: "", null: false
t.text "description"
t.integer "category_id"
t.integer "vendor_id"
t.string "state", null: false
t.boolean "shippingInside", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "priceComparison"
t.string "image1_file_name"
t.string "image1_content_type"
t.integer "image1_file_size"
t.datetime "image1_updated_at"
t.float "price"
end
create_table "variants", force: :cascade do |t|
t.string "tipoVariant"
t.integer "options_product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "controlStock"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
end
Script for add and remove fields
$(document).on 'ready page:load', ->
$('form').on 'click', '.remove_field', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_field', (event) ->
time = new Date().getTime()
regular_expression = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regular_expression,time))
event.preventDefault()
Product create and update
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product}
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product}
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
Product Params
def product_params
params.require(:product).permit(:name, :description, :state, :shippingInside, :vendor_id, :category_id, :priceComparison, :image1, :price, offer_attributes: [:status], options_products_attributes: [:price, :stock, variant_attributes: [:tipoVariant, :controlStock, :image]])
function in application helper to add association
def link_to_add_association(name, field, association)
new_object = field.object.send(association).klass.new
new_object_id = new_object.object_id
fields = field.fields_for(association, new_object, child_index: new_object_id) do |builder|
new_object.build_variant
render(association.to_s.singularize + '_field', f: builder)
end
link_to(name, '#', class: 'add_field', data: { id: new_object_id, fields: fields.gsub("\n", "") })
end
Product model
class Product < ActiveRecord::Base
#relations
belongs_to :category
belongs_to :vendor
has_one :offer, :dependent => :destroy
has_many :options_products, :dependent => :destroy
#accepts
accepts_nested_attributes_for :offer, allow_destroy: true
accepts_nested_attributes_for :options_products, allow_destroy: true
#validations
validates :name, presence:true
validates :name, uniqueness:true
validates :state, presence:true
validates :category_id, presence:true
validates :vendor_id, presence:true
has_attached_file :image1, styles: {medium: "300x300>", thumb: "150x150#" }
validates_attachment_content_type :image1, content_type: /\Aimage\/.*\z/
end
Option Product Model
class OptionsProduct < ActiveRecord::Base
belongs_to :product
has_one :variant, :dependent => :destroy
accepts_nested_attributes_for :variant, allow_destroy: true
end
Variant model
class Variant < ActiveRecord::Base
belongs_to :options_product
has_attached_file :image,
styles: {medium: "300x300>", thumb: "150x150#" }
validates_attachment_content_type :image,
content_type: /\Aimage\/.*\z/
end
_form of Product
= form_for #product, html: { multipart: true } do |f|
.row
.form-group.col-lg-6
.field
= f.file_field :image1
.row
.form-group.col-lg-6
.field
= f.text_field :name, :placeholder => 'Nombre', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.text_area :description, :placeholder => 'Descripcion', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.number_field :price, :placeholder => 'Precio a mostrar', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.label :Estado
%br/
= f.select :state, options_for_select(['Disponible', 'No disponible'])
.row
.form-group.col-lg-6
.field
= f.label :Envio
%br/
= f.check_box :shippingInside
.row
.form-group.col-lg-6
.field
= f.text_field :priceComparison, :placeholder => 'Precio anterior', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.label :vendor_id
%br/
= f.select :vendor_id, Vendor.all.collect { |vendor| [vendor.name, vendor.id] }
.row
.form-group.col-lg-6
.field
= f.label :category_id
%br/
= f.select :category_id, Category.all.collect { |category| [category.name, category.id] }
= f.fields_for :offer, #product.build_offer do |o|
= o.label :Oferta
%br/
= o.check_box :status
%br/
.row
= f.fields_for :options_products do |op|
= render 'options_product_field', f: op
= link_to_add_association 'Agregar variante', f, :options_products
%br/
.actions
= f.submit "Enviar", :class => 'button btn btn-primary bold'
options_product_field file
%fieldset
.row
.form-group.col-lg-6
.field
= f.text_field :price, :placeholder => 'Precio', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= f.text_field :stock, :placeholder => 'Stock', :class => 'form-control input-border-left'
= f.fields_for :variant do |v|
.row
.form-group.col-lg-6
.field
= v.text_field :tipoVariant, :placeholder => 'Tipo de variante', :class => 'form-control input-border-left'
.row
.form-group.col-lg-6
.field
= v.label :ControlarStock
%br/
= v.check_box :controlStock
.row
.form-group.col-lg-6
.field
= v.label :ImagenDeVariante
%br/
= v.file_field :image
= f.hidden_field :_destroy
= link_to 'Remover variante', '#', class: 'remove_field'
In product_params, you should specify id of options_products_attributes. Without id, attributes will be newly added to product model.
So, try
... options_products_attributes: [ :id, price, :stock, variant_attributes: [ :id, :tipoVariant, ...

Nested forms with join table attributes

I have a new Match form:
EDIT:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.label :player_ids, 'Select players'
= f.collection_select :player_ids, #players, :id, :lastname, {}, { multiple: true }
= f.fields_for :match_edits do |ff|
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
When I choose two players I want set for each one match result like this:
id: 1, match_id: 1, player_id: 1, result: 4
id: 2, match_id: 1, player_id: 2, result: 10
I'm new in rails and I don't know how to fix that
MatchController
class MatchesController < ApplicationController
respond_to :html
def index
#matches = Match.all
end
def show
#match = Match.find(params[:id])
#results = #match.match_edits
end
def new
#match = Match.new
#players = Player.all
2.times {#match.match_edits.build}
end
def create
#match = Match.new(match_params)
respond_to do |format|
if #match.save
format.html { redirect_to #match, notice: 'Match was successfully created.' }
format.json { render :show, status: :created, location: #match }
else
format.html { render :new }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
private
def match_params
params[:match].permit :match_date, player_ids: [], :match_edits_attributes => [:id, :result]
end
end
MatchEdit model
class MatchEdit < ActiveRecord::Base
belongs_to :match
belongs_to :player
end
Match model
class Match < ActiveRecord::Base
has_many :match_edits
has_many :players, through: :match_edits
accepts_nested_attributes_for :match_edits, allow_destroy: true, reject_if: proc { |attrs| attrs['result'].blank? }
end
Schema.rb
ActiveRecord::Schema.define(version: 20150629144534) do
create_table "match_edits", force: :cascade do |t|
t.integer "match_id"
t.integer "player_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "result"
end
add_index "match_edits", ["match_id"], name: "index_match_edits_on_match_id"
add_index "match_edits", ["player_id"], name: "index_match_edits_on_player_id"
create_table "matches", force: :cascade do |t|
t.date "match_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "players", force: :cascade do |t|
t.string "firstname"
t.string "lastname"
t.string "picture"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
You need to update your form to remove redundant code i.e.:
= ff.number_field :result, in: 0..10
Your form will look like:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.label :player_ids, 'Select players'
= f.collection_select :player_ids, #players, :id, :lastname, {}, { multiple: true }
= f.fields_for :match_edits do |ff|
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
Your controller's new method is responsible to to provide multiple fields for result:
class MatchsController << ApplicationContoller
...
def new
...
2.times { #match.match_edits.build }
...
end
...
end
Your model should allow to accept nested attributes as following:
class Match
...
has_many :match_edits
accepts_nested_attributes_for :match_edits, allow_destroy: true,
reject_if: proc { |attrs| attrs['result'].blank? }
...
end
class MatchEdit
...
belongs_to :match
...
end
I found solution. Form should look like this:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.fields_for :match_edits do |ff|
= ff.label :player_id, 'Select player'
= ff.collection_select :player_id, #players, :id, :lastname, {}, { multiple: false }
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
and in matches_controller:
def match_params
params[:match].permit :match_date, :match_edits_attributes => [:id, :result, :player_id]
end

Rails - cant add a photo with another model as an index

I'm trying to make a create product page that would allow users to upload photos. So products has_many photos. I got this set up but when a photo is added it should have product_id in a column but when it saves to my database the product_id column is blank.
product controller
def create
#product = current_user.products.build(params[:product])
#photo = current_user.photos.new(params[:photo])
if #product.valid? && #photo.valid?
#product.save
#photo.save
#photo.product_id = #product.id
render "show", :notice => "Sale created!"
else
render "new", :notice => "Somehting went wrong!"
end
end
new product page(HAML)
%h1
create item
= form_for #product,:url => products_path, :html => { :multipart => true } do |f|
%p
= f.label :name
= f.text_field :name
%p
= f.label :description
= f.text_field :description
%p
= fields_for :photo, :html => {:multipart => true} do |fp|
=fp.file_field :image
%p.button
= f.submit
schema.rb
create_table "products", :force => true do |t|
t.string "name"
t.text "description"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "user_id"
end
create_table "photos", :force => true do |t|
t.integer "product_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
end
This is merely a result of saving the #photo first, and then setting its product_id after saving, which of course never gets updated in the database. Just reverse the operations.
if #product.valid? && #photo.valid?
# Recommended to test for success saving the product
if #product.save
# Set the product_id before saving
#photo.product_id = #product.id
# Then save the photo
#photo.save
render "show", :notice => "Sale created!"
end
else
render "new", :notice => "Somehting went wrong!"
end

Why does my form_for area#new have a button that says "Update Area", not "Create Area"

I have an area model that belongs to a report model. I have built a form partial using SimpleForm. When I go to new_report_area_path(#report), I get a New Area form that works just fine. Enter the details and hit submit and it creates an area and takes you to area#show. But the button on the new area form says "Update Area" not "Create Area". Why?
config/routes.rb:
Testivate::Application.routes.draw do
resources :reports do
resources :areas
end
end
db/schema.rb:
ActiveRecord::Schema.define(:version => 20121205045544) do
create_table "areas", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "report_id"
end
create_table "reports", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
app/models/area.rb:
class Area < ActiveRecord::Base
attr_accessible :name
has_many :heuristics
belongs_to :report
end
app/models/report.rb:
class Report < ActiveRecord::Base
attr_accessible :name
has_many :heuristics
has_many :areas
end
app/controllers/areas_controller.rb:
class AreasController < ApplicationController
filter_resource_access
def new
#report = Report.find(params[:report_id])
#area = #report.areas.create
respond_to do |format|
format.html # new.html.erb
end
end
def create
#report = Report.find(params[:report_id])
#area = #report.areas.create(params[:area])
respond_to do |format|
if #area.save
format.html { redirect_to report_area_path(#report, #area), notice: 'Area was successfully created.' }
else
format.html { render action: "new" }
end
end
end
end
app/views/areas/news.html.haml:
%h1 New Area
= render 'form'
app/views/areas/_form.html.haml:
= simple_form_for [#report, #area] do |f|
= f.error_notification
= f.input :name
= f.button :submit
Instead of creating an area you should building it as it's a new action:
def new
#report = Report.find(params[:report_id])
#area = #report.areas.build # build instead of create
respond_to do |format|
format.html # new.html.erb
end
end

Resources