我有一些不可靠的代码,我试图看看我是否可以改进,用户可以回答分配给他们的问题。通过迭代分配给它们的问题来填充答案文本字段,代码如下所示:
<%= form_for answer do |f| %>
<% current_user.all_weekly_questions.each do |question| %>
<%= hidden_field_tag 'questions[][id]', question.id %>
<h4><%= question.name %></h4>
<% if question.text_based? %>
<%= text_area_tag 'questions[][reply]', nil, class: 'form-control', "required" => true, rows: 2 %>
<% else %>
<% (question.min_number_range..question.max_number_range).each do |question_value| %>
<%= label_tag "questions[][reply]", class: 'question-value-label' do %>
<%= question_value %>
<%= radio_button_tag "questions[][reply]", "#{question_value}" %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= f.submit 'Send' %>
<% end %>
现在已经在这里我看到了一些问题,因为我无法真正验证他们是否回答了所有问题,因为我只是迭代它们并填写答案表。
现在,在我的控制器中,我有:
def new
@user = User.eager_load(:questions, :group_questions)
.find(current_user.id)
@user_answered_weekly_questions =
current_user.answers.includes(user: [:answers])
.detect { |a| a.week_number == @current_week.to_s }
.present?
@answer = current_user.answers.new
@current_week = Time.zone.now.strftime('%V')
end
def create
@questions = current_user.questions
params[:questions].map do |question|
current_user.answers.create(
question_id: question[:id],
reply: question[:reply],
week_number: Time.zone.now.strftime('%V')
)
end
redirect_to answers_path
end
在这里我还发现了一些问题,这些问题在创建中。我确实没有条件来检查它是否已保存。因此它成了问题。
现在我遇到的最后一个问题是,由于我有两个基于数字的问题填写表单的性质,我生成一组radio_button_tag
,它有无法说出他们是不同的群体 - 因此用户无法回答这两个问题。
现在我想要改进/修复的是:
questions[][reply]
架构:
create_table "questions", force: :cascade do |t|
t.string "name"
t.integer "company_id"
t.boolean "optional", default: false
t.boolean "active", default: true
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "frequency", default: 0
t.integer "answer_format", default: 0
t.integer "min_number_range"
t.integer "max_number_range"
end
create_table "answers", force: :cascade do |t|
t.integer "user_id"
t.text "reply"
t.integer "question_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "week_number"
end
答案 0 :(得分:0)
我首先回顾一下您的域名模型 - 您显然缺少一个将一堆问题组合在一起的组件。
我们还需要在任何Q / A或轮询应用中处理一个非常明显的问题。我们需要将表格和用户反馈的问题和答案分开。
# == Schema Information
#
# Table name: questionnaires
#
# id :integer not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
# week_number :integer
#
class Questionnaire < ActiveRecord::Base
has_many :questions
has_many :answers, through: :questions
has_many :user_questionnaires, class_name: 'Users::Questionnaire'
scope :this_week, -> { where(week_number: Time.zone.now.strftime('%V'))
end
# == Schema Information
#
# Table name: answers
#
# id :integer not null, primary key
# question_id :integer
# body :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Answer < ActiveRecord::Base
belongs_to :question
end
# == Schema Information
#
# Table name: questions
#
# id :integer not null, primary key
# questionnaire_id :integer
# body :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Question < ActiveRecord::Base
belongs_to :questionnaire
has_many :answers
accepts_nested_attributes_for :answers
end
这提供了一个非常自然的层次结构 - 问卷调查有许多问题,其中有许多潜在的答案。
这些可以由管理员在管理GUI上创建。例如。
在你的域建模中,你的问答模型中有一些元数据 - 我会仔细考虑这些模型是否真的需要知道的不仅仅是它们的价值/文本以及它们立即属于谁。如果您需要添加数据 - 将其添加到问卷中,这样您就不会在表格中填充重复数据。
我们还没有完成建模。在处理用户回复的问题上,您可能希望为此目的创建单独的模型,而不是膨胀问题/答案模型:
# == Schema Information
#
# Table name: users_answers
#
# id :integer not null, primary key
# users_questionnaire_id :integer
# question_id :integer
# answer_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Users::Reply < ActiveRecord::Base
belongs_to :users_questionnaire, class_name: 'Users::Questionnaire',
inverse_of: :users_replies
belongs_to :question
belongs_to :answer
end
# == Schema Information
#
# Table name: users_questionnaires
#
# id :integer not null, primary key
# user_id :integer
# questionnaire_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Users::Questionnaire < ActiveRecord::Base
belongs_to :user
belongs_to :questionnaire
has_many :users_replies, class_name: 'Users::Reply'
delegate :questions, to: :questionnaire
accepts_nested_attributes_for :users_replies
end
class Users::QuestionnairesController < ApplicationController
def index
@questionnaires = current_user.questionnaires
end
def new
@questionnaire = current_user.questionnaires.new(
# We need to tell ruby to use the "root" namespace
questionnaire: ::Questionnaire.this_week.first!
)
# @todo - handle case where no weekly questionnaire is found
end
def create
@questionnaire = current_user.questionnaires.new(create_params)
if @questionnaire.save
redirect_to :index
else
render :new
end
end
private
def create_params
params.require(:questionnaire)
.permit(
:questionnaire_id,
users_replies: [:question_id, :answer_id]
)
end
end
控制器非常简单 - 我们在Users::Questionaire
内存中创建#new
,并接受用户输入并在Users::Questionaire
中保留#create
和相关模型。
我们可以使用fields_for
:
<%= form_for @questionnaire do |f| %>
<% f.object.questions.each do |q| %>
<fieldset>
<%= f.fields_for :users_answers, Users::Answer.new(question_id: q.id) do |a| %>
<%= a.hidden_field(:question_id) %>
<%= a.collection_radio_buttons(:answer_id, q.answers, :id, :body) %>
<% end %>
<legend><%= q.body %></legend>
</fieldset>
<% end %>
<%= f.submit %>
<% end %>
class Users::Questionnaire < ActiveRecord::Base
belongs_to :user
belongs_to :questionnaire
has_many :users_answers, class_name: 'Users::Answer'
delegate :questions, to: :questionnaire
accepts_nested_attributes_for :users_answers
validate :number_of_answers
def number_of_answers
unless users_answers.size == questions.size
errors[:users_answers] << "You have not answered all the questions"
end
end
end
这是验证用户输入的简化版本。要创建更复杂的验证,例如,如果您想为每个缺失的答案添加错误或确保答案有效,最好的方法是创建custom validator class。只是不要陷入在控制器中做所有事情的陷阱。
我忘了提到您需要在用户和Users::Questionnaire
class User < ActiveRecord::Base
# ...
has_many :questionnaires, class_name: 'Users::Questionnaire'
end
设置路线非常简单:
namespace :users
resources :questionnaires, only: [:new, :create]
end
为控制器使用命名空间是可选的,它实际上取决于您想要的应用程序路径。