Rails 3 - nil 的未定义方法“map”:NilClass 用于我自己的验证

2024-01-25

我遇到了很奇怪的问题。我有Timetable模型并尝试编写我的自定义验证。所以,现在我只是尝试为字段添加测试错误以确保一切正常。但这不起作用。因此,我尝试更新时间表模型的对象,但是当我不使用测试自定义验证时,一切都很完美。否则我会得到这样的错误:

NoMethodError in Timetables#update 

undefined method `map' for nil:NilClass

32: 
33:       <div class="controls">
34:         <%= f.select( :curriculum_id,                                                           
35:                       options_for_select( @subjects_with_curriculums,
36:                                           @tt.curriculum_id ),
37:                       { :include_blank => true }) %>
38:       </div>

这是我的模型:

# == Schema Information
#
# Table name: timetables
#
#  id                  :integer         not null, primary key
#  curriculum_id       :integer
#  school_class_id     :integer
#  tt_day_of_week      :string(255)
#  tt_number_of_lesson :integer
#  tt_room             :string(255)
#  tt_type             :string(255)
#  created_at          :datetime        not null
#  updated_at          :datetime        not null
#

class Timetable < ActiveRecord::Base
  belongs_to :curriculum
  belongs_to :school_class

  has_many :lessons

  validates :school_class_id, :presence => { :message => "should exist" }

  validates :tt_day_of_week,
              :presence  => true,
              :inclusion => { :in => %w(Mon Tue Wed Thu Fri) }

  validates :tt_number_of_lesson,
              :presence => true,
              :inclusion => {
                              :in => 1..9,
                              :message => "should have 1..9 symbols"
                            }

  validates :tt_room,
              :length => {
                           :maximum => 3,
                           :message => "should have 3 symbols"
                         },
              :allow_blank => true

  validates :tt_type,
              :inclusion => { :in => ["Primary lesson", "Extra"] },
              :allow_blank => true

  validate :test

  def test
    errors.add(:tt_number_of_lesson, "test")
  end
end 

我的控制器:

# encoding: UTF-8
class TimetablesController < ApplicationController
  ...

  def edit
    @types_of_lesson = collect_types_of_lesson
    @tt = Timetable.find( params[:id] )
    @subjects_with_curriculums = collect_subjects_with_curriculums( @tt.school_class )
  end

  def update
    @tt = Timetable.find( params[:id] )

    if @tt.update_attributes( params[:timetable] )
      flash[:success] = "Расписание успешно обновлено!"
      redirect_to timetables_path
    else
      flash.now[:error] = @tt.errors.full_messages.to_sentence :last_word_connector => ", ",
                                                               :two_words_connector => ", "
      render 'edit'
    end
  end

  private
    # Collecting subjects names for school class and curriculum_id for each subject.
    def collect_subjects_with_curriculums( school_class )
      subjects = school_class.curriculums.collect do |c|
        [ c.qualification.subject.subject_name, c.id  ]
      end
    end

    def timetable_for_class_with_existance_data( school_class )
      return [] if Timetable.all.empty?

      Timetable.select do |t|
        ( t.school_class.class_code == school_class.class_code ) and
        not ( t.tt_room.blank? ) and not ( t.tt_type.blank? ) and
        not ( t.curriculum_id.nil? )
      end.to_a
    end

    # Return for school class it's timetable.
    def timetable_for_class( school_class )
      Timetable.select{|t| t.school_class.class_code == school_class.class_code }.to_a
    end

    def subjects_of_class( school_class )
      subjects = school_class.curriculums.collect do |c|
        c.qualification.subject.subject_name
      end
    end

    # Return sorted by number of lesson tometable for one day.
    def sorted_timetable_for_day( timetable, day )
      timetable.select{ |t| t.tt_day_of_week == day }
               .sort_by{ |e| e[:tt_number_of_lesson] }
    end

    # Return russian name for type of lesson.
    def collect_types_of_lesson
      [ ["Обязательно занятие", "Primary lesson"], ["Электив", "Extra"] ]
    end

    # Check if timetable already has been created for school class.
    def timetable_exists?( school_class )
      not timetable_for_class( school_class ).empty?
    end
end

My view

<%= form_for @tt, :html => {:class => "form-horizontal"} do |f| %>
  <%= field_set_tag do %>

    <%= f.hidden_field :tt_number_of_lesson %>
    <%= f.hidden_field :tt_day_of_week %>
    <%= f.hidden_field :school_class_id %>

    <div class="control-group">
       <%= f.label :tt_day_of_week, "Day of the week", :class => "control-label" %>

       <div class="controls">
         <%= content_tag( :span, translate_day_of_week( @tt.tt_day_of_week ),
                          :class =>"input-xlarge uneditable-input span2" ) %>
       </div>
    </div>

    <div class="control-group">
       <%= f.label :tt_number_of_lesson, "Number of lesson", :class => "control-label" %>

       <div class="controls">
         <%= content_tag( :span, @tt.tt_number_of_lesson,
                          :class =>"input-xlarge uneditable-input span1" ) %>
       </div>
    </div>

    <hr/>

    <div class="control-group">
      <%= f.label :curriculum_id, "Type of subject", :class => "control-label" %>

      <div class="controls">
        <%= f.select( :curriculum_id,                                                           
                      options_for_select( @subjects_with_curriculums,
                                          @tt.curriculum_id ),
                      { :include_blank => true }) %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :tt_room, "Code of the room", :class => "control-label" %>

      <div class="controls">
        <%= f.text_field :tt_room, :class => "span2", :maxlength => 3 %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label :tt_type, "Type of the lesson", :class => "control-label" %>

      <div class="controls">
        <%= f.select( :tt_type,                                                           
                      options_for_select( @types_of_lesson,
                                          @tt.tt_type ),
                      { :include_blank => true }) %>
      </div>
    </div>

    <%= f.submit "Update", :class => "btn btn-large btn-warning" %>
  <% end %>
<% end %>  

当我删除时:

<div class="control-group">
  <%= f.label :curriculum_id, "Type of subject", :class => "control-label" %>

  <div class="controls">
    <%= f.select( :curriculum_id,                                                           
                  options_for_select( @subjects_with_curriculums,
                                      @tt.curriculum_id ),
                  { :include_blank => true }) %>
  </div>
</div>

<div class="control-group">
  <%= f.label :tt_type, "Type of the lesson", :class => "control-label" %>

  <div class="controls">
    <%= f.select( :tt_type,                                                           
                  options_for_select( @types_of_lesson,
                                      @tt.tt_type ),
                  { :include_blank => true }) %>
  </div>
</div>

我可以查看我的测试错误。我不明白发生了什么事。


你在你的update控制器:

render 'edit'

这只是告诉 Rails 渲染编辑模板,它不会运行任何与edit控制器,它只是渲染edit.html.erb在当前的背景下。

来自Rails 中的布局和渲染指南 http://guides.rubyonrails.org/layouts_and_rendering.html#rendering-an-action-s-view:

2.2.2 渲染动作视图
[一些关于render 'x', render :x, and render :action => :x是一样的……]

Using render with :action对于 Rails 新手来说,这是一个常见的困惑源。指定的操作用于确定要渲染哪个视图,但 Rails 不会not在控制器中运行该操作的任何代码。视图中需要的任何实例变量必须在调用之前在当前操作中设置render.

特别注意最后一句:

视图中需要的任何实例变量必须在调用之前在当前操作中设置render.

你正在设置@subjects_with_curriculums in the edit控制器但使用edit视图从update控制器。结果是@subjects_with_curriculums is nil when edit.html.erb尝试使用它时update尝试渲染编辑视图。

你需要设置@subjects_with_curriculums在你的update控制器,您可能需要@types_of_lesson以及。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Rails 3 - nil 的未定义方法“map”:NilClass 用于我自己的验证 的相关文章

随机推荐