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
Related
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
I have two models/tabels: room and room_location, that have a one on one relation:
class Room < ApplicationRecord
has_one :room_location
end
class RoomLocation < ApplicationRecord
belongs_to :room
end
And this is what i want to do in my form in rooms.rb:
ActiveAdmin.register Room do
menu parent: 'Rooms'
permit_params :name, :description, room_location_attributes: [:address, :city]
form do |f|
f.inputs 'Roomsdata' do
f.input :name, as: :string
f.input :description
f.has_one :room_location do |t|
t.inputs do
t.address
t.city
end
end
f.actions
end
end
end
The has_one doesnt work and if i do has_many, it says relation "room_locations" does not exist
You should write in the params room_location_id instead of attributes
ActiveAdmin.register Room do
menu parent: 'Rooms'
permit_params :name, :description, room_location_id
form do |f|
address_id = ''
cs = params[:id].present? ? Case.find(params[:id]) : nil
unless cs.nil?
address_id = cs.address.id unless cs.address.nil?
end
f.inputs 'Roomsdata' do
f.input :name, as: :string
f.input :description
f.input :room_location_id , :as => :select, :collection => room_location.order(address: :desc).to_a.uniq(&:address).collect {|room_location| [room_location.address, room_location.id] }, selected: room_location_id
f.input :room_location_id , :as => :select, :collection => room_location.order(city: :desc).to_a.uniq(&:city).collect {|room_location| [room_location.address, room_location.id] }, selected: room_location_id
f.actions
end
end
end
I have something like this:
class Actor < ActiveRecord::Base
attr_accessible :name, :actor_movie_relationships_attributes
validates :name
has_many :actor_movie_relationships, autosave: true
has_many :movies, through: :actor_movie_relationships
accepts_nested_attributes_for :actor_movie_relationships
end
And:
class ActorMovieRelationship < ActiveRecord::Base
belongs_to :movie
belongs_to :actor
validates :movie, :actor, presence: true
end
Since Actor can have many movies, I'd like to paginate the ActiveAdmin form that is used to administer these relationships.
Currently I have this form (which is NOT paginated):
form do |f|
tabs do
tab 'Actor' do
f.inputs 'Basic Information' do
f.semantic_errors *f.object.errors.keys
f.input :name
f.input :deleted
end
end
tab 'Movies' do
f.inputs 'List of movies' do
f.has_many :actor_movie_relationships, heading: '', new_record: 'Add a Movie', allow_destroy: true do |a|
a.input :Movie, collection: Movie.dropdown_names_map.call
end
end
end
end
f.actions
end
Any idea how to add pagination to Movies in this form?
My Rails application was working just fine, when suddenly I started getting the following error: Collection is not a paginated scope. Set collection.page(params[:page]).per(10) before calling :paginated_collection.
Here's the code of my Course model:
# app/models/course.rb
class Course < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
has_many :steps
has_many :subscriptions
has_many :users, through: :subscriptions
has_many :reviews
validates :name, presence: true, length: { maximum: 50 }
validates :content, presence: true, length: { maximum: 500 }
validates :price, presence: true, numericality: { only_integer: true }
has_attached_file :image, :styles => { :medium => "680x300>", :thumb => "128x118>" }
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
def average_rating
reviews.blank? ? 0 : reviews.average(:star).round(2)
end
def price_in_cents
price * 100
end
end
And here's the code in app/admin/course.rb:
ActiveAdmin.register Course do
permit_params :name, :content, :price, :image, :slug
show do |t|
attributes_table do
row :name
row :content
row :price
row :slug
row :image do
course.image? ? image_tag(course.image.url, height: '100') : content_tag(:span, "No photo yet")
end
end
end
form :html => { :enctype => "multipart/form-data" } do |f|
f.inputs do
f.input :name
f.input :content
f.input :price
f.input :slug
f.input :image, hint: f.course.image? ? image_tag(course.image.url, height: '100') : content_tag(:span, "Upload JPG/PNG/GIF image")
end
f.actions
end
end
What could be the cause for this error?
I found the problem.
In my ApplicationController I had declared a variable that was conflicting with an existing variable in my CourseController, so I fixed it by changing the variable name.
I have a nested form in ActiveAdmin for these models (a :class_section has_many :class_dates):
class ClassDate < ActiveRecord::Base
belongs_to :class_section
validates :start_time, :presence => true
validates :end_time, :presence => true
end
and
class ClassSection < ActiveRecord::Base
belongs_to :class_course
has_many :class_dates
belongs_to :location
accepts_nested_attributes_for :class_dates
end
Everything seems to be in the right place when I look at the form. However, when I update a class_date, it doesn't save.
ActiveAdmin.register ClassSection do
permit_params :max_students, :min_students, :info, :class_course_id, :location_id
form do |f|
f.inputs "Details" do
f.input :class_course, member_label: :id_num
f.input :min_students, label: "Minunum Students"
f.input :max_students, label: "Maxiumum Students"
f.input :location
end
f.inputs do
f.input :info, label: "Additional Information"
end
f.inputs "Dates" do
f.has_many :class_dates, heading: false do |cd|
cd.input :start_time, :as => :datetime_picker
cd.input :end_time, :as => :datetime_picker
end
end
f.actions
end
index do
column :class_course
column :location
default_actions
end
filter :class_course
filter :location
show do |cs|
attributes_table do
row :class_course do
cs.class_course.id_num + " - " + cs.class_course.name
end
row :location
row :min_students, label: "Minunum Students"
row :max_students, label: "Maxiumum Students"
row :info, label: "Additional Information"
end
panel "Dates" do
attributes_table_for class_section.class_dates do
rows :start_time, :end_time
end
end
active_admin_comments
end
end
Here is the ActiveAdmin file for ClassDates:
ActiveAdmin.register ClassDate, as: "Dates" do
permit_params :start_time, :end_time, :class_section_id
belongs_to :class_section
end
Can you see a reason why it's not saving properly?
UPDATE: I added the following code to the AA and it seems to work now:
controller do
def permitted_params
params.permit!
end
end
Let me know if there is a better solution. Thanks.
UPDATE 2: There is one lingering problem however. I am unable to delete ClassDates using this form.
You need to permit nested parameters, but you should never use params.permit!. It's extremely unsafe. Try this:
ActiveAdmin.register ClassSection do
permit_params :max_students, :min_students, :info, :class_course_id, :location_id,
class_dates_attributes: [ :id, :start_time, :end_time, :_destroy ]
form do |f|
# ...
f.inputs "Dates" do
f.has_many :class_dates, heading: false, allow_destroy: true do |cd|
cd.input :start_time, :as => :datetime_picker
cd.input :end_time, :as => :datetime_picker
end
end
f.actions
end
# ...
end
The configuration (and permitted_params) of your ClassDate admin panel has nothing to do with the permitted parameters within the ClassSection admin panel. Treat them as separate controllers within the app.
Adding the allow_destroy: true option to the has_many call will add a checkbox to the form to allow you to delete a class time upon form submission.