Rails 4 - select related records - ruby-on-rails

On my application, I have an object for many-to-many relationships called "selection"
class Selection < ActiveRecord::Base
belongs_to :question
belongs_to :projecttype
end
and 2 objects to relate to each other
class Projecttype < ActiveRecord::Base
has_many :selections
has_many :questions, :through => :selections
validates :projecttype, uniqueness: true, length: { is: 3 }
validates :name, presence: true
mount_uploader :image, ImageUploader
end
And...
class Question < ActiveRecord::Base
has_many :selections
has_many :projecttypes, :through => :selections
validates :question, presence: true
validates :comment, presence: true, length: { minimum: 5}
validates :sequence, presence: true
end
Now in my edit form for the projecttype, I want to be able to select related questions (using check boxes).
<div class="row">
<div class="col-md-2"><%= f.label :selection %></div>
<div class="col-md-4"><%= f.select :selection, {}, {:multiple => true, :style => "width:100%; border:none" } %></div>
</div>
Have tried options_for_selection and _from_collection and don't anymore know what to do as I get many errors.
what should the f.select code be?

I'm a little rusty with Rails now, but it should be something like this.
<%= f.select :selection, Selection.all.collect {|x| x.name}, {}, :multiple => true %>
and don't forget to add
def post_params
params.require(:post).permit(:title, :body, category_ids: [])
end
to your selection.rb file

Related

How to create a form object with multiple input fields with the same name

I am learning rails and will be very grateful for an advice about creating a form object with multiple input fields with the same name. I am having errors with unpermitted params.
I've been searching information for several days, have tried a lot without any result. This is my first question, sorry if it is not easy to understand.
I am using rails 6.0.0, and having 3 models: Recipe, Ingredient, RecipeIngredientRelation. I want to make multiple input fields for Ingredient and RecipesIngredient. Errors:
Parameters: {"authenticity_token"=>"G6vwDdptMBgl2SPvLfS4BoiCjoRnf+FGYuKW0Ykt3Aodt5HfBEx5KHWBEpl4r52ANCv9QWz13rDyAP42qCyESg==", "recipes_ingredient"=>{"name"=>"Recipe", "portions"=>"1", "time_count_id"=>"3", "calories"=>"", "ingredients"=>{"i_name"=>"water"}, "recipe_ingredient_relations"=>{"quantity"=>["100"], "measurement_id"=>"3"}, "recipe"=>"some text", "tips"=>"", "is_public"=>"0"}, "commit"=>"レシピを投稿する"}
Unpermitted parameters: :ingredients, :recipe_ingredient_relations
binding.pry gets me #recipes_ingredient.errors:
#details={:i_name=>[{:error=>:blank}]},
#messages={:i_name=>["can't be blank"]}>
my models are:
Recipe
has_many :recipe_ingredient_relations
has_many :ingredients, through: :recipe_ingredient_relations
accepts_nested_attributes_for :ingredients, :recipe_ingredient_relations, allow_destroy: true
Ingredient
has_many :recipe_ingredient_relations
has_many :recipes, through: :recipe_ingredient_relations
RecipeIngredientRelation
belongs_to :recipe
belongs_to :ingredient
Form Object
class RecipesIngredient
include ActiveModel::Model
attr_accessor :ingredients, :i_name, :recipe_ingredient_relations, :quantity, :measurement_id, :name, :image, :portions, :time_count_id, :recipe, :tips, :calories, :is_public, :user_id
with_options presence: true do
validates :i_name #, uniqueness: true
validates :quantity, length:{maximum: 10}, allow_blank: true
validates :measurement_id, allow_blank: true
validates :name
validates :portions
validates :time_count_id, numericality: {other_than: 1}
validates :recipe
validates :tips, allow_blank: true
validates :calories, allow_blank: true
validates :is_public
end
def save
recipe = Recipe.create(user_id: user_id, name: name, image: image, portions: portions, time_count_id: time_count_id, recipe: recipe, tips: tips, calories: calories, is_public: is_public)
ingredient = Ingredient.create(i_name: i_name)
recipe_ingredient_relation = RecipeIngredientRelation.create(recipe_id: recipe.id, ingredient_id: ingredient.id, quantity: quantity, measurement_id: measurement_id)
end
end
part of input form view:
%= form_with(model: #recipes_ingredient, url:recipes_path, local: true) do |form| %>
<...>
<%= form.fields_for :ingredients do |f| %>
<%= f.text_field :i_name, placeholder: "例)レタス", class: "form-el ingredient-input required" %>
<% end %>
<%= form.fields_for :recipe_ingredient_relations do |f| %>
<div class="quantity">
<%= f.text_field :quantity, multiple: true, placeholder: "例)100", class: "form-el quantity-input required" %>
<%= f.collection_select(:measurement_id, Measurement.all, :id, :name, {}, {class:"after-input-required"}) %>
<p class="after-input after-input-btn-required">+</p>
</div>
</div>
<% end %>
and, finally, the Recipes controller:
class RecipesController < ApplicationController
def index
#recipe = Recipe.all
end
def new
#recipes_ingredient = RecipesIngredient.new
end
def create
#recipes_ingredient = RecipesIngredient.new(recipe_params)
binding.pry
# if #recipe.valid?
# #recipe.save
# redirect_to root_path
# else
# render action: :new
# end
end
private
def recipe_params
params.require(:recipes_ingredient).permit(:name, :image, :portions, :time_count_id, :recipe, :tips, :calories, :is_public, ingredients_attributes: [:id, :i_name], recipe_ingredient_relations_attributes: [:id, :quantity, :measurement_id] ).merge(user_id: current_user.id)
end
end

Rails 5.2 ActiveAdmin deep nested objects

I have this scheme: Course->Segments->Questions.
Course has many segments and segments has many questions.
I added segment_type attribute to segment model so I can show/hide question model depends on the type (video or quiz).
I managed to write the form in courses.rb and I got access to questions through course.
I'm having a problem with showing question attributes in show page even when I searched 3-4 hours on google for answer. Here is a screenshot of the show page
I wrote accept_nested_attribute in all the models so this is not the problem. I can edit, create and delete courses, segments and questions, it's just I can't see in the show the data relevant to questions.
EDIT: I know that the relations works fine in rails console and I have data in questions (it suppose to display the last segment's questions)
these are my models:
class Course < ApplicationRecord
validates :title ,presence: true
validates :author ,presence: true
has_many :segments, inverse_of: :course, :dependent => :destroy, :autosave => true
accepts_nested_attributes_for :segments, :allow_destroy => true
end
class Segment < ApplicationRecord
validates :course_id ,presence: true
validates :data ,presence: true, if: :segment_is_video?
validates :segment_type ,presence: true
validates_presence_of :course
belongs_to :course
has_many :questions, inverse_of: :segments, :dependent => :destroy, :autosave => true
accepts_nested_attributes_for :questions, :allow_destroy => true
def self.segment_type
{"Video": "Video", "Quiz": "Quiz"}
end
def segment_is_video?
segment_type == 'Video'
end
end
class Question < ApplicationRecord
validates :question ,presence: true
validates :answer1 ,presence: true
validates :answer2 ,presence: true
validates :answer3 ,presence: true
validates :answer4 ,presence: true
validates :correct ,presence: true
validates_presence_of :segment
belongs_to :segment
end
this is courses.rb:
ActiveAdmin.register Course do
permit_params :id, :title, :autor,
segments_attributes: [:id, :unit_id, :unit_title, :name, :segment_type, :data, :_destroy, :_create, :_update, questions_attributes: [:id, :question, :answer1, :answer2, :answer3, :answer4, :correct, :_destroy, :_create, :_update]]
config.sort_order = 'id_asc'
member_action :segments do
#segments = resource.segments
end
index do
column :id
column :title
column :author
actions
end
show do
panel "Course Segments" do
h3 "Videos"
table_for resource.segments.where('segment_type = "Video"') do
column :id
column :name
column :data
end
# Here it's only displaying id and name values, but the others no.
h3 "Quizes"
table_for resource.segments.where('segment_type = "Quiz"') do
column :id
column :name
column :question
column :answer1
column :answer2
column :answer3
column :answer4
column :correct
end
end
active_admin_comments
end
sidebar "Course Details", only: :show do
attributes_table_for course do
row :title
row :author
end
end
form do |f|
tabs do
tab 'Basic Info' do
f.inputs "Course details" do
f.input :title
f.input :author
end
end
tab 'Content' do
f.inputs "Segments" do
f.has_many :segments, heading: false, allow_destroy: true do |s|
s.input :unit_id
s.input :unit_title
s.input :name
s.input :segment_type, as: :select, collection: Segment.segment_type, input_html: { class: 'select-type' }
s.input :data, label: 'Url', :as => :string, input_html: { class: 'video-tab' }
s.has_many :questions, heading: false, allow_destroy: true do |q|
q.input :question, input_html: { class: 'quiz-tab' }
q.input :answer1, input_html: { class: 'quiz-tab' }
q.input :answer2, input_html: { class: 'quiz-tab' }
q.input :answer3, input_html: { class: 'quiz-tab' }
q.input :answer4, input_html: { class: 'quiz-tab' }
q.input :correct, input_html: { class: 'quiz-tab' }, as: :select, collection: [1,2,3,4]
end
end
end
end
end
f.actions
end
end

form validation with nested models in Rails

I have this problem. I need to validate the attributes of two models in the same form in Rails. One is the parent of the other.
The form is like this:
<%= semantic_form_for #professional do |pro| %>
<%= pro.inputs :id => "information" do %>
<%= pro.input :name, label: t("Artistic Name") %>
<%= pro.semantic_fields_for #user do |user| %>
<%= user.inputs :id => "register" do %>
<%= user.input :email, :placeholder=>"email#example.com" %>
<%= user.input :password, label: t('Password') %>
<%end%>
<% end %>
<% end %>
<% end %>
The models I am using are like this:
User:
class User < ActiveRecord::Base
belongs_to :role, polymorphic: true
validates :email, :password, presence: true
end
Professionals:
class Professional < ActiveRecord::Base
has_one :user, as: :role, dependent: :destroy
accepts_nested_attributes_for :user
validates :date_birthday, :gender, :height, :name, :description, :Weight, :address, :languages,:services, :category, :phonenumber, :fullname, :hair_color, :age, :orientation, presence: true
end
So, what is the problem?
When I clicked in the submit button the professional attributes are marked but not the users attributes.
Like this:
The fields marked in red belongs to the professional model but the fields email and password belongs to the user model aren't marked in red when it should be because they are empty.
What can i do? I need the warning message for the user is attributes too
Thanks in advances.
We've achieved what you need before.
We had to use inverse_of so that the object was a singular piece of data (rather than multiple pieces as is the case by default):
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :role, polymorphic: true, inverse_of: :user
validates :email, :password, presence: true
end
#app/models/professional.rb
class Professional < ActiveRecord::Base
has_one :user, as: :role, dependent: :destroy, inverse_of: :role
accepts_nested_attributes_for :user
end
This will help.
You also need to make sure you're passing these objects correctly (I see so many people not doing this).
You need to tell Professional to validate the associated User:
class Professional < ActiveRecord::Base
...
validates_associated :user

Rails: has_many, through not showing in view/index

I'm writing a simple time tracking app where the user has_many clients and where the client has_many projects.
I want a user to be able to view a list of their projects (for all clients). To implement this, I've set up a has_many, through relationship between the users and projects.
But for some reason I can't get the projects to show up in their index view. There's probably a really simple reason for this that I'm just not noticing, so apologies upfront if that's the case.
Here's the relevant code.
Project index controller:
def index
#projects = current_user.projects.paginate(:page => params[:page], :per_page => 6)
end
Project model:
class Project < ActiveRecord::Base
belongs_to :client
validates :name, presence: true, length: { maximum: 30 }
validates :fee, presence: true, numericality: { only_integer: true,
greater_than_or_equal_to: 0, less_than_or_equal_to: 100000 }
validates :client_id, presence: true
end
Client model:
class Client < ActiveRecord::Base
belongs_to :user
has_many :projects, dependent: :destroy
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 30 }
validate :user_id_is_valid
private
def user_id_is_valid
errors.add(:user_id, "is invalid") unless User.exists?(self.user_id)
end
end
Relevant part of the User model:
class User < ActiveRecord::Base
has_many :clients, dependent: :destroy
has_many :projects, through: :clients
index.html.erb:
<div id="projects-list">
<% if current_user.projects.any? %>
<h3>Projects</h3>
<ul class="project-list">
<% render #projects %>
</ul>
<%= will_paginate #projects %>
<% end %>
</div>
_project.html.erb:
<li>
<%= link_to "#{project.name}", '#' %>
</li>
It should be:
<%= render #projects %>
Not:
<% render #projects %>

Rails nested form not saving children

My User model has_many Responses. I'm trying to create a nested form to create a new user with three child responses. My code looks identical to the Rails cast, but although it will save the user it does not save their responses. Can anyone see what is wrong?
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
3.times{#user.responses.build}
#responses = #user.responses
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user #todo: where do we want to redirect?
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#responses = #user.responses
end
def index
#users = User.all
end
private
def user_params
params.require(:user).permit(:username, :email, :responses)
end
end
user.rb
class User < ActiveRecord::Base
attr_accessor :responses_attributes
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
before_save { self.email = email.downcase }
validates :username, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } ,
uniqueness: {case_sensitive: false};
validates :responses, presence: true
end
response.rb
class Response < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id, :content
end
users/new.html.erb (the form)
<h1>This is a test form for adding Users with child Responses</h1>
<%= form_for #user do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :email %>
<%= f.text_field :email %>
<p>
<%= f.fields_for :responses do |builder| %>
<%= builder.label :content, "Response" %>
<%= builder.text_field :content %>
<% end %>
</p>
<%= f.submit "Submit" %>
<% end %>
Edit:
I have change the strong parameters in the Users controller to:
def user_params
params.require(:user).permit(:username, :email, responses_attributes: [:content])
end
And I have updated the User model to:
class User < ActiveRecord::Base
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
before_save { self.email = email.downcase }
validates :username, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } ,
uniqueness: {case_sensitive: false};
validates_associated :responses, presence: true
end
Now it continues to fail my validations for the responses, and also fails my validations for username and email.
The problem is in your Response class:
validates_presence_of :user_id, :content
This validation requires the existence of user_id, which means the User has to be created before Response. To make it work you can try couple of things:
Change validates_presence_of :user_id to validates_presence_of :user to make it validate the User object instead of the user's id.
Use inverse_of option. See the Rails API doc.
Updated code.
user.rb
class User < ActiveRecord::Base
attr_accessible :responses_attributes
has_many :responses, :dependent => :destroy, :inverse_of => :user
accepts_nested_attributes_for :responses
# ...
end
response.rb
class Response < ActiveRecord::Base
belongs_to :user, :inverse_of => :responses
validates_presence_of :user, :content
end
Answer for RoR v3:
The problem is the following line in your User model:
attr_accessor :responses_attributes
Replace it with:
attr_accessible :responses_attributes
The answer to question Difference between attr_accessor and attr_accessible should help you understand the difference between the two.
Also your validation validates :responses, presence: true needs to be updated to use validates_associated:
validates_associated :responses, presence: true
Answer for RoR v4:
I hadn't noticed the use of Strong parameters ealier, apologies for this. A couple of changes should fix your issue:
Remove attr_accessor :responses_attributes from User model.
Replace validates :responses, presence: true with validates_associated :responses, presence: true
Define responses attributes in permit list.
Something like follows:
class User < ActiveRecord::Base
# attr_accessor :responses_attributes # Remove this line
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
...
validates_associated :responses, presence: true # Update here
end
#Users Controller
class UsersController < ApplicationController
...
private
def user_params
params.require(:user).permit(:username, :email, :responses_attributes[:content])
end
end
Your code looks almost correct:
user.rb
class User < ActiveRecord::Base
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses
before_save { self.email = email.downcase }
validates :username, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } ,
uniqueness: {case_sensitive: false};
end
users_controller.rb
private
def user_params
params.require(:user).permit(:username, :email, responses_attributes: [:content])
end

Resources