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?
Related
I've been struggling with this issue for 2 days now and I think I'm slowly starting to lose my mind.
I'm trying to update boolean 'schedule_display' for 'profiles' table in my nested form. Everything except this checkbox works just fine. At the present state html looks like this:
<%= nested_form_for #profile, html: { multipart: true } do |f| %>
<span class="picture">
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
</span>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :schedule_display %>
<%= f.check_box :schedule_display, {}, "true", "false" %>
<%= f.fields_for :buttons %>
<%= f.link_to_add "Add a button", :buttons %>
<%= f.submit "Save changes" %>
<% end %>
Parameters after submit look fine I think:
profile"=>{"description"=>"Dolor et exercitationem.", "schedule_display"=>"true", ...
Schedule_display is also in permited params in the right place:
params.require(:profile).permit(:id, :description, :picture,
:schedule_display, buttons_attributes: [
Corresponding part of Profile model looks like this:
class Profile < ActiveRecord::Base
belongs_to :user
has_many :buttons, :dependent => :destroy
accepts_nested_attributes_for :buttons,
reject_if: proc { |attributes| attributes['user_website_url'].blank? },
:allow_destroy => true
mount_uploader :picture, PictureUploader
attr_accessor :schedule_display
validates :description, presence: true, length: { maximum: 500 }
and update method is simply:
def update
#user = User.find(params[:id])
#profile = #user.profile
if #profile.update_attributes(profile_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
I've tried doing something like this:
if #profile.update_attributes(profile_params)
params[:profile][:schedule_display] == '1' ?
profile.turn_schedule_on : profile.turn_schedule_off
with these turn_schedule_ functions and simple update_attribute(), but it didn't work either.
Why isn't it working?
I'm new to Nested Forms. I'm building a form where Companies can create Promos as under a few Categories. I keep getting undefined method "model_id" for Class. My codes and exact error messages are as below :-
Error Message:-
undefined method `category_id' for #<Company:0x007fd43828b6f8>
<%= f.collection_select :category_id, Category.all, :id, :name, {include_blank: 'Choose Category'}, required: true %>
<%= render partial: 'promo', locals: {f: f}%>
Models :-
class Company < ActiveRecord::Base
has_many :users, :through => :company_customers
has_many :company_users
has_many :categories, :through => :category_companies
has_many :category_companies
accepts_nested_attributes_for :category_companies
accepts_nested_attributes_for :categories s
end
class CompanyCategory < ActiveRecord::Base
belongs_to :company
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :company_categories
has_many :companies, :through => :company_categories
has_many :promos
accepts_nested_attributes_for :company_categories
end
class Promo < ActiveRecord::Base
belongs_to :category
end
Controller :-
class CompaniesController < ApplicationController
def new
#company = Company.new
end
def create
#company = Company.new(company_params)
respond_to do |format|
if #company.save
format.html { redirect_to #company, notice: 'Company was successfully created.' }
format.json { render :show, status: :created, location: #company }
else
format.html { render :new }
format.json { render json: #company.errors, status: :unprocessable_entity }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_company
#company = Company.find(params[:id])
end
def company_params
params.require(:company).permit(:company_name, :registration_no, :address, :phone_no, :outlet_location, company_categories_attributes: [:id, :company_id, :category_id, category_attributes:[:id, :name, promo_attribute:[:id, :name, :description]]])
end
end
View:-
<%= form_for(:company) do |f| %>
<div class="medium-6 columns">
<%= f.collection_select :category_id, Category.all, :id, :name, {include_blank: 'Choose Category'}, required: true %>
<%= render partial: 'promo', locals: {f: f}%>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
_promo.html.erb
<%= f.fields_for :promos do |promo| %>
<h4 class="text-center">Promotions to be offered</h4><br>
<div class="row">
<div class="medium-6 columns">
<%= f.text_field :name, placeholder: "Name", required: true %>
</div>
<div class="medium-6 columns">
<%= f.text_field :description, placeholder: "Description", required: true %>
</div>
</div>
<% end %>
<p><%= f.link_to_add "Add More Promos", :promos %> </p>
Appreciate the help. Many Thanks !
From the docs, the collection_select has the following use:
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
They have this example:
class Post < ActiveRecord::Base
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :posts
def name_with_initial
"#{first_name.first}. #{last_name}"
end
end
Which would be used like this:
collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
So, when you do:
f.collection_select :category_id, Category.all, :id, :name, {include_blank: 'Choose Category'}, required: true
I think maybe you're missing the object argument? Like, where they have :post.
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.
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
Im struggling with a nested form in Rails 4, this is the first time that I made a form of that kind. I have read lots of documentation but Im not able to make it work. :-(
I have two models: Person and Disease. One person can have many diseases and one disease belongs to person. Looks quite simple. The form for Diseases is not saved in the database.
Person Model:
class Person < ActiveRecord::Base
belongs_to :user
has_many :diseases, :dependent => :destroy #if you delete a person you also delete all diseases related
has_many :appointments, :dependent => :destroy
validates_presence_of :name, :email
validates :name, :length => {:maximum => 50, :too_long => "name is too long"}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, format: { :with => VALID_EMAIL_REGEX , message: "is invalid" }
accepts_nested_attributes_for :diseases
end
Disease Model:
class Disease < ActiveRecord::Base
belongs_to :person
has_many :treatments
validates_presence_of :name, :start
validates :name, :length => {:maximum => 50, :too_long => "is too long, you can use the description field"}
validate :start_must_be_before_end, :unless => [:chronical, :unfinished], :presence => true
validates :end, :presence => true, :unless => [:chronical, :unfinished], :presence => true
validates :description, :length => {:maximum => 5000, :too_long => "is too long"}
def start_must_be_before_end
if self[:end] < self[:start]
errors.add(:start, "must be before end time")
return false
else
return true
end
end
end
People Controller:
def create
current_user
#person = Person.new(person_params)
#person.user_id = #current_user.id
respond_to do |format|
if #person.save
format.html { redirect_to #person, notice: 'Person was successfully created.' }
format.json { render action: 'show', status: :created, location: #person }
else
format.html { render action: 'new' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
def person_params
params.require(:person).permit(:name, :surname, :gender, :birthdate, :bloodtype, :user_id, :phone, :email, diseases_attributes: [:id, :description] )
end
Form:
<%= simple_form_for #person do |f| %>
<%= f.input :name %>
<%= f.input :email %>
<%= simple_fields_for :diseases do |my_disease| %>
<%= my_disease.input :description %>
<% end %>
<%= f.button :submit %>
<% end %>
Thanks a lot for your help.
The issue is with line <%= simple_fields_for :diseases do |my_disease| %>.It should be
<%= f.simple_fields_for :diseases do |my_disease| %>
This should work.
<%= simple_form_for #person do |f| %>
<%= f.input :name %>
<%= f.input :email %>
<%= f.simple_fields_for :diseases do |my_disease| %> #here
<%= my_disease.input :description %>
<% end %>
<%= f.button :submit %>
<% end %>
For more Info,see this API