I'm really new to both ruby on rails and programming. I am trying to develop an application but i am stucked now. I was watching http://railscasts.com/episodes/196-nested-model-form-part-1 to make nested model forms but i am having an error. My problem details are as follows;
I have employers model, and employers model has_many interviews, and interview model has_many customquestions. I'm trying to create a form through which i will collect info to create interview. Although i made all necessary assosications, when i submit the form it raises error saying that "Customquestions interview can't be blank". I am kinda sure that it is because of that i miss some code in interview controller. Below you can see my interview controller and the form template that i am using to submit info.
Interview Controller
class InterviewsController < ApplicationController
before_filter :signed_in_employer
def create
#interview = current_employer.interviews.build(params[:interview])
if #interview.save
flash[:success] = "Interview created!"
redirect_to #interview
else
render 'new'
end
end
def destroy
end
def show
#interview = Interview.find(params[:id])
end
def new
#interview = Interview.new
3.times do
customquestion = #interview.customquestions.build
end
end
end
Form which i use to submit info:
<%= provide(:title, 'Create a new interview') %>
<h1>Create New Interview</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#interview) do |f| %>
<%= render 'shared/error_messages_interviews' %>
<%= f.label :title, "Tıtle for Interview" %>
<%= f.text_field :title %>
<%= f.label :welcome_message, "Welcome Message for Candidates" %>
<%= f.text_area :welcome_message, rows: 3 %>
<%= f.fields_for :customquestions do |builder| %>
<%= builder.label :content, "Question" %><br />
<%= builder.text_area :content, :rows => 3 %>
<% end %>
<%= f.submit "Create Interview", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
In interview model, i used accepts_nested_attributes_for :customquestions
Interview Model
class Interview < ActiveRecord::Base
attr_accessible :title, :welcome_message, :customquestions_attributes
belongs_to :employer
has_many :customquestions
accepts_nested_attributes_for :customquestions
validates :title, presence: true, length: { maximum: 150 }
validates :welcome_message, presence: true, length: { maximum: 600 }
validates :employer_id, presence: true
default_scope order: 'interviews.created_at DESC'
end
The validation error gets raised in the customquestions model because (I assume) it validates :interview_id. The problem is that interview_id won't get set until the parent object (Interview) is saved, but validations for customquestion are run before Interview is saved.
You can let cusomtquestions know about this dependency by adding the option :inverse_of=> :customquestions to belongs_to :interview in the customquestions model.
Related
RAILS 6
Hey, I'm working on a class system that uses units and assignments as a many-to-many relationship. When I submit a new assignment form with a dropdown collection for units, the unit is not being received by the controller, but no error log is displayed. When I use byebug, the following error is displayed:
Unpermitted parameter: :unit_ids
Even though it has been permitted. Here's my controller for assignments.
class AssignmentsController < ApplicationController
def new
#assignment = Assignment.new
end
def create
debugger
#assignment = Assignment.new(assignment_params)
#assignment.save
if #assignment.save
flash[:success] = "The unit was successfully submitted."
redirect_to units_path
else
render 'new'
end
end
def show
end
private
def assignment_params
params.require(:assignment).permit(:name, :description, :duedate, user_ids: [])
end
end
Using byebug, I know the unit_id is being correctly received, from this form:
<%= form_for(#assignment, :html => {class: "form-horizontal", role: "form"}) do |f| %>
<div class="form-group">
<div>
<%= f.collection_select(:unit_ids, Unit.all, :id, :name, placeholder: "Units" )%>
</div>
<div>
<%= f.text_field :name, class:"form-control", placeholder: "Title of Assignment", autofocus: true %>
</div>
<div>
<%= f.text_area :description, class:"form-control materialize-textarea", placeholder: "Assignment Description", autofocus: true %>
</div>
<div>
<%= f.text_field :duedate, class: "datepicker", placeholder: "Due Date"%>
</div>
<div class="form-group" id="submitbutton">
<div align = "center">
<%= f.submit class: "btn waves-effect waves-light" %>
</div>
</div>
</div>
<%end%>
Here are the relevant models just to be safe. Note that I added the nested lines to both after I received this error because I saw it on another thread, but it doesn't seem to be fixing it.
class Unit < ApplicationRecord
has_and_belongs_to_many :users
has_and_belongs_to_many :assignments
accepts_nested_attributes_for :assignments
end
And the Assignment model:
class Assignment < ApplicationRecord
has_and_belongs_to_many :units
has_many :users, :through => :units
accepts_nested_attributes_for :units
end
The answer was a mix of a couple things, as Rockwell pointed out I was using User instead of Units, but that still didn't fix it. My collection had multiple choices set to false, so my controller wanted simply
params.require(:assignment).permit(:name, :description, :duedate, :unit_ids)
However, when I set multiple to true, that didn't work. Then, it wanted
params.require(:assignment).permit(:name, :description, :duedate, unit_ids[])
My solution was to leave multiple as true, and use the unit_ids[].
You have to update the permitted parameters
def assignment_params
params.require(:assignment).permit(:name, :description, :duedate, user_ids: [], unit_ids: [])
end
You mentioned that is was permitted, but I do not see unit_ids in the permitted params, I do see user_ids. Is there a spelling error? Or do you just need to include the unit_ids in there?
unit_ids is not a column name. You can use accept_nested_attribute or form object to solve this problem.
I'm building off the tutorial app in ruby on rails for a project, and I'm trying to create an association between two models.
In my database, there are users, events, and an attendance table that associates with the email from a user and a code from an event.
I've tried to research how to do this myself, but every time I try to validate the attendance email to a user, it states that the user cannot be blank as if I were trying to create a new one.
Still quite new to Ruby on Rails, so any advice would be appreciated! The models are below.
User Model:
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation
has_secure_password
has_many :attendances, inverse_of: :user
accepts_nested_attributes_for :attendances
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates(:name, presence: true, length: { maximum: 50 })
validates(:email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false})
validates(:password, length: { minimum: 6 } )
validates(:password_confirmation, presence: true)
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Attendance Model:
class Attendance < ActiveRecord::Base
attr_accessible :code, :email
belongs_to :user
validates_presence_of :user
end
So far I'm only trying to enforce the association between User and Attendance, once I get that working I'll do the same to Events. Also, this is Rails 3.2.19 and Ruby 1.9.3.
EDIT: Here is the code I'm using for the form, I believe that it works because until I put the validation into the model it was creating rows into the Attendance table.
<% provide(:title, 'Test Event') %>
<h1>Attendance Registration</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#attendance) do |f| %>
<%= render 'shared/attendance_error_messages' %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :code %>
<%= f.text_field :code %>
<%= f.submit "Submit", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
Also, here's the attendance controller, if that helps.
class AttendancesController < ApplicationController
def new
#attendance = Attendance.new
end
def create
#attendance = Attendance.new(params[:attendance])
if #attendance.save
flash[:success] = "Attendance logged."
redirect_to root_path
else
render 'new'
end
end
end
Add this line to your form to avoid the user presence error
<%= f.hidden_field :user_id, current_user.id %>
I'm attempting to build a recipe-keeper app with three primary models:
Recipe - The recipe for a particular dish
Ingredient - A list of ingredients, validated on uniqueness
Quantity - A join table between Ingredient and Recipe that also reflects the amount of a particular ingredient required for a particular recipe.
I'm using a nested form (see below) that I constructed using an awesome Railscast on Nested Forms (Part 1, Part 2) for inspiration. (My form is in some ways more complex than the tutorial due to the needs of this particular schema, but I was able to make it work in a similar fashion.)
However, when my form is submitted, any and all ingredients listed are created anew—and if the ingredient already exists in the DB, it fails the uniqueness validation and prevents the recipe from being created. Total drag.
So my question is: Is there a way to submit this form so that if an ingredient exists whose name matches one of my ingredient-name fields, it references the existing ingredient instead of attempting to create a new one with the same name?
Code specifics below...
In Recipe.rb:
class Recipe < ActiveRecord::Base
attr_accessible :name, :description, :directions, :quantities_attributes,
:ingredient_attributes
has_many :quantities, dependent: :destroy
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, allow_destroy: true
In Quantity.rb:
class Quantity < ActiveRecord::Base
attr_accessible :recipe_id, :ingredient_id, :amount, :ingredient_attributes
belongs_to :recipe
belongs_to :ingredient
accepts_nested_attributes_for :ingredient
And in Ingredient.rb:
class Ingredient < ActiveRecord::Base
attr_accessible :name
validates :name, :uniqueness => { :case_sensitive => false }
has_many :quantities
has_many :recipes, through: :quantities
Here's my nested form that displays at Recipe#new:
<%= form_for #recipe do |f| %>
<%= render 'recipe_form_errors' %>
<%= f.label :name %><br>
<%= f.text_field :name %><br>
<h3>Ingredients</h3>
<div id='ingredients'>
<%= f.fields_for :quantities do |ff| %>
<div class='ingredient_fields'>
<%= ff.fields_for :ingredient_attributes do |fff| %>
<%= fff.label :name %>
<%= fff.text_field :name %>
<% end %>
<%= ff.label :amount %>
<%= ff.text_field :amount, size: "10" %>
<%= ff.hidden_field :_destroy %>
<%= link_to_function "remove", "remove_fields(this)" %><br>
</div>
<% end %>
<%= link_to 'Add ingredient', "new_ingredient_button", id: 'new_ingredient' %>
</div><br>
<%= f.label :description %><br>
<%= f.text_area :description, rows: 4, columns: 100 %><br>
<%= f.label :directions %><br>
<%= f.text_area :directions, rows: 4, columns: 100 %><br>
<%= f.submit %>
<% end %>
The link_to and link_to_function are there to allow the addition and removal of quantity/ingredient pairs on the fly, and were adapted from the Railscast mentioned earlier. They could use some refactoring, but work more or less as they should.
Update: Per Leger's request, here's the relevant code from recipes_controller.rb. In the Recipes#new route, 3.times { #recipe.quantities.build } sets up three blank quantity/ingredient pairs for any given recipe; these can be removed or added to on the fly using the "Add ingredient" and "remove" links mentioned above.
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
3.times { #recipe.quantities.build }
#quantity = Quantity.new
end
def create
#recipe = Recipe.new(params[:recipe])
if #recipe.save
redirect_to #recipe
else
render :action => 'new'
end
end
You shouldn't put the logic of ingredients match into view - it's duty of Recipe#create to create proper objects before passing 'em to Model. Pls share the relevant code for controller
Few notes before coming to code:
I use Rails4#ruby2.0, but tried to write Rails3-compatible code.
attr_acessible was deprecated in Rails 4, so strong parameters are used instead. If you ever think to upgrade your app, just go with strong parameters from the beginning.
Recommend to make Ingredient low-cased to provide uniform appearance on top of case-insensitivity
OK, here we go:
Remove attr_accessible string in Recipe.rb, Quantity.rb and Ingredient.rb.
Case-insensitive, low-cased Ingredient.rb:
class Ingredient < ActiveRecord::Base
before_save { self.name.downcase! } # to simplify search and unified view
validates :name, :uniqueness => { :case_sensitive => false }
has_many :quantities
has_many :recipes, through: :quantities
end
<div id='ingredients'> part of adjusted form to create/update Recipe:
<%= f.fields_for :quantities do |ff| %>
<div class='ingredient_fields'>
<%= ff.fields_for :ingredient do |fff| %>
<%= fff.label :name %>
<%= fff.text_field :name, size: "10" %>
<% end %>
...
</div>
<% end %>
<%= link_to 'Add ingredient', "new_ingredient_button", id: 'new_ingredient' %>
We should use :ingredient from Quantity nested_attributes and Rails will add up _attributes-part while creating params-hash for further mass assignment. It allows to use same form in both new and update actions. For this part works properly association should be defined in advance. See adjusted Recipe#new bellow.
and finally recipes_controller.rb:
def new
#recipe = Recipe.new
3.times do
#recipe.quantities.build #initialize recipe -> quantities association
#recipe.quantities.last.build_ingredient #initialize quantities -> ingredient association
end
end
def create
#recipe = Recipe.new(recipe_params)
prepare_recipe
if #recipe.save ... #now all saved in proper way
end
def update
#recipe = Recipe.find(params[:id])
#recipe.attributes = recipe_params
prepare_recipe
if #recipe.save ... #now all saved in proper way
end
private
def prepare_recipe
#recipe.quantities.each do |quantity|
# do case-insensitive search via 'where' and building SQL-request
if ingredient = Ingredient.where('LOWER(name) = ?', quantity.ingredient.name.downcase).first
quantity.ingredient_id = quantity.ingredient.id = ingredient.id
end
end
end
def recipe_params
params.require(:recipe).permit(
:name,
:description,
:directions,
:quantities_attributes => [
:id,
:amount,
:_destroy,
:ingredient_attributes => [
#:id commented bc we pick 'id' for existing ingredients manually and for new we create it
:name
]])
end
In prepare_recipe we do the following things:
Find ID of ingredient with given name
Set foreign_key quantity.ingredient_id to ID
Set quantity.ingredient.id to ID (think what happens if you don't do that and change ingredient name in Recipe)
Enjoy!
I am currently working on a nested model form.
I have a subject model.
This subject model has lessons of 3 different types - tutorial, lecture and laboratory.
I am able to get the nested form working with https://github.com/ryanb/nested_form.
But I want to fix it such that in the form only 3 forms for the child(lesson model) will be produced and that their first field (lesson_type field) will be automatically filled in and fixed.
I am not too sure on how to model such a situation on Rails.
These are the codes I have so far.
Any advice on what I could try out or point out the mistakes I have made would be appreciated.
This is the form.
Right now I could get the form to show up three times on my controller but I am not sure how I could generate different values for the fields. They are all showing lecture as of now.
<%= nested_form_for(#subject, :remote=>true) do |f| %>
<div class="field">
<%= f.label :subject_code %><br />
<%= f.text_field :subject_code %>
</div>
<%= f.fields_for :lessons do |lesson_form| %>
<%= lesson_form.label :lesson_type %><br/>
<%= lesson_form.text_field :lesson_type, :value=> "lecture"%><br/>
<%= lesson_form.label :name %><br/>
<%= lesson_form.text_field :name %><br/>
<%= lesson_form.fields_for :lesson_groups do |lesson_group_form| %>
<%= lesson_group_form.label :group_index %><br/>
<%= lesson_group_form.text_field :group_index %>
<%= lesson_group_form.link_to_remove "Remove this task" %>
<% end %>
<p><%= lesson_form.link_to_add "Add a lesson_group",:lesson_groups,:id=>"open-lesson"%></p>
<% end %>
<% end %>
This is the controller. The creation will happen on the index page.
def index
#subjects = Subject.all
#subject = Subject.new
lecture = #subject.lessons.build
lecture.lesson_groups.build
lecture.destroy
tutorial = #subject.lessons.build
tutorial.lesson_groups.build
tutorial.destroy
laboratory = #subject.lessons.build
laboratory.lesson_groups.build
laboratory.destroy
respond_to do |format|
format.html # index.html.erb
format.json { render json: #subjects }
format.js
end
end
The subject model
class Subject < ActiveRecord::Base
attr_accessible :subject_code, :lessons_attributes
has_many :lessons, :dependent => :destroy
accepts_nested_attributes_for :lessons, :allow_destroy => :true, :reject_if => lambda { |a| a[:lesson_type].blank? }
end
And the lesson model
class Lesson < ActiveRecord::Base
belongs_to :subject
attr_accessible :lesson_type, :name, :subject, :lesson_groups_attributes
has_many :lesson_groups, :dependent => :destroy
accepts_nested_attributes_for :lesson_groups, :allow_destroy => true
end
Okay, I am not sure if this is to the Rails convention but I got it working according to what I want. Added the following lines in the subject model: Basically assigning the lesson type field in the model.
lecture = #subject.lessons.build
lecture.lesson_type = "lecture"
lecture.lesson_groups.build
lecture.destroy
tutorial = #subject.lessons.build
tutorial.lesson_type = "tutorial"
tutorial.lesson_groups.build
tutorial.destroy
laboratory = #subject.lessons.build
laboratory.lesson_type = "laboratory"
laboratory.lesson_groups.build
laboratory.destroy
And to make it such that they can't change the lesson type I made it read only
<%= lesson_form.text_field :lesson_type, :readonly=>true%><br/>
I am working on a Rails app, wherein I have two models, i.e. a chef model and a dish model.
class Dish < ActiveRecord::Base
belongs_to :chef
attr_accessible :description, :photo, :price
validates :chef_id, presence: true
has_attached_file :photo
end
class Chef < ActiveRecord::Base
attr_accessible :name, :email, :mobile ,:password, :password_confirmation, :postcode
has_many :dishes
has_secure_password
end
I (chef) am trying to create to a dish by going to the /upload url, whose view is
<%= form_for(#dish) do |d| %>
<%= d.label :description, "Please name your dish..."%>
<%= d.text_field(:description)%>
<%= d.label :price, "What should the price of the dish be..."%>
<%= d.number_field(:price)%>
<%= d.submit "Submit this Dish", class: "btn btn-large btn-primary"%>
<% end %>
I want the created dish to appear on the show page of the chef,
<% provide(:title, #chef.name)%>
<div class = "row">
<aside class = "span4">
<h1><%= #chef.name %></h1>
<h2><%= #chef.dishes%></h2>
</aside>
<div>
<% end %>
And, dishes_controller is:
class DishesController < ApplicationController
def create
#dish = chef.dishes.build(params[:dish])
if #dish.save
redirect_to chef_path(#chef)
else
render 'static_pages/home'
end
But as soon as I try to create a dish from the /upload url, I get the following error in the dishes_controller:
NameError undefined local variable or method `chef' for #<DishesController:0x3465494>
app/controllers/dishes_controller.rb:5:in `create'
I think I have instantiated all variables, but the problem persists.
In this line:
#dish = chef.dishes.build(params[:dish])
The chef variable is not instantiated. You have to do something like this:
#chef = Chef.find(params[:chef_id])
#dish = #chef.dishes.build(params[:dish])
This way the #chef variable is populated before you use it.