Im creating an online retail store. That has Items that are belongs_to a category. When i try to submit the item with a category selected it wont save
I tried for many hours to fix but can't figure it out. Anyone see what the problem is?
Error is
ActiveRecord::AssociationTypeMismatch in ItemsController#create
Category(#70298375791060) expected, got String(#70298372605800)
def create
#item = current_user.items.build(item_params)
if #item.save
redirect_to #item
flash[:success] = "You have created a new item"
Items Form
<h1>Create New item</h1>
<div class="container">
<div class=“row”>
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-primary">
<div class="panel-body">
<%= simple_form_for #item, html: { multipart: true } do |f| %>
<%= f.input :image%>
<%= f.collection_select :category, Category.order(:name), :id, :name, include_blank: true, :prompt => "Select One Category" %>
<%= f.input :title%>
<%= f.input :price %>
<%= f.input :description %>
<%= f.button :submit, "Create new item", class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
</div>
</div>
Items Controller
class ItemsController < ApplicationController
before_action :correct_user_edit, only: [:edit, :update, :destroy]
def index
#item = #user.items.paginate(page: params[:page])
end
def new
#item = Item.new
end
def home
#items = Item.paginate(page: params[:page])
end
def edit
#item = Item.find(params[:id])
#user = User.find(params[:id])
end
def show
#item = Item.find(params[:id])
end
def update
#item = Item.find(params[:id])
if #item.update(item_params)
redirect_to #item
flash[:success] = 'Item was successfully updated.'
else
render "edit"
end
end
def create
#item = current_user.items.build(item_params)
if #item.save
redirect_to #item
flash[:success] = "You have created a new item"
else
flash[:danger] = "Your item didn't save"
render "new"
end
end
def destroy
Item.find(params[:id]).destroy
flash[:success] = "Item deleted"
redirect_to users_url
end
private
def item_params
params.require(:item).permit(:title, :category, :price, :description, :image)
end
#Check to see if user can edit item.
def correct_user_edit
if #item = current_user.items.find_by(id: params[:id])
else
flash[:danger] = "You can't edit that item"
redirect_to root_url if #item.nil?
end
end
end
Item model
class Item < ActiveRecord::Base
belongs_to :user
belongs_to :category
validates :category, presence: true
validates :title, presence: true, length: { maximum: 30 }
validates :price, presence: true
validates :description, presence: true, length: { maximum: 2000 }
validates :user_id, presence: true
has_attached_file :image, styles: { large: "600x600", medium: "250x250", thumb:"100x100#"}
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
Category Model
class Category < ActiveRecord::Base
has_ancestry
has_many :items
end
ActiveRecord::AssociationTypeMismatch in ItemsController#create
Category(#70298375791060) expected, got String(#70298372605800)
This line
<%= f.collection_select :category, Category.order(:name), :id, :name, include_blank: true, :prompt => "Select One Category" %>
should be
<%= f.collection_select :category_id, Category.order(:name), :id, :name, include_blank: true, :prompt => "Select One Category" %>
And also change in the the item_params as well
def item_params
params.require(:item).permit(:title, :category_id, :price, :description, :image)
end
undefined method `category_id' for Item:0x007fdf43bebed0
You should have category_id column in items table. Run the following command which creates a migration file for adding category_id to items.
rails g migration add_category_id_to_items category_id:integer
and run rake db:migrate
And I suggest you to read these Guides before going further.
Instead of category, you should pass category_id param.
Related
I'm trying to set up a nested form in rails and both the parent and child objects in the form need to have an "Account ID" so that they are both scoped to the current user's account, but I can't figure out how to pass the current user's Account ID for the child object through the nested form. I keep getting a validation error of "Account id must be present" for the nested object.
The parent form is "Product" and I'm trying to nest "Options" into the Product.new form.
I'm trying to do something like this:
#product.options.account_id = current_user.account.id
But it's not working.
Here is the Product model:
class Product < ApplicationRecord
belongs_to :account
has_many :options, dependent: :destroy
accepts_nested_attributes_for :options, allow_destroy: true
validates :account_id, presence: true
validates :name, presence: true, length: { maximum: 120 }
end
And options model:
class Option < ApplicationRecord
belongs_to :account
belongs_to :product
has_many :option_values, dependent: :destroy
validates :account_id, presence: true
validates :name, presence: true,
length: { maximum: 60 }
end
Here's how I'm nesting "Options" into the Product form:
<%= form.fields_for :options do |builder| %>
<fieldset class='form-group'>
<%= builder.label :name, 'Add option(s)' %>
<%= builder.text_field :name %>
<small id="optionHelp" class="form-text text-muted">
(e.g. "Sizes" or "Color")
</small>
</fieldset>
<% end %>
And here is my ProductsController:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
before_action :restrict_access
def index
#products = Product.where(:account_id => current_user.account.id).all
end
def show
end
def new
#product = Product.new
#product.options.build
end
def edit
end
def create
#account = current_user.account
#product = #account.products.build(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
end
end
private
def set_product
if Product.find(params[:id]).account_id == current_user.account.id
#product = Product.find(params[:id])
else
redirect_to dashboard_path
end
end
def restrict_access
if index
authorize #products
else
authorize #product
end
end
def product_params
params.require(:product).permit(:account_id, :name,
options_attributes: [:id, :account_id, :name ])
end
end
What is the correct way to do this?
Alternatively, you can pass a hidden_field with form and nested_form as given below: -
<%= form_for #product do |form|%>
<%= form.fields_for :options do |builder| %>
<fieldset class='form-group'>
<%= builder.label :name, 'Add option(s)' %>
<%= builder.text_field :name %>
<small id="optionHelp" class="form-text text-muted">
(e.g. "Sizes" or "Color")
</small>
<%=builder.hidden_field :account_id, value: current_user.account.id%>
</fieldset>
<% end %>
<%= form.hidden_field :account_id, value: current_user.account.id%>
<%end%>
Other than this you can set account_id at the controller
def new
##product = Product.new
#product = current_user.account.products.new
#product.options.build(account_id: current_user.account.id)
end
The easiest option would be to add a hidden field into both forms:
https://apidock.com/rails/ActionView/Helpers/FormHelper/hidden_field
So in your case, for the options form, something like:
<%= form.fields_for :options do |builder| %>
<fieldset class='form-group'>
<%= builder.label :name, 'Add option(s)' %>
<%= builder.hidden_field :account_id, value: current_account.id %>
<%= builder.text_field :name %>
<small id="optionHelp" class="form-text text-muted">
(e.g. "Sizes" or "Color")
</small>
</fieldset>
<% end %>
This will give you access to the [:option][:account_id] param, which will match the current user's.
Error Message
I'm trying to insert a number value through 'number_field_tag' but keep getting the same error message. Does anyone know what is causing this?
new.html.erb
<% provide(:title, 'Products') %>
<h1>Products</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= render 'shared/error_messages_p' %>
<%= form_for([:admin, #product], :html => {multipart:true}) do |f| %>
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
<%= f.label :description %>
<%= f.text_area :description, :rows => 6, class: 'form-control' %>
<%= f.file_field :image %>
<%= number_field_tag :stock_quantity, 1, min: 1, class: "form-control" %>
<%= f.label :price %>
<%= f.text_field :price, class: 'form-control' %>
<%= f.submit "Create product", class: "btn btn-primary" %>
<% end %>
</div>
</div>
products_controller.rb
class Admin::ProductsController < Admin::BaseController
before_action :set_product, only: [:show, :edit, :update, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_product
def index
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def new
#product = Product.new
end
def edit
end
def create
#product = Product.new(product_params)
if #product.save
redirect_to [:admin, #product], notice: 'Product was successfully created.'
else
render :new
end
end
def update
if #product.update(product_params)
redirect_to #product, notice: 'Product was successfully updated.'
else
render :edit
end
end
def destroy
#product.destroy
redirect_to products_url, notice: 'Product was successfully destroyed.'
end
private
def set_product
#product = Product.find(params[:id])
end
def product_params
params.require(:product).permit(:title, :description, :image, :price, :stock_quantity)
end
def invalid_product
logger.error "Attempt to access invalid cart #{params[:id]}"
redirect_to products_url, notice: 'Invalid product'
end
end
product model
class Product < ActiveRecord::Base
attr_accessor :image
validates :title, :description, :image, presence: true
validates :price, numericality: {greater_than_or_equal_to: 0.01}
validates :title, uniqueness: true
validates :stock_quantity, numericality: { only_integer: true}
validates :image, allow_blank: true, format: {with: %r{\.(gif|jpg|png)\Z}i, message: 'must be a URL for GIF, JPG or PNG image.'}
mount_uploader :image, ImageUploader
solved the issue by replacing the quantity submit code with this:
<%= f.label :stock_quantity %>
<%= f.number_field :stock_quantity, min: 1, class: 'form-control' %>
can anyone enlighten me why 'number_field_tag' not working?
this solve it,
<%= number_field_tag 'product[stockquantity]', min: 1, class:"form-control" %>
thanks to previous commenters highlighting the html wordings.
I am trying to validate presence of :picture on a user_items model in a nested form.
item.rb
validates :name, presence: true, uniqueness: true
validates :description, presence: true
has_many :user_items, inverse_of: :item
has_many :users, -> { uniq }, through: :user_items
belongs_to :user
accepts_nested_attributes_for :user_items
validates_associated :user_items
user_item.rb
belongs_to :user
belongs_to :item, inverse_of: :user_items
mount_uploader :picture, PictureUploader
validates :picture, presence: true
validate :picture_size
items_controller.rb
def new
#item = Item.new
#item.user_items.build
end
def create
#item = item.new item_params
if #item.save
redirect_to items_path, notice: "Thank you for your item request!"
else
render :new
end
end
private
def item_params
params.require(:item).permit(:name, :description, :tag_list, user_items_attributes: [:picture]).merge(created_by: current_user.id)
end
new.html.erb
<%= simple_form_for #item, html: { class: "create-item-form" } do |item_builder| %>
<div class="well">
<%= item_builder.input :name, required: false, error: false, label: "Item name" %>
<%= item_builder.input :description, as: :text, required: false, error: false, label: "Description of item" %>
<%= item_builder.input :tag_list, required: false, label: "Tags (these will help users find your item)" %>
<%= item_builder.simple_fields_for :user_items do |user_item_builder| %>
<%= user_item_builder.input :picture, as: :file, required: false, label: "Picture of you with this item" %>
<% end %>
</div>
<div class="clearfix">
<%= item_builder.submit 'Submit new item request', class: "btn btn-primary pull-right inherit-width" %>
</div>
<% end %>
This is not working. I submit a blank form and only the name and description validations trigger.
Update:
Oddly enough it seems the validation will work if I add a hidden_field to my form for the user_id:
<%= user_item_builder.input :user_id, as: :hidden, input_html: { value: current_user.id } %>
What is going on here?
Update2:
The validation also seems to work if I change the create action:
def create
#item = item.new item_params
#item.user_items.build
if #item.save
redirect_to items_path, notice: "Thank you for your item request!"
else
render :new
end
end
Why do I need to add #item.user_items.build? What's the correct way to do this?
I have models for Venues and Photos:
class Venue < ActiveRecord::Base
has_and_belongs_to_many :photos
validates :name, presence: true
validates :permalink, presence: true, uniqueness: true
def to_param
permalink
end
end
class Photo < ActiveRecord::Base
mount_uploader :image, PhotoUploader
has_and_belongs_to_many :venues
end
I'm using simple_form to make the following form:
<div class="content-box">
<%= simple_form_for [:admin, #venue] do |f| %>
<%= f.input :name %>
<%= f.input :permalink %>
<%= f.input :description, input_html: { cols: 100, rows: 6 }%>
<%= f.input :address %>
<%= f.input :city %>
<%= f.input :phone %>
<%= f.association :photos %>
<%= f.button :submit, class: 'small radius' %>
<% end %>
</div>
And here is my controller for the Edit and Update methods:
class Admin::VenuesController < ApplicationController
def edit
#venue = Venue.find_by_permalink!(params[:id])
#photos = #venue.photos.all
end
def update
#venue = Venue.find_by_permalink!(params[:id])
if #venue.update_attributes(venue_params)
redirect_to edit_admin_venue_path(#venue), notice: 'Venue was successfully updated.'
else
render action: "edit"
end
end
private
def venue_params
params.require(:venue).permit(:name, :permalink, :description, :address, :city, :phone, :photo_ids)
end
end
The problem is that when I update using the form, all of the attributes for the venue model update fine, but the photo_ids are not updated or stored. I'm guessing it's something simple, but I'm not sure what it is. I'm using Rails 4, btw.
You need to permit photo_ids as an Array because photo_ids is passed as an Array upon form submission. Currently, photo_ids are not getting updated as you didn't permit them as an Array.
Update the venue_params as below:
def venue_params
params.require(:venue).permit(:name, :permalink, :description, :address, :city, :phone, :photo_ids => [])
end
Notice: :photo_ids => [] and NOT :photo_ids
I have four controllers - users, categories, stories and comments. My problem is with comments. When I submit a comment #comment.save is false and I can't understand where is the problem. My table in DB for Comment has content, user_id, story_id. Here is part of my code:
def new
#comment = Comment.new
#story = Story.find(params[:story_id])
end
def create
#story = Story.find(params[:story_id])
if current_user
#comment = current_user.comments.create(params[:comment])
end
if #comment.save
flash[:success] = "Successfull added comment"
redirect_to story_path(#story)
else
render 'new'
end
end
show.html.erb for StoriesController:
<b><%= #story.title %></b> <br/><br/>
<%= #story.content %> <br/><br/>
<% #story.comments.each do |comment| %>
<b>Comment:</b>
<%= comment.content %>
<% end %>
<%= form_for([#story, #story.comments.build]) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit "Add" %>
</div>
<% end %>
comment.rb:
class Comment < ActiveRecord::Base
attr_accessible :content, :story_id, :user_id
belongs_to :story
belongs_to :user
validates :content, :presence => true
validates :story_id, :presence => true
validates :user_id, :presence => true
default_scope :order => 'comments.created_at DESC'
end
story.rb
class Story < ActiveRecord::Base
attr_accessible :title, :content, :category_id
belongs_to :user
belongs_to :category
has_many :comments
validates :title, :presence => true
validates :content, :presence => true
validates :user_id, :presence => true
default_scope :order => 'stories.created_at DESC'
end
UPDATE
When I use save! I have an error message Story cannot be blank.
You need to set the story for the comment your are building since (as you've obviously worked out), the story in question is given by params[:story_id]. That story id isn't magically going to find its way into the params[:comment] hash. You could either do
#comment = #story.comments.build(params[:comment])
#comment.user = current_user
or create the comment on the user and then set its story.