I have a one model 'Task' and have two entities - 'tasks' and 'subtasks' with self-reference association.
class Task < ActiveRecord::Base
has_many :subtasks, class_name: 'Task', foreign_key: 'parent_id', dependent: :destroy
belongs_to :parent, class_name: 'Task'
accepts_nested_attributes_for :subtasks, allow_destroy: true
validates :title, presence: true, length: { minimum: 3 }
validates :priority, presence: true, numericality: { only_integer: true }, length: { is: 1 }
validates_associated :subtasks
end
And i use one controller - TasksController.
class TasksController < ApplicationController
before_action :find_task, only: [:show, :edit, :update, :destroy, :run, :complete]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_task
def run
#task.run!
redirect_to :back
end
def complete
#task.complete!
redirect_to :back
end
def index
#tasks = Task.all
end
def show
end
def new
#task = Task.new
end
def edit
end
def create
#task = Task.create(task_params)
if #task.errors.empty?
redirect_to tasks_path, notice: "Task created!"
else
render 'new', notice: "Invalid input!"
end
end
def update
#task.update_attributes(task_params)
if #task.errors.empty? || :subtasks_attributes?
redirect_to #task
else
render 'edit'
end
end
def destroy
#task.destroy
if #task.parent_id?
redirect_to #task.parent
else
redirect_to tasks_path
end
end
private
def task_params
params.require(:task).permit(:title, :description, :scheduled, :deadline, :priority, :project, subtasks_attributes: [:title, :priority])
end
def find_task
#task = Task.find(params[:id])
end
def invalid_task
redirect_to tasks_path, notice: "Invalid task!"
end
end
I wanna create subtasks on task show page:
- #task.subtasks.each do |subtask|
- if subtask.in_work?
=> link_to 'Complete', complete_task_path(subtask), method: :put
- else
=> link_to 'Run', run_task_path(subtask), method: :put
=> subtask.title
=> link_to 'Edit', edit_task_path(subtask)
= link_to 'Delete', [subtask], method: :delete, data: { confirm: 'Are you sure?' }
= simple_form_for #task do |t|
= t.simple_fields_for :subtasks, #task.subtasks.build do |f|
.form-inputs
= f.input :title
= f.hidden_field :priority, value: #task.priority
.form-actions
= f.button :submit, "Add a subtask"
Now on the task show page i can create a subtask with valid attributes and can't create a subtask with invalid attributes, but i do not get validation message.
How can i fix it?
Ty and sorry for my English.
PS:
i don't know why, but errors exist inside controller and doesn't exist inside view
#project.update_attributes(project_params)
puts #project.errors.full_messages
if #project.errors.empty? || :tasks_attributes?
redirect_to #project
puts #project.errors.full_messages
(0.0ms) begin transaction
(0.0ms) rollback transaction
Tasks title can't be blank
Tasks title is too short (minimum is 3 characters)
Redirected to http://localhost:3000/projects/3
Tasks title can't be blank
Tasks title is too short (minimum is 3 characters)
Completed 302 Found in 6ms (ActiveRecord: 0.2ms)
You should add the errors messages to the view too:
= simple_form_for #task do |t|
= t.simple_fields_for :subtasks, #task.subtasks.build do |f|
#error message added here
- if #task.subtasks.errors.any?
%ul.errors
- #task.subtasks.errors.full_messages.each do |msg|
%li= msg
.form-inputs
= f.input :title
= f.hidden_field :priority, value: #task.priority
.form-actions
= f.button :submit, "Add a subtask"
EDIT
You have a _form partial in you application, change that code to this
= simple_form_for #task do |f|
- if #task.errors.any?
ul.errors
- #task.errors.full_messages.each do |msg|
=> msg
= f.input :title
= f.input :description
= f.input :scheduled
= f.input :deadline
= f.input :priority, collection: [["None", 0], ["High", 3], ["Medium", 2], ["Low", 1]], selected: ["None"]
= f.button :submit
Related
Nested form (with simple_form gem) creates the record, but doesn't want to update it.
Checklist has many questions.
Question belongs to one checklist.
All strong params are setted.
So, in controller:
...
before_action :set_checklist, only: [:show, :edit, :update, :destroy]
...
def new
#checklist = Checklist.new
#checklist.questions.build
end
def create
#checklist = Checklist.new(checklist_params)
if #checklist.save
redirect_to checklists_url, notice: 'Checklist was successfully created.'
...
def edit
end
def update
if #checklist.update(checklist_params)
redirect_to #checklist, notice: 'Checklist was successfully updated.'
else
render :edit
end
end
...
private
def set_checklist
#checklist = Checklist.find(params[:id])
end
def checklist_params
params
.require(:checklist)
.permit(:title, :description,
questions_attributes: Question.attribute_names.map(&:to_sym).push(:_destroy))
end
in view form:
= simple_form_for(#checklist) do |f|
= f.error_notification
.form-inputs
= f.input :title
= f.input :description
...
%tbody.questions
= f.simple_fields_for :questions do |builder|
= render 'question_fields', f: builder
...
in _question_fields:
%tr.nested-fields
%td
= link_to_remove_association "remove", f, class: 'btn btn-primary btn-xs'
%td
= f.input :title, label: false
%td
= f.input :description, label: false
in checklist model:
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions,
allow_destroy: true,
reject_if: proc { |att| att['title'].blank? }
in question model:
belongs_to :checklist, optional: true
Thank you
%tr.nested-fields
%td
= link_to_remove_association "remove", f, class: 'btn btn-primary btn-xs'
%td
= f.input :title, label: false
%td
= f.input :description, label: false
your partial is missing = f.hidden_field :id and = f.hidden_field :_destroy
I am developing a recipe app and am not able to show a dropdown menu for category and sub categories.
I created menu for categories and it is showing correctly, but now I want when I click on category it will show a subcategories which belongs to them and after clicking on subcategories I want it to show the recipes.
Here is my code:
controller
def index
if params[:category].blank?
#recipe = Recipe.all.order("created_at DESC")
elsif params[:category]
#category_id = Category.find_by(name: params[:category]).id
#recipe = Recipe.where(category_id: #category_id).order("created_at DESC")
else params[:subcategory]
#subcategory_id = Subcategory.find(name: params[:subcategory]).id
#recipe = Recipe.where(subcategory_id: #subcategory_id).order("created_at DESC")
end
end
def show
end
def new
#recipe = current_user.recipe.build
end
def create
#recipe = current_user.recipe.build(recipe_params)
if #recipe.save
redirect_to #recipe, notice: "recipe sucessfully created"
else
render 'new'
end
end
def edit
end
def update
if #recipe.update(recipe_params)
redirect_to #recipe
else
render 'edit'
end
end
def destroy
#recipe.destroy
redirect_to root_path, notice: "sucessfully deleted"
end
private
def recipe_params
params.require(:recipe).permit(:title, :description, :image, :category_id, :subcategory_id, ingredients_attributes: [:id, :name, :_destroy], directions_attributes: [:id, :step, :_destroy])
end
def find_recipe
#recipe = Recipe.find(params[:id])
end
end
applicatio.html.haml
!!! 5
%html
%head
%title Recipe App
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
= javascript_include_tag 'application', 'data-turbolinks-track' => true
= csrf_meta_tags
body
%nav.navbar.navbar-default
.container
.navbar-brand= link_to "Recipe Box", root_path
%ul.nav.navbar-nav
- Category.all.each do |category|
%li= link_to category.name, recipes_path(category: category.name)
- category.subcategories.each do |subcategory|
%li= link_to subcategory.name, recipes_path(subcategory: subcategory.name)
= render 'layouts/header'
- if user_signed_in?
%ul.nav.navbar-nav.navbar-right
%li= link_to "New Recipe", new_recipe_path
%li= link_to "Sign Out", destroy_user_session_path, method: :delete
- else
%ul.nav.navbar-nav.navbar-right
%li= link_to "Sign Up", new_user_registration_path
%li= link_to "Sign In", new_user_session_path
.container
- flash.each do |name, msg|
= content_tag :div, msg, class: "alert"
= yield
recipe.rb model
class Recipe < ActiveRecord::Base
searchkick
belongs_to :user
belongs_to :subcategory
has_many :ingredients
has_many :directions
accepts_nested_attributes_for :ingredients,
reject_if: proc { |attributes| attributes ['name'].blank? },
allow_destroy: true
accepts_nested_attributes_for :directions,
reject_if: proc { |attributes| attributes ['step'].blank? },
allow_destroy: true
validates :title, :description, :image, presence: true
has_attached_file :image, style: { :medium => "400x400#>" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
category.rb
class Category < ActiveRecord::Base
has_many :subcategories
has_many :recipes, :through => :subcategories
end
subcategory.rb
class Subcategory < ActiveRecord::Base
belongs_to :category
has_many :recipes
end
Please help me out with solving this question.
You can do this with javascript or JQuery. Call change function on category_name id and within this function change the option values of subcategory and so on.
i have model poll with nested attributes poll_items:
class Poll < ActiveRecord::Base
ITEMS_COUNT_MAX = 5
attr_accessible :from_date, :question, :results_hidden, :to_date, :owner_id, :poll_items_attributes
belongs_to :owner, polymorphic: true
has_many :poll_items, dependent: :destroy
has_many :poll_votes, through: :poll_items
accepts_nested_attributes_for :poll_items, allow_destroy: true,
reject_if: proc { |attributes| attributes['answer'].blank? }
#validates :owner, :question, :from_date, :to_date, presence: true
#validate :validate_duplicate, on: :create
validate :validate_max_poll_items, on: :update
after_initialize :defaults
...................................................................
model pollItem
attr_accessible :answer
attr_readonly :poll_votes_count
belongs_to :poll
has_many :poll_votes, dependent: :destroy
has_many :users, through: :poll_votes
validates :poll, :answer, presence: true
validate :validate_duplicate, on: :create
scope :editable, lambda { |u|
unless u.moderator?
where(:poll.owner.user_id => u.id).where(:created_at.gt Settings.edit_delay.minutes.ago)
end
}
private
def validate_duplicate
errors.add(:base, :duplicate) unless poll.poll_items.where(answer: answer).empty?
end
end
Poll_controller:
class PollsController < ApplicationController
before_filter :authenticate_user!
before_filter :set_poll, only: [:show, :edit, :update, :destroy]
before_filter :find_owner, only: [:new, :create]
def new
#poll = Poll.new
end
def create
#poll = Poll.new params[poll_params]
##poll.owner = #owner
respond_to do |format|
if #poll.save
format.html { redirect_to [:edit, #poll], notice: 'Опрос был успешно создан.' }
else
format.html { render :new }
end
end
end
def show
end
def edit
#poll.poll_items.build
end
def update
if #poll.editable?(current_user)
if #poll.update_attributes params[:poll]
respond_to do |format|
format.html { redirect_to [:edit, #poll], notice: 'Опрос был успешно обновлен.' }
end
else
respond_to do |format|
format.html { render :edit, alert: #poll.errors }
end
end
end
end
def destroy
#poll.destroy
respond_to do |format|
format.html { redirect_to #owner, notice: 'Опрос был успешно удален.' }
end
end
private
def poll_params
params.require(:poll).permit(:from_date, :question, :results_hidden, :to_date,
:owner_id, poll_items_attributes: [:id, :unswer, :_destroy])
end
protected
def set_poll
#poll = Poll.find(params[:id])
#owner = #poll.owner
end
def find_owner
#owner = case
when params[:post_id]
Post.visible(current_user).find(params[:post_id])
when params[:blog_post_id]
BlogPost.with_privacy(current_user).find(params[:blog_post_id])
end
end
end
I installed gem cocoon:
next i create view new.html.haml:
%h1= title "Новый опрос"
= simple_form_for #poll do |f|
= f.error_messages header_message: nil
= f.input :question, disabled: !#poll.editable?(current_user), input_html: { class: 'input-block-level' }
= f.input :results_hidden, as: :boolean, inline_label: 'Скрыть результаты до окончания опроса', label: false
= f.input :from_date, as: :datetime, input_html: { class: 'poll_date' }
= f.input :to_date, as: :datetime, input_html: { class: 'poll_date' }
.items-index
%h3#poll-items Варианты ответа (не больше пяти)
= f.simple_fields_for :poll_items do |poll|
= render 'poll_items_fields', f: poll
= link_to_add_association 'Добавить еще вариант', f, :poll_items
.form-actions
= f.button :submit, 'Опубликовать опрос', class: 'btn-bg'
%p
Вернуться к посту:
= link_to #owner
poll_items_fields.html.haml:
.poll_row
.poll_item
= f.input :answer, input_html: { class: 'ctrlenter expanding' }, label: false, placeholder: 'Введите вариант ответа'
= link_to_remove_association "удалить", f
I open page new for creating new poll, but only field for new name quation poll and button "create poll". No fields for adding poll_items. I thinkm dont render form poll_items_fields.html.haml. But why? How fix?
If I understood correctly, your new method should look like this
def new
#poll = Poll.new
#poll.poll_items.build
end
And also you have unswer instead of answer in poll_items_attributes, so fix that too.
def poll_params
params.require(:poll).permit(:from_date, :question, :results_hidden, :to_date, :owner_id, poll_items_attributes: [:id, :answer, :_destroy])
end
Update:
You should also remove attr_accessible :from_date, :question, :results_hidden, :to_date, :owner_id, :poll_items_attributes from your Poll model as Rails 4 uses strong parameters.
And also this line #poll = Poll.new params[poll_params] in create method should look like this #poll = Poll.new(poll_params)
Ruby version :
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]
Rails version :
Rails 4.1.4
I have a HABTM association between two models : Product and Page. This association works well when I use the console with the following command :
Product.first.pages << Page.first
I inserted a check_box_tag in my products/_form.html.haml for displaying my pages. The check_box works well and I can check/uncheck all my pages.
The problem is when I try to submit the form, the modifications I have done in the checkbox are not saved. I have this problem in both ways.
I figured the problem is the Unpermitted parameters error in my log.
Started PATCH "/admin/pages/linge-de-lit" for 127.0.0.1 at 2014-09-23 23:13:40 +0200
Processing by Admin::PagesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"q/ZskL+Zijc8GMX9lF+EPgqc9uic9N9B/isWYHx7Cx0=", "page"=>{"category"=>"fabric", "title"=>"Linge de lit", "intro"=>"", "visibility"=>"1", "favorite"=>"0", "priority"=>"50", "product_ids"=>["", "1", "2"]}, "commit"=>"Save", "id"=>"linge-de-lit"}
Page Load (0.3ms) SELECT "pages".* FROM "pages" WHERE "pages"."slug" = 'linge-de-lit' ORDER BY "pages"."id" ASC LIMIT 1
Unpermitted parameters: product_ids
(0.3ms) begin transaction
Page Exists (0.3ms) SELECT 1 AS one FROM "pages" WHERE ("pages"."title" = 'Linge de lit' AND "pages"."id" != 5) LIMIT 1
(0.2ms) commit transaction
Redirected to http://localhost:3000/admin/pages/linge-de-lit
Completed 302 Found in 86ms (ActiveRecord: 1.1ms)
I think the problem is my strong parameters but I doesn't see where.
Here is my models :
class Product < ActiveRecord::Base
has_and_belongs_to_many :pages
end
class Page < ActiveRecord::Base
has_and_belongs_to_many :products
has_many :posts
validates :category, presence: true
validates :title, presence: true, length: {maximum: 20}, uniqueness: true
extend FriendlyId
friendly_id :title, :use => [:slugged, :finders]
before_save :default_values
def default_values
self.title = self.title.capitalize
end
#Scopes
scope :visible, -> { where(visibility: true) }
scope :favorite, -> { where(favorite: true) }
end
Here is my controllers :
class Admin::ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /admin/products
def index
#products = Product.all
end
# GET /admin/products/1
def show
end
# GET /admin/products/new
def new
#product = Product.new
#pages = Page.all
end
# GET /admin/products/1/edit
def edit
#pages = Page.all
end
# POST /admin/products
def create
#product = Product.new(product_params)
if #product.save
redirect_to [:admin, #product], notice: 'Product was successfully created.'
else
render action: 'new'
end
end
# PATCH/PUT /admin/products/1
def update
if #product.update(product_params)
redirect_to [:admin, #product], notice: 'Product was successfully updated.'
else
render action: 'edit'
end
end
# DELETE /admin/products/1
def destroy
#product.destroy
redirect_to admin_products_url, notice: 'Product was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def product_params
params.require(:product).permit(:name, :description, :brand_id, :price, :minimum_price, :shop_disponibility, :web_disponibility, :purchase_link, :favorite, {:pages_ids => []})
end
end
class Admin::PagesController < ApplicationController
before_action :set_page, only: [:show, :edit, :update, :destroy]
# GET /admin/pages
def index
#pages = Page.all
end
# GET /admin/pages/1
def show
end
# GET /admin/pages/new
def new
#page = Page.new
#products = Product.all
end
# GET /admin/pages/1/edit
def edit
#products = Product.all
end
# POST /admin/pages
def create
#page = Page.new(page_params)
if #page.save
redirect_to [:admin, #page], notice: 'Page was successfully created.'
else
render action: 'new'
end
end
# PATCH/PUT /admin/pages/1
def update
if #page.update(page_params)
redirect_to [:admin, #page], notice: 'Page was successfully updated.'
else
render action: 'edit'
end
end
# DELETE /admin/pages/1
def destroy
#page.destroy
redirect_to admin_pages_url, notice: 'Page was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_page
#page = Page.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def page_params
params.require(:page).permit(:category, :title, :intro, :visibility, :favorite, :priority, :products_ids => [])
end
end
Here is my views :
# products/_form
= form_for([:admin, #product]) do |f|
- if #product.errors.any?
#error_explanation
%h2= "#{pluralize(#product.errors.count, "error")} prohibited this product from being saved:"
%ul
- #product.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_field :name
.field
= f.label :description
= f.text_area :description
.field
= f.label :brand_id
= f.text_field :brand_id
.field
= f.label :price
= f.text_field :price
.field
= f.label :minimum_price
= f.check_box :minimum_price
.field
= f.label :shop_disponibility
= f.check_box :shop_disponibility
.field
= f.label :web_disponibility
= f.check_box :web_disponibility
.field
= f.label :purchase_link
= f.text_field :purchase_link
.field
= f.label :favorite
= f.check_box :favorite
= hidden_field_tag "product[page_ids][]", nil
- Page.all.each do |page|
= check_box_tag "product[page_ids][]", page.id, #product.page_ids.include?(page.id), id: dom_id(page)
= label_tag dom_id(page), page.title
.actions
= f.submit 'Save'
# pages/_form
= form_for([:admin, #page]) do |f|
- if #page.errors.any?
#error_explanation
%h2= "#{pluralize(#page.errors.count, "error")} prohibited this page from being saved:"
%ul
- #page.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :category
= f.select :category, options_for_select(#pages_categories), {:prompt => "- Sélectionner une catégorie -"}
.field
= f.label :title
= f.text_field :title
.field
= f.label :intro
= f.text_area :intro
.field
= f.label :visibility
= f.check_box :visibility
.field
= f.label :favorite
= f.check_box :favorite
.field
= f.label :priority
= f.number_field :priority
= hidden_field_tag "page[product_ids][]", nil
- Product.all.each do |product|
= check_box_tag "page[product_ids][]", product.id, #page.product_ids.include?(product.id), id: dom_id(product)
= label_tag dom_id(product), product.name
.actions
= f.submit 'Save'
There is a typo there :) Instead of
# Only allow a trusted parameter "white list" through.
def page_params
params.require(:page).permit(:category, :title, :intro, :visibility, :favorite, :priority, :products_ids => [])
end
It should be product_ids:
# Only allow a trusted parameter "white list" through.
def page_params
params.require(:page).permit(:category, :title, :intro, :visibility, :favorite, :priority, :product_ids => [])
end
I'm working on an app to book different types of tours. I have 3 tables - 1) reservations 2) tours 3) join table (reservations_tours). I'm trying to pass the tour id to reservations, but keep getting this error:
Couldn't find Tour without an ID
app/controllers/reservations_controller.rb:12:in `create'
Reservations controller:
class ReservationsController < ApplicationController
def index
end
def new
#reservation = Reservation.new
#tour = Tour.find(params[:tour_id])
end
def create
#tour = Tour.find(params[:tour_id])
#reservation = Reservation.new(reservation_params)
##reservation.tour = #tour # associate #reservation with video
if #reservation.save
Stripe.api_key = ENV["STRIPE_SECRET_KEY"]
Stripe::Charge.create(
:amount => 9000, # amount in cents, again
:currency => "usd",
:card => params[:stripeToken] # Get the credit card details submitted by the form
)
flash[:success] = "Your reservation has been booked for #{#reservation.date} for #{#reservation.passengers} person(s). Please save this info."
redirect_to new_tour_path
else
render 'new'
end
end
private
def reservation_params
params.require(:reservation).permit(:date, :passengers)
end
end
Tours controller:
class ToursController < ApplicationController
def index
end
def new
#tour = Tour.new
end
def create
#tour = Tour.new(tours_params)
if #tour.save
flash[:success] = "Tour #{#tour.name} has been successfully added."
redirect_to new_tour_path
else
flash[:error] = "The tour #{#tour.name} was not successfully saved. Please try again"
render 'new'
end
end
def show
#tour = Tour.find_by(id: params[:id])
#reservation = Reservation.new
end
def edit
end
def update
end
private
def tours_params
params.require(:tour).permit(:name, :amount)
end
end
Reservations view (new)
= javascript_include_tag "https://js.stripe.com/v2/"
= javascript_include_tag 'payment'
:javascript
Stripe.setPublishableKey("#{ENV['STRIPE_PUBLISHABLE_KEY']}");
.container
.row
.col-md-9
%h2= #tour.name
.col-md-3
%p= #tour.amount
= bootstrap_form_for(#reservation, html: { class: 'form-horizontal', id: 'payment-form'}) do |f|
= f.alert_message 'Please fix the errors below:'
= f.text_field :date
= f.text_field :passengers
%fieldset.credit_card
%span.payment-errors
.control-group
= label_tag :card_number, 'Credit card number:', class: 'control-label'
.controls
= text_field_tag :card_number, nil, name: nil, class: 'span3', data: {stripe: 'number'}
.control-group
= label_tag :security_code, 'Security code:', class: 'control-label'
.controls
= text_field_tag :security_code, nil, name: nil, class: 'span3', data: {stripe: 'cvc'}
.control-group
= label_tag :exp_date, 'Expiration:', class: 'control-label'
.controls
= select_month(Date.today, {add_month_numbers: true}, class: 'span2', data: {stripe: 'exp-month'})
= select_year(Date.today.year, {start_year: Date.today.year, end_year: Date.today.year + 4}, class: 'span1', data: {stripe: 'exp-year'})
%fieldset.actions.control-group
.controls
= f.submit 'Sign up'
Reservation model:
class Reservation < ActiveRecord::Base
has_many :reservations_tours, foreign_key: 'reservation_id'
has_many :tours, through: :reservations_tours
end
Tour model
class Tour < ActiveRecord::Base
has_many :reservations_tours, foreign_key: 'tour_id'
has_many :reservations, through: :reservations_tours
end
Join table
class ReservationsTours < ActiveRecord::Base
belongs_to :reservation, foreign_key: 'reservation_id'
belongs_to :tour, foreign_key: 'tour_id'
end
Routes:
Rails.application.routes.draw do
resources :reservations, only: [:new, :create]
resources :tours
end
You are trying to create a non existing relation. You have two basic options to create a relation in rails:
Provide a dropdown with existing tours in the reservations form
f.collection_select(:tour_id, Tour.all, :id, :name)
it will become available in the params[:reservation] array. You will have to permit the tour_id param in reservation_params
make a nested resource for reservations in config/routes.rb.
resources :tours do
resources :reservations
end
which will give you a POST url like /tours/:tour_id/reservations and provide you with params[:tour_id]