STI и form_for проблемы
Я использую Single Table Inheritance для управления различными типами проектов.
Модели:
class Project < ActiveRecord::Base
end
class SiteDesign < Project
end
class TechDesign < Project
end
Изменить действие из project_controller:
def edit
@project = Project.find(params[:id])
end
Просмотреть edit.html.erb:
<% form_for(@project, :url => {:controller => "projects",:action => "update"}) do |f| %>
...
<%= submit_tag 'Update' %>
<% end %>
Обновить действие project_controller:
def update
@project = Project.find(params[:id])
respond_to do |format|
if @project.update_attributes(params[:project])
@project.type = params[:project][:type]
@project.save
flash[:notice] = 'Project was successfully updated.'
format.html { redirect_to(@project) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
end
end
end
Затем я делаю некоторые изменения в записи TechDesign в режиме редактирования и получаю ошибку:
NoMethodError in ProjectsController#update
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
В параметрах очевидно, что вместо имени параметра проекта у меня есть tech_design
Параметры:
{"commit"=>"Update",
"_method"=>"put",
"authenticity_token"=>"pd9Mf7VBw+dv9MGWphe6BYwGDRJHEJ1x0RrG9hzirs8=",
"id"=>"15",
"tech_design"=>{"name"=>"ech",
"concept"=>"efds",
"type"=>"TechDesign",
"client_id"=>"41",
"description"=>"tech"}}
Как это исправить?
Ответы
Ответ 1
Вот источник вашей проблемы. Это устанавливает @project как экземпляр объекта TechDesign.
def edit
@project = Project.find(params[:id])
end
Вы можете гарантировать, что все работает так, как вы хотите, указав: проект для имени в форме для вызова.
<% form_for(:project, @project, :url => {:controller => "projects",:action => "update"}) do |f| %>
...
<%= submit_tag 'Update' %>
<% end %>
Ответ 2
Для Rails 3
<% form_for(@project, :as => :project, :url => {:controller => "projects",:action => "update"}) do |f| %>
...
<%= submit_tag 'Update' %>
<% end %>
Ответ 3
Случайное примечание. Если вы используете однонаправленное наследование таблицы (STI) и забыли удалить метод инициализации из своих определений подкласса, вы получите аналогичное исключение "нулевой объект, когда вы этого не ожидали".
Например:
class Parent < ActiveRecord::Base
end
class Child < Parent
def initialize
# you MUST call *super* here or get rid of the initialize block
end
end
В моем случае я использовал свою IDE для создания дочерних классов, а среда IDE создала метод инициализации. Принял меня навсегда, чтобы отследить...
Ответ 4
Для Rails 4 я подтвердил, что единственное, что, казалось, работает для меня, - это явно указать URL-адрес и параметры AS:
<% form_for(@project, as: :project, url: {controller: :projects, action: :update}) do |f| %>
...
<% end %>
Кажется уродливым для меня!