Associate photo to album

Before we associate the album and photo model, it would be better to plan the entry point — routes. By doing that, we can have a clear blueprint on how the app should redirect.

Photo flow

Target URL:

/                                   home page
/albums/new                         create album
/albums/:album_id                   show spceific album with photos
/albums/:album_id/photos/new        create photo
/albums/:album_id/photos/:photo_id  show specific photo

Important: Do not underestimate the power of URL design. It acts as the entry point of your web app. Your users bookmark it; They share it; They hack it; And Google indexes it.

Do not under estimate the power of URL design

We want to update the routes.rb file to reflect our URL design.

1PhotoGallery::Application.routes.draw do
2  resources :albums do
3    resources :photos
4  end
5end

The association starts in the model.

album.rb file.

1class Album < ActiveRecord::Base
2  attr_accessible :title
3  has_many :photo
4end

photo.rb file.

1class Photo < ActiveRecord::Base
2  attr_accessible :title, :file, :album_id
3  has_attached_file :file, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
4  belongs_to :album
5end

Now photos is nested inside albums. This means all our photo actions will need the @album instance. We can do that by using before_filter (or before_action in rails 4).

In the photos_controller.rb file, add the before_filter line at the beginning and the private methods at the end, before the class end.

1before_filter :set_album
2
3private
4def set_album
5  @album = Album.find params[:album_id]
6end

Then we change all the Photo reference to @album.photos because every photos collection querying is bound by the @album.

And before saving the photo, we set the photo to the associated album again.

1@photo.album = @album

Note: before_filter means running the given method before running every actions. We can use :only action to run it only in a given list of methods. Or we can use :except for a list of methods that doesn’t run the given method.

Example on using before_filter:

1before_filter :authorize, except: [:index, :show]
2before_filter :authorize, only: :delete

And here is the full photos_controller.rb file dump, in case you failed to complete the changes:

 1class PhotosController < ApplicationController
 2
 3  before_filter :set_album
 4
 5  def show
 6    @photo = @album.photos.find params[:id]
 7  end
 8
 9  def new
10    @photo = @album.photos.new
11  end
12
13  def create
14    @photo = @album.photos.new params[:photo]
15    @photo.album = @album
16    if @photo.save
17      redirect_to @album
18    else
19      render :new
20    end
21  end
22
23  def edit
24    @photo = @album.photos.find params[:id]
25  end
26
27  def update
28    @photo = @album.photos.find params[:id]
29
30    if @photo.update_attributes params[:photo]
31      redirect_to @album
32    else
33      render :edit
34    end
35  end
36
37  private
38  def set_album
39    @album = Album.find params[:album_id]
40  end
41
42end

And then the views update.

views/photos/show.html.erb file.

1<%= link_to 'Edit', edit_photo_path(@photo) %>
2
3# becomes =>
4
5<%= link_to 'Edit', edit_album_photo_path(@album, @photo) %>

As a bonus, we can also create a breadcrumb when showing the photo:

views/photos/show.html.erb file.

1<p>
2  <%= link_to @album.title, @album %>
3  /
4  <%= @photo.file_file_name %>
5</p>

Note: As documented in the paperclip gem, we can use <attachment>_file_name to refer to the filename string.

This is how the breadcrumb looks like:

Breadcrumbs

views/photos/_form.html.erb file.

 1<%= form_for [@album, @photo], html: { :multipart => true } do |f| %>
 2
 3  ...
 4
 5  <p>
 6    <%= f.submit 'Upload Photo' %>
 7    or
 8    <%= link_to 'Cancel', [@album, @photo] %>
 9  </p>
10
11<%- end %>

First, we get the @album reference in the albums_controller.rb file.

1class AlbumsController < ApplicationController
2  def show
3    @album = Album.find params[:id]
4  end
5
6  ...
7end

Now we can list all the photos in the album.

Add the views/albums/show.html.erb file with the following HTML/ERB code

 1<h1><%= @album.title %></h1>
 2
 3<p><%= link_to 'Upload new photo', new_album_photo_path(@album) %></p>
 4
 5<% @album.photos.each do |photo| %>
 6  <%= link_to [@album, photo] do %>
 7    <%= image_tag photo.file.url(:thumb) %>
 8  <% end %>
 9<% end %>

When test the code in browser, the albums list the photos and show the photo after clicking on it.

Album show

What’s next? We’re going to take a look at “Introducing asset pipeline”.

overlaied image when clicked on thumbnail

Makzan | Ruby on rails 101 | Table of Content