В Rails 3, когда действие создания ресурса завершается неудачей и вызывает render:new, почему URL-адрес должен измениться на url-адрес индекса ресурса?


у меня есть ресурс под названием книги. Он указан как ресурс правильно в моем файле маршрутов.

у меня есть новое действие, которое дает новый вид стандарта:

@book = Book.new

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

в моем контроллере:

@book = Book.create
...  # some logic
if @book.save
  redirect_to(@book)
else
  render :new
end

Это довольно стандартно; и обоснование использования render: new заключается в том, что объект передается обратно вид и ошибки могут быть представлены, записи в форме повторного заполнения, и т. д.

это работает, за исключением того, что каждый раз, когда я отправляюсь обратно в форму (через render :new), мои ошибки появляются, но мой URL-адрес является URL-адресом индекса, который

/books

, а не

/books/new

где я начал в первую очередь. Я видел несколько других сообщений об этой проблеме, но никаких ответов. Как минимум, можно было бы предположить, что это приведет вас в /books / create, у которого также есть файл представления for (идентично новому в данном случае).

Я могу сделать это:

# if the book isn't saved then
flash[:error] = "Errors!"
redirect_to new_book_path

но тогда данные @book теряются вместе с сообщениями об ошибках, что является целым моментом наличия формы и действий и т. д.

почему render: new landing me at /books, мое действие индекса, когда обычно этот URL вызывает метод индекса, в котором перечислены все книги?

6 53

6 ответов:

Это на самом деле и отправка в путь создания. Это в create действие, путь к которому /books, используя HTTP метод POST. Это выглядит так же, как путь индекса /books, но путь к индексу использует метод HTTP GET. Код маршрутизации rails учитывает метод при определении того, какое действие вызывать. После сбоя проверки вы все еще находитесь в действии create, но вы визуализируете new вид. Это немного запутанно, но строка, как render :new на самом деле не вызывает новое действие вообще; он все еще запускает действие create и сообщает Rails для отображения нового посмотреть.

Я только начал с Рельсы-Учебник и была та же проблема. Решение просто: если вы хотите тот же URL-адрес после отправки формы (с ошибками), просто объедините новое и создайте действие в одном действии.

вот часть моего кода, которая делает это возможным (надеюсь, это поможет кому-то^^)

маршруты.rb (добавление пост-маршрута для new-action):

...
    resources :books
    post "books/new"
...
:
...
def create
    @book = Book.new(book_params)

    if @book.save
        # save was successful
        print "Book saved!"

        else
        # If we have errors render the form again   
        render 'new'
    end
end

def new 
    if book_params
        # If data submitted already by the form we call the create method
        create
        return
    end

    @book = Book.new

    render 'new' # call it explicit
end

private

def book_params
    if params[:book].nil?  || params[:book].empty?
        return false
    else
        return params.require(:book).permit(:title, :isbn, :price)
    end
end

новый.формат html.Эрб:

<%= form_for @book, :url => {:action => :new} do |f| %>
  <%= f.label :title %>
  <%= f.text_field :title %>

  <%= f.label :isbn %>
  <%= f.text_field :isbn %>

  <%= f.label :price %>
  <%= f.password_field :price %>

  <%= f.submit "Save book" %>
<% end %>

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

1) в действии создать:

if @book.save
  redirect_to(@book)
else
  flash[:book] = @book
  redirect_to new_book_path
end

2) в новом действии:

@book = flash[:book] ? Book.new(flash[:book]): Book.new

3) везде, где вы анализируете флэш-хэш, не забудьте отфильтровать flash[:book].

--> отображается правильный URL, данные формы сохраняются. Все-таки мне как-то не нравится помещая объект пользователя во флэш-хэш, я не думаю, что это его цель. Кто-нибудь знает лучшее место, чтобы положить его?

не на /books/new Так как вы создаете ресурс, разместив на /books/. Когда ваше создание терпит неудачу, оно просто отображает новое действие, а не перенаправляет вас на новое действие. Как @MrYoshiji говорит выше, вы можете попробовать перенаправить его на новое действие, но это действительно неэффективно, поскольку вы создадите еще один HTTP-запрос и туда и обратно на сервер, только чтобы изменить url. В этот момент, если это имеет значение, вы, вероятно, можете использовать javascript, чтобы изменить его.

это может быть исправлено с помощью того же url, но разные методы для новых и создать действие.

в файле маршрутов можно использовать следующий код.

resources :books do
  get :common_path_string, on: :collection, action: :new
  post :common_path_string, on: :collection, action: :create
end

Теперь вы новая страница будет отображаться по url

книги/common_path_string

в случае возникновения каких-либо ошибок после проверки, все равно url будет таким же.

также в форме вместо использования

books_path

использовать

url: common_path_string_books_path, method: :post

выбрать common_path_string своему вкусу.

вот мое решение. Не уверен, что это успокоительно, и Рейли и все остальное (как я еще новичок), но это работает, по крайней мере, пока:

# routes.rb
post 'posts/new', to: 'posts#create', as: 'post_create'
resources :posts

# posts_controller.rb
def new
end

def create
  if Post.save?
    redirect_to posts_path
  else
    render action: 'new'
end

# new.html.erb
<%= form_with model: Post.new, url: :post_create, method: :post, local: true do |f| %>
<%= f.submit 'Submit' %>

обновление

несмотря на то, что решение технически работает, учитывая эту цель create действие творения (и в большинстве случаев оно будет положительным), я собираюсь рассмотреть этот:

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

отсюда статьи