Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: how to redirect to "show" action of nested resource in a "create" action?

Hi I currently have 3 models: user, album, photo. I've made registration, but not login/sessions yet, since I am trying to have the album/photo creation finished first. However, when I create an album the URL changes from

http://localhost:3000/users/13/albums/new (no problem)

to

http://localhost:3000/albums/76/photos/76 (problem)

the issues here are: 1. the user_id disappears from the url. 2. it's sending me to the wrong url. As you can see below, I am redirecting to [@user, @album]. Isn't that supposed to be the show action of the albums controller? I wanted something like /users/13/albums/1 as the url. Instead, it's sending me to photos#show. 3. even though it's the wrong link, why are the album id and the photo id are always the same? 76/76 77/77 etc... I don't know where that is coming from..

here are my files:

album controller

def show
  @user = User.find(params[:user_id])
  @album = @user.albums.find(params[:id])
  @photo = @album.photos.build(params[:photo])
  respond_to do |format|
    if @album.save
      format.html { redirect_to album_photo_path(@album), notice: 'Album was successfully created.' }
      format.json { render json: @album, status: :created, location: @album}
    else
      format.html { render action: "new" }
      format.json { render json: @album.errors, status: :unprocessable_entity }
    end
  end
end

def update
end

def edit
end

def create
  @user = User.find(params[:user_id])
  @album = @user.albums.build(params[:album])
  respond_to do |format|
    if @user.save
      format.html { redirect_to [@user, @album], notice: 'Album was successfully created.' }
      format.json { render json: @album, status: :created, location: @album}
    else
      format.html { render action: "new" }
      format.json { render json: @album.errors, status: :unprocessable_entity }
    end
  end 
end

albums/new.html.erb

<%= form_for (@album), url: user_albums_path, :html => { :id => "uploadform", :multipart => true } do |f| %>



<div>
    <%= f.label :name %>
    <%= f.text_field :name %>


    <%= f.label :description %>
    <%= f.text_area :description %>

    <br>

    <%=f.submit %>
</div>
<% end %>

config/routes

Pholder::Application.routes.draw do
resources :users do
  resources :albums 
end

resources :albums do
  resources :photos
end

let me know if you need anymore files.

like image 732
bigpotato Avatar asked Oct 02 '12 20:10

bigpotato


1 Answers

Here's the series of requests that your use case runs through:

First, the form submits some data to your Album#create action.

After the Album#create request succeeds, the request is redirected to the Albums#show action.

In the Albums#show action, the controller instantiates @user and @album with your current User and the new Album, then builds a new Photo object within the Album's photos collection.

The action then saves the Album record again. Since nothing has changed, this doesn't actually do anything. But it does do nothing successfully, so it proceeds to redirect the request to album_photo_path, and this is where things go pear-shaped. This route, as defined, leads to a Photo#show action, in the context of the photo's parent Album. The route expects two arguments, an Album and a Photo. As invoked in your code, it shouldn't work—throwing a Routing Error exception instead. (Believe me, I tried.)

Instead of getting hung up on minutae like that, though, I'm going to make a blind recommendation instead!

It sounds like you didn't intend for your Album#show action to save the album and redirect to Photo#show via a malformed route. I think this is the source of all three of your issues. And, ultimately, I think your AlbumsController's show action needs to look like this:

def show
  @user = User.find(params[:user_id])
  @album = @user.albums.find(params[:id])
end

Hope this helps!


Edited to address Edmund's comment:

Absolutely, you can set up the Album#show action to allow users to add photos. This would involve two changes to what I suggest above.

In your controller, you would instantiate a new Photo on @album's Photos collection, much as you had earlier:

def show
  @user = User.find(params[:user_id])
  @album = @user.albums.find(params[:id])
  @photo = @album.photos.build
end

Then, in the Album#show template (e.g. app/views/albums/show.html.erb), you'd include a form for submitting a new photo:

<%= form_for [@user, @album, @photo] do |f| %>
  <%= f.text_field :caption %>
  <%= f.file_field :image %>
<%- end %>

This form would submit its contents to the user_album_photos_path, which you may note does not yet exist. So the final change would be to nest Photos as a resource in routes.rb:

resources :users do
  resources :albums do
    resources :photos
  end
end

This will provide you with the route needed to use the form. Now you'll just need to add a Photo#create action to handle the form submission, and you're off to the races!

like image 115
Daniel Wright Avatar answered Oct 31 '22 20:10

Daniel Wright