Рельсы: Предотвратите повторные вставки из-за нажатия кнопки назад и сохраните снова - PullRequest
8 голосов
/ 11 января 2011

Подумайте о простом приложении Rails scaffold с «новым» действием, содержащим форму для добавления записей в базу данных с помощью кнопки «сохранить». После действия «create» контроллер перенаправляет на действие «show», где пользователь может использовать ссылку «edit» для редактирования только что вставленной записи. Пока все просто.

Но если пользователь вместо этого использует кнопку «Назад» браузера после создания записи, чтобы вернуться к «новому» действию, браузер отображает форму со значениями, которые пользователь только что ввел. Теперь он меняет некоторые значения и снова нажимает «сохранить». Он думает, что это изменит запись, но, конечно, это создаст новую запись.

Каков предпочтительный способ предотвращения повторяющихся записей? Я ищу общее решение, возможно, на основе файлов cookie или JavaScript.

Ответы [ 8 ]

7 голосов
/ 13 января 2011

После некоторых исследований я нашел подходящее решение на основе файлов cookie.Вот оно:

В «новом» действии контроллера создается метка времени с текущим временем, которая отображается в форме как скрытое поле.Когда пользователь отправляет форму, эта метка времени возвращается к действию контроллера «создать».После создания записи эта метка времени сохраняется в файле cookie сеанса.Если пользователь возвращается к «новой» форме с помощью кнопки «Назад» в браузере, он получает устаревшую форму, что означает, что его метка времени старше, чем та, которая хранится в куки.Это проверяется перед созданием записи и выдает сообщение об ошибке.

Вот код контроллера:

def new
  @post = Post.new
  @stale_form_check_timestamp = Time.now.to_i
end

def create
  @post = Post.new(params[:post])

  if session[:last_created_at].to_i > params[:timestamp].to_i
    flash[:error] = 'This form is stale!'
    render 'new'
  else
    @post.save!
    @stale_form_check_timestamp = Time.now.to_i
    session[:last_created_at] = @stale_form_check_timestamp
  end
end

А вот код формы:

- form_for @post do |f|
  = tag :input, :type => 'hidden', :name => 'timestamp', :value => @stale_form_check_timestamp
  = f.input :some_field
  = .......
4 голосов
/ 06 июля 2013

Когда у меня возникла та же проблема, я создал этот маленький драгоценный камень, который решает ее. Когда пользователь наносит ответный удар, он перенаправляется на edit_path записи, а не на new_path.

https://github.com/yossi-shasho/redirect_on_back

Вы можете сделать что-то вроде:

def create
  @user = User.new(params[:user])
  if result = @user.save
    redirect_on_back_to edit_user_path(@user) # If user hits 'back' he'll be redirected to edit_user_path
    redirect_to @user
  end
end
2 голосов
/ 24 августа 2012

Сеанс или cookie могут привести к побочным эффектам.

Я полностью согласен: если есть способ проверки с вашей моделью, это самый безопасный способ предотвратить дублирование записей.

Тем не менее вымогу сделать 2 вещи.Запретить кеширование в браузере: поля будут пустыми в форме, когда пользователь нажимает кнопку возврата .И отключите кнопку «Создать» при нажатии.

= f.submit "Create", :disable_with => "Processing..."

Когда пользователь нажмет кнопку «Назад», кнопка будет отключена.

2 голосов
/ 11 января 2011

Проверка вашей модели обеспечит уникальность таких вещей, как адреса электронной почты, но я думаю, что это больше касается удобства и простоты использования, чем что-либо еще.

Скажем, вы говорите о форме создания учетной записи.Прежде всего, кнопка отправки формы должна содержать что-то вроде «Создать учетную запись» , а не просто «Отправить».Затем в зависимости от того, был ли он успешным или нет, покажите сообщение, например «Учетная запись успешно создана» или «При создании учетной записи возникли ошибки».Если пользователь увидит это сообщение, он узнает, что произошло.

Конечно, вы не можете помешать кому-либо нажать кнопку «Назад» и снова нажать «Enter», но вы должны разрабатывать для большинства случаев использования.Если им случится нанести ответный удар, они увидят кнопку с надписью «Создать учетную запись».Вероятно, у вас должен быть какой-то другой текст на странице с надписью «Пожалуйста, зарегистрируйтесь, чтобы начать новую учетную запись».

Только мои 0,02 доллара.

0 голосов
/ 27 июня 2019

Добавьте html: { autocomplete: "off" } в ваш form_for, как это:

<%= form_for @object, url: xxx_path, html: { autocomplete: "off" } do |f| %>
0 голосов
/ 06 октября 2015

Вот кое-что, что сработало для меня.

Вам нужно будет сделать 2 вещи: создать метод в вашем контроллере и добавить условный оператор в тот же контроллер под вашим методом 'create'.

1) Ваш метод должен вернуть общее количество этого объекта от этого пользователя.

EX:

def user current_user.object.count конец

2) Добавьте условный оператор в ваш метод create.

Пример:

def create @object = Object.create (object_params) @ object.save если пользователь == 0 redirect_to x_path конец

Надеюсь, это поможет!

0 голосов
/ 14 апреля 2015

на основе ответа @Georg Ledermann. Я делаю этот небольшой фрагмент кода для перенаправления на путь редактирования, если пользователь нажимает кнопку «Назад», а затем нажимает «Создать».

#objects_controller.rb
def new
    @object = Object.new
    @stale_form_check = Time.now.to_i
end

def create
    @object = Object.new(object_params)
    #function defined in application_controller.rb
    redirect_to_on_back_and_create(@object)
end

#application_controller.rb
private
def redirect_to_on_back_and_create(object)
    if session[:last_stale].present? and session[:last_stale_id].present? and session[:last_stale].to_i == params[:stale_form_check].to_i 
        redirect_to edit_polymorphic_path(object.class.find(session[:last_stale_id].to_i)), alert: "Este #{object.model_name.human} ya ha sido creado, puedes editarlo a continuación"
    else 
        if object.save
            session[:last_stale] = params[:stale_form_check].to_i
            session[:last_stale_id] = object.id
            redirect_to object, notice: "#{object.model_name.human} Creado con éxito"
        else
            render :new 
        end
    end
end

Инаконец, добавьте параметр @stale_form_check к вашей форме

<%= hidden_field_tag :stale_form_check, @stale_form_check %>

Вы всегда можете абстрагировать этот метод там, где он вам нужен, но таким образом вы можете избежать большого количества повторений в вашем проекте, если вам нужно такое поведение во многих частях

Надеюсь, что это поможет в следующем, я использовал гем redirect_on_back, но на этот раз он не работал для меня, параметр _usec, который использует этот гем, всегда сбрасывался, поэтому он не может сравниватьсяв каждый раз, когда это было необходимо

0 голосов
/ 11 января 2011

Вы можете использовать валидаторы, чтобы убедиться, что никакие повторяющиеся значения не вставлены.В этом случае validates_uniqueness_of :field

Если вы, например, хотите запретить пользователям использовать один и тот же адрес электронной почты, вы можете добавить следующий код в вашу модель пользователя.

validates_uniqueness_of :email

Это проверяет столбецдля любых предыдущих записей, которые совпадают с той, которую вы пытаетесь инертировать.Удачи

...