Проблема
Забавная проблема.Забавно, потому что это выглядит непринужденно, пока вы не начнете думать об этом.Допустим, я позволяю людям создавать предметы на основе других предметов.Вы можете открыть /items/new?id=3
и, в отличие от вашего обычного нового действия, вместо просмотра пустой формы вы увидите форму, предварительно заполненную значениями из элемента-3.Таким образом, в отличие от вашего нового действия по умолчанию, мое выглядит следующим образом:
def new
@item = params[:id] ? Item.find(params[:id]) : Item.new
end
Таким образом, вы можете получить доступ к действию с URL-адресом, подобным /items/new?id=1
, и форма будет предварительно заполнена данными из элемента-1.
Пока все хорошо.
Теперь вы отправляете эти данные.Мое действие создания довольно стандартно.
def create
@item = Item.new(params[:item])
if @item.save
# happy
else
# sad
end
end
Здесь есть только одна проблема.Новый элемент, который был только что создан (назовем его item-2), должен знать, кто этот папа.В нашем случае это папка item-1 (поскольку исходная форма была предварительно заполнена данными из item-1), и поэтому item-2 должен иметь parent_id, установленный в 1. Только это не просто хорошоиметь.Он должен быть твердым, невозможным вмешиваться.Вы не должны иметь возможность подделать parent_id.
Поэтому мой вопрос - как я могу убедиться, что parent_id установлен правильно при создании нового элемента на основе существующего элемента?
Некоторая помощь будет принята с благодарностью, но если вы не думаете, что это простая случайная проблема, вот несколько решений, о которых вы, вероятно, думаете.
Non-solution # 1
Таким образом, обычное решение для сохранения parent_id - это просто использовать его как скрытое поле в форме.
<% form_for @item do |f| %>
...
<%= f.hidden_field :parent_id, :value => params[:id] %>
...
<% end %>
Но здесь я могу легко переключить его в своем html.Когда форма отправлена, сервер не имеет ни малейшего представления.Сервер просто «поверит», что элемент был создан с родителем [что угодно], когда он должен был быть родительским 1.
Non-solution # 2
На сервере я мог бы использоватьсеанс, чтобы сохранить, какой элемент вы редактируете.Тогда мое новое действие будет выглядеть следующим образом.
def new
if params[:id]
@item = Item.find(params[:id])
session[:creating_item] = params[:id].to_i
else
@item = Item.new
session[:creating_item] = :mine # this prevents loophole if session is cleared
end
end
И мое действие создания примет это значение и установит его в parent_id.
def create
@item = Item.new(params[:item])
if session[:creating_item] == :mine
@item.parent_id = nil
elsif session[:creating_item].is_a?(Fixnum)
@item.parent_id = session[:creating_item]
else
# session was probably cleared
redirect_to root_url and return
end
if @item.save
# happy
else
# sad
end
end
Это не будет работать, если вы откроете /items/new
несколько раз (например, в нескольких вкладках в браузере).Значение сеанса будет перезаписываться каждый раз.Поэтому, если вы отправляете форму, открытую в /items/new?id=1
, но вы также открыли /items/new?id=2
, то parent_id в сеансе будет равен 2. Неудачно.
Не решение # 3
Что если вы сохранитевсе открытые родители в массиве в сеансе, вместо того, чтобы просто перезаписывать одно значение.Ну, я не собираюсь выписывать длинные примеры, я просто скажу, что при отправке любой из открытых форм - вы будете знать, что это должен быть один из родителей, которых вы сохранили в сеансе (например, 1, 2 или 3), но вы все еще не знаете, какой.Затем вы можете сохранить данные из родительского 1, как если бы они были из родительского 3.