#ruby-on-rails
#ruby-on-rails
Вопрос:
Я запускаю before_filter для некоторых действий, чтобы проверить, является ли пользователь current_user .
before_filter :correct_user, :only => [:edit, :update, :destroy]
def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
redirect_to current_user, :notice => "User updated!"
else
redirect_to current_user, :notice => "User not updated. waa waa."
end
end
private
def correct_user
if current_user != @user
redirect_to root_url, :notice => "Cannot act on different user."
end
end
Не уверен, что это лучший способ что-то сделать, но это работает (может быть, лучше просто использовать current_user вместо поиска @user через параметры?)
Теперь у пользователя есть_many photos, и в моем представлении индекса фотографий я перечисляю все фотографии пользователя и разрешаю пользователю устанавливать любую фотографию в качестве фотографии профиля. В таблице user есть столбец с именем primary_photo_id для хранения этого идентификатора, и я использую link_to для его установки:
=link_to "Make this your profile photo", user_path(@user, :user => {:primary_photo_id => "#{photo.id}"}), :method => :put
Проблема в том, что before_filter запускается и не позволяет этому работать, потому что @user, который извлекается через params[:id], завершается с ошибкой, потому что это неправильные параметры. Если я удалю before_filter, он будет работать нормально, но тогда он больше не проверяет правильность пользователя.
(второй несколько связанный вопрос заключается в том, почему приведенный выше код работает, но этот:
=link_to "Make this your profile photo", user_path(@user, :primary_photo_id => "#{photo.id}"), :method => :put
не работает.
Спасибо. Я довольно новичок в rails и программировании, поэтому все, что вы можете сказать по моему конкретному вопросу, и любые плохие практики, которые я делаю с кодом здесь, очень ценятся.
Ответ №1:
Фильтр before ( #correct_user
) выполняется раньше #update
, поэтому ваша переменная экземпляра @user
еще не установлена, когда вы сравниваете ее в фильтре, если вы сначала не установили ее в другом фильтре before. Ваша последовательность выполнения выглядит так:
- выполнить
#correct_user
— сравнить current_user с@user
(если не задано, это равно нулю). я предполагаю, что они будут совпадать только в том случае, если пользователь не вошел в систему - предполагая, что мы справились, запустите #update и посмотрите
@user
Вероятно, самый простой способ решить вашу проблему — просто переместить @user
поиск в фильтр before:
before_filter :correct_user, :only => [:edit, :update, :destroy]
def edit
# .. as before, but no need to look up user first
end
def update
if @user.update_attributes(params[:user])
redirect_to current_user, :notice => "User updated!"
else
redirect_to current_user, :notice => "User not updated. waa waa."
end
end
def destroy
# .. as before, but no need to look up user first
end
private
def correct_user
@user = User.find(params[:id])
if current_user != @user
redirect_to root_url, :notice => "Cannot act on different user."
end
end
Поскольку @user
теперь находится в фильтре, нет необходимости искать его снова в каждом из ваших действий контроллера. Надеюсь, это поможет!
Комментарии:
1. Это отлично сработало. Спасибо, Мэтт! На самом деле я решил использовать два before_filters, первый для get_user, другой для проверки правильного пользователя. для меня читается немного понятнее, хотя он, вероятно, менее «сухой». Вместо того, чтобы использовать user = User.find(params[:id]), я мог бы также использовать user = current_user (поскольку есть вспомогательный метод для захвата пользователя сеанса).. Рекомендуется ли это вообще, особенно, скажем, для редактирования, обновления и других вещей, которые действительно не должны быть выполнимы кем-либо еще, кроме зарегистрированного пользователя?
2. Да, часто для такого рода условий я настраиваю свой фильтр before, чтобы он определял, является ли пользователь администратором, и если да, то разрешит настройку из параметров, в противном случае жестко подключенных к зарегистрированному пользователю. Что-то вроде
@user = current_user.admin? ? User.find(params[:id]) : current_user
. Если у вас нет регистра администратора, я бы просто установил его в current_user напрямую, поскольку у вас, вероятно, уже есть экземпляр пользовательского объекта, нет смысла выполнять другой поиск.