web-dev-qa-db-fra.com

Dans Rails 3, lorsqu'une action de création de ressource échoue et appelle render: new, pourquoi l'URL doit-elle changer pour l'URL d'index de la ressource?

J'ai une ressource appelée Livres. Il est répertorié comme une ressource correctement dans mon fichier de routes.

J'ai une nouvelle action, qui donne à la nouvelle vue la norme:

@book = Book.new

Sur le modèle, certains attributs sont validés par la présence, donc si une action de sauvegarde échoue, des erreurs seront générées.

Dans mon contrôleur:

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

C'est assez standard; et la justification de l'utilisation de render: new est pour que l'objet soit renvoyé à la vue et que des erreurs puissent être signalées, des entrées de formulaire remplies à nouveau, etc.

Cela fonctionne, sauf que chaque fois que je suis renvoyé au formulaire (via render: new), mes erreurs apparaissent, mais mon URL est l'URL INDEX, qui est

/books

Plutôt que

/books/new

C'est là que j'ai commencé en premier lieu. J'ai vu plusieurs autres articles sur ce problème, mais aucune réponse. Au minimum, on pourrait supposer que cela vous amènerait à/books/create, pour lequel j'ai également un fichier de vue (identique à nouveau dans ce cas).

Je peux le faire:

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

Mais alors les données @book sont perdues, ainsi que les messages d'erreur, ce qui est tout l'intérêt d'avoir le formulaire et les actions, etc.

Pourquoi est-ce que render: new m'arrive dans/books, mon action d'index, alors que normalement cette URL appelle la méthode INDEX, qui répertorie tous les livres?

57
rcd

En fait, est vous envoie vers le chemin de création. C'est dans l'action create, dont le chemin est /books, en utilisant la méthode HTTP POST. Cela ressemble au chemin d'index /books, mais le chemin d'index utilise la méthode HTTP GET. Le code de routage Rails prend en compte la méthode lors de la détermination de l'action à appeler. Après l'échec de la validation, vous êtes toujours dans l'action de création, mais vous rendez le new C'est un peu déroutant, mais une ligne comme render :new n'invoque pas du tout la nouvelle action; il exécute toujours l'action de création et il indique Rails pour rendre le nouveau voir.

27
Jim Stewart

Je viens de commencer avec le Rails-Tutorial et j'ai eu le même problème. La solution est simple: si vous voulez la même URL après avoir soumis un formulaire (avec des erreurs), combinez simplement la nouvelle et créez l'action en une seule action.

Voici la partie de mon code qui rend cela possible (j'espère que ça aide quelqu'un ^^)

routes.rb (Ajout de la post-route pour une nouvelle action):

...
    resources :books
    post "books/new"
...

Manette:

...
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

new.html.erb:

<%= 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 %>
8
ofhouse

Je viens de poser la même question, alors peut-être que cela pourrait aider quelqu'un un jour. Vous devez essentiellement faire 3 ajustements pour que cette chose fonctionne, bien que ma solution ne soit toujours pas idéale.

1) Dans l'action de création:

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

2) Dans la nouvelle action:

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

3) Où que vous analysiez le hachage du flash, assurez-vous de filtrer le flash [: livre].

-> l'URL correcte s'affiche, les données du formulaire sont conservées. Pourtant, je n'aime pas du tout mettre l'objet utilisateur dans le hachage flash, je ne pense pas que ce soit son but. Quel garçon connaît-il un meilleur endroit pour le mettre?

4
panepeter

Il ne vous atterrit pas à /books/new puisque vous créez une ressource en publiant sur /books/. Lorsque votre création échoue, elle rend simplement la nouvelle action, pas vous redirige vers la nouvelle action. Comme @MrYoshiji le dit ci-dessus, vous pouvez essayer de le rediriger vers la nouvelle action, mais cela est vraiment inefficace car vous créeriez une autre demande HTTP et un aller-retour vers le serveur, uniquement pour changer l'URL. À ce stade, si cela est important, vous pouvez probablement utiliser javascript pour le modifier.

2
Doon

Il peut être corrigé en utilisant la même URL mais différentes méthodes pour une nouvelle action et créer une action.

Dans le fichier routes, le code suivant peut être utilisé.

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

Maintenant, votre nouvelle page s'affichera à l'URL

livres/common_path_string

Si des erreurs surviennent après la validation, l'URL sera toujours la même.

Également dans le formulaire à la place en utilisant

books_path

utilisation

url: common_path_string_books_path, method: :post

Choisissez common_path_string à votre convenance.

1
Bot