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)
Related
So i have two models : Internship and Review. I want the Review to be a nested_attributs of Internship. So that i can create an internship with a review.
My problem is that the review form integrated in the new form internship, doesn't find the id of the internship. It raise the errors 'Review internship must exist'
Internship.rb
has_many :reviews, foreign_key: "review_internship_id"
has_many :review_users, foreign_key: 'review_user_id', class_name:"User", through: :reviews
accepts_nested_attributes_for :reviews, allow_destroy: true
validates_associated :reviews
Review.rb
belongs_to :review_user, class_name: "User"
belongs_to :review_internship, class_name: "Internship"
Internship_controller
def new
#internship = Internship.new
#internship.reviews.new
end
def create
#internship = Internship.new(internship_params)
#internship.user = current_user
#internship.reviews.first.review_user_id = current_user.id
respond_to do |format|
if #internship.save
format.html { redirect_to #internship, notice: 'Expérience crée avec succès' }
format.json { render :show, status: :created, location: #internship }
else
format.html { render :new }
format.json { render json: #internship.errors, status: :unprocessable_entity }
end
end
end
private
def internship_params
params.require(:internship).permit(:adress, :zipcode, :city, :specialty, :organization, :population, :cursus, :title, :duration, :description, :region, :remuneration, :user_id, reviews_attributes: [:title, :notation, :description, review_internship_id: params[:internship_id], review_user_id: current_user.id])
end
The new form in internship new
<%= form_with(model: internship, local: true) do |form| %>
....
<!--NESTED ATTRIBUTS REVIEW-->
<%= form.fields_for :reviews do |p| %>
<%= p.label :titre %>
<%= p.text_field :title, class:"form-control" %>
<%= p.label :note %>
<%= p.number_field :notation, min: 1, max: 5, class:"form-control" %>
<%= p.label :description %>
<%= p.text_area :description, class:"form-control" %>
<% end %>
...
<% end %>
So this is what i've tried in Internship controller
#intership.reviews.review_internship_id = #internship.id
So that it can find the id of the internship. The error is "Review Internship must exist".
It did the same with "Review User", which was solved with #internship.reviews.first.review_user_id = current_user.id
Do you have any idea where the problem is, and how i can find the internship id with another way. I've also tried the params[:id]
Thanks for your help
:
You don't really need a user_id foreign key on reviews since it can get there through the interview:
class Review
belongs_to :internship
has_one :user, through: :interview
end
class Internship
belongs_to :user
has_many :reviews
end
class User
has_many :internships
has_many :reviews, through: :internships
end
And you definitely don't ever need to be manually assigning parent ids for nested records.
class IntershipsController < ApplicationController
def new
#internship = Internship.new
#internship.reviews.new
end
def create
#internship = Internship.new(internship_params) do |i|
i.user = current_user
end
respond_to do |format|
if #internship.save
format.html { redirect_to #internship, notice: 'Expérience crée avec succès' }
format.json { render :show, status: :created, location: #internship }
else
format.html { render :new }
format.json { render json: #internship.errors, status: :unprocessable_entity }
end
end
end
private
def internship_params
# use some line-breaks!
params.require(:internship)
.permit(
:adress, :zipcode, :city, :specialty,
:organization, :population, :cursus,
:title, :duration, :description,
:region, :remuneration, :user_id,
reviews_attributes: [
:id, :title, :notation, :description
]
)
end
end
I am trying simple_form nested attributes as suggested in https://github.com/plataformatec/simple_form/wiki/Nested-Models
The thing is, when I render the form I just can see the submit button, but not the input field. What am I doing wrong?
_form.html.erb
<%= simple_form_for [:admin, #incident] do |f| %>
<%= f.error_notification %>
<%= f.simple_fields_for :comments do |builder| %>
<%= builder.input :info, label: "Informe de seguimiento" %>
<% end %>
<div class="form-actions">
<%= f.submit "Enviar", class: "btn btn-primary" %>
</div>
<% end %>
incidents_controller.rb
class Admin::IncidentsController < ApplicationController
before_action :set_incident, only: [:show, :edit, :update]
def index
#incidents = Incident.all
end
def show
end
def new
#incident = Incident.new
#incident.comments.build
end
def edit
end
def update
respond_to do |format|
if #incident.update(incident_params)
format.html { redirect_to #incident, notice: 'Incidencia actualizada actualizada con éxito.' }
format.json { render :show, status: :ok, location: #incident }
else
format.html { render :edit }
format.json { render json: #incident.errors, status: :unprocessable_entity }
end
end
end
private
def set_incident
#incident = Incident.find(params[:id])
end
def incident_params
params.require(:incident).permit(:info, :subject, :status, comments_attributes: [:info])
end
end
incident.rb
class Incident < ApplicationRecord
belongs_to :user, optional: true
has_many :comments, dependent: :destroy
accepts_nested_attributes_for :comments, allow_destroy: true, reject_if: proc { |attributes| attributes['info'].blank? }
enum status: [:abierto, :tramite, :pendiente, :cerrado]
after_initialize :set_default_status, :if => :new_record?
def set_default_status
self.status ||= :abierto
end
end
comment.rb
class Comment < ApplicationRecord
belongs_to :user, optional: true
belongs_to :incident
end
You need to add #incident.comments.build to the show action of Admin::IncidentsController. Now it has no comments, I suppose, so the form is empty.
And you need to add :id to comments_attributes, without it comment can't be saved. If you planning to have some 'Delete' checkbox for existing comments you also need to add :_destroy to the attributes array
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
I have few multi-selects in my app that validation always fail (getting form errors: genres can't be blank, languages can't be blank) during every form submit (even if the multi-select options were selected). Code:
models/dvd.rb
class Dvd < ActiveRecord::Base
has_and_belongs_to_many :genres
has_and_belongs_to_many :languages
has_many :rentals, dependent: :destroy
has_many :users, through: :rentals
validates :title, presence: true
validates :year, inclusion: {in: 1900..Time.now.year.to_i}, :presence => {:message => 'Year must be from 1900 till current year.'}
validates :length, inclusion: {in: 1..999}, :presence => {:message => 'DVD length must be in minutes in range 1..999.'}
validates :genres, presence: true
validates :languages, presence: true
end
models/language.rb
class Language < ActiveRecord::Base
has_and_belongs_to_many :dvds
validates :title, presence: true, uniqueness: { case_sensitive: false }
end
models/genre.rb
class Genre < ActiveRecord::Base
has_and_belongs_to_many :dvds
validates :title, presence: true, uniqueness: { case_sensitive: false }
end
dvds_controller.rb
class DvdsController < ApplicationController
before_action :set_dvd, only: [:show, :edit, :update, :destroy]
before_action :set_genres, :set_languages, only: [:new, :edit]
before_action :delete_genres, :delete_languages, only: [:update]
after_action :add_genres, :add_languages, only: [:create, :update]
# GET /dvds
# GET /dvds.json
def index
#dvds = Dvd.all
end
# GET /dvds/1
# GET /dvds/1.json
def show
end
# GET /dvds/new
def new
#dvd = Dvd.new
end
# GET /dvds/1/edit
def edit
end
# POST /dvds
# POST /dvds.json
def create
#dvd = Dvd.new(dvd_params)
respond_to do |format|
if #dvd.save
format.html { redirect_to #dvd, notice: 'Dvd was successfully created.' }
format.json { render :show, status: :created, location: #dvd }
else
format.html { render :new }
format.json { render json: #dvd.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /dvds/1
# PATCH/PUT /dvds/1.json
def update
respond_to do |format|
if #dvd.update(dvd_params)
format.html { redirect_to #dvd, notice: 'Dvd was successfully updated.' }
format.json { render :show, status: :ok, location: #dvd }
else
format.html { render :edit }
format.json { render json: #dvd.errors, status: :unprocessable_entity }
end
end
end
# DELETE /dvds/1
# DELETE /dvds/1.json
def destroy
#dvd.destroy
respond_to do |format|
format.html { redirect_to dvds_url, notice: 'Dvd was successfully deleted.' }
format.json { head :no_content }
end
end
private
def set_dvd
if params[:id]
#dvd = Dvd.find(params[:id])
else
#dvd = Dvd.find(params[:dvd][:id])
end
end
def dvd_params
params.require(:dvd).permit(:title, :description, :year, :genres, :languages, :length)
end
def add_languages
params[:dvd][:languages].each do |l|
if !l.empty?
#dvd.languages << Language.find(l)
end
end
end
def add_genres
params[:dvd][:genres].each do |g|
if !g.empty?
#dvd.genres << Genre.find(g)
end
end
end
def set_genres
#genres = Genre.all
end
def set_languages
#languages = Language.all
end
def delete_genres
# Delete all dvd genre relations
#dvd.genres.delete_all
end
def delete_languages
# Delete all dvd language relations
#dvd.languages.delete_all
end
end
routes.rb
Rails.application.routes.draw do
resources :dvds do
resources :rentals
end
resources :rentals
resources :languages
resources :genres
resources :dvds
resources :users, :path => 'clients'
root to: "index#index"
end
form
<div class="field">
<%= f.label :genres %><br>
<%= f.collection_select(:genres, Genre.all, :id, :title, {:selected => #dvd.genres.map {|dl| dl.id}, :include_blank => false}, {:multiple => true}) %>
</div>
<div class="field">
<%= f.label :languages %><br>
<%= f.select :languages, options_for_select(Language.all.map {|l| [l.title,l.id]}, #dvd.languages.map {|dl| dl.id}), {:include_blank=> false}, {:multiple => true} %>
</div>
Any ideas what is wrong with that?
The field names for the form should be genre_ids and language_ids, not genres and languages.
I have a clan.rb and clan_options.rb
clan.rb
class Clan < ActiveRecord::Base
has_one :options, :class_name => "ClanOptions", :foreign_key => "clan_id", dependent: :destroy
accepts_nested_attributes_for :options
end
clan_options.rb
class ClanOptions < ActiveRecord::Base
belongs_to :clan
end
To create an edit form for clan.rb and clan_options.rb I use the following in edit.html.erb:
<%= form_for #clan, :html => {:class => 'form-horizontal'} do |clan| %>
<fieldset>
<!-- Form stuff -->
<%= clan.fields_for :options do |o| %>
<!-- o.text_field -->
<% end %>
</fieldset>
<% end %>
I can update the fields of clan.rb but when I try to edit the value backgroundurl it won't save it. Backgroundurl is one of the clan_options.rb
clans_controller.rb
class ClansController < ApplicationController
before_filter :check_login, :only => [:new, :edit]
before_filter :check_bound, :only => [:new, :edit]
before_filter :check_clan, :only => :new
def update
#clan = Clan.find(params[:id])
if #clan.update_attributes(clan_update_params)
flash[:status] = TRUE
flash[:alert] = "Successfully updated your clan."
redirect_to clan_path(params[:id])
else
flash[:status] = FALSE
flash[:alert] = #clan.errors.full_messages
redirect_to edit_clan_path(#clan.id)
end
end
def edit
clan = Clan.where(id: params[:id])
if !clan.blank?
#clan = Clan.find(params[:id])
user = User.where(id: session[:user_id])
if !user.blank?
#De gebruiker is ingelogt en zit in de clan
#user = User.find(session[:user_id])
if #clan.id != #user.clan.id
flash[:status] = FALSE
flash[:alert] = 'That was not your clan, you may not edit theirs.'
redirect_to clans_path
elsif #user.clanmember.group.rank != 10
flash[:status] = FALSE
flash[:alert] = "You must be the leader to edit the clan."
redirect_to clan_path(#clan.id)
end
end
else
flash[:status] = FALSE
flash[:alert] = 'that clan doesn\'t exist or has been removed.'
redirect_to clans_path
end
end
def clan_params
params.require(:clan).permit(:name, :prefix, :description, :user_id)
end
def clan_update_params
params.require(:clan).permit(:name, :prefix, :description, :user_id, options: [:id, :clan_id, :backgroundurl])
end
end
I've fixed it by changing
def clan_update_params
params.require(:clan).permit(:name, :prefix, :description, :user_id, options: [:id, :clan_id, :backgroundurl])
end
to
def clan_update_params
params.require(:clan).permit(:name, :prefix, :description, :user_id, options_attributes: [:backgroundurl])
end