Introduction
In my last post I made a model plant with 3 attributes, namely name, height and color. Now I would like to add an image to that because that always seems to be the most difficult thing to do. I also decided that it would be a good idea to keep the image in the database, just because I like to be different.
And again the guide to read for me was migrations on the ruby on rails guide.
Migrate
After some swearing and cursing and reading lots of out of date websites, I finally found what I was looking for when it comes to adding a column to a table/model.
Some things to not that are very important in this case or it won’t work.
The name is very important, I stress VERY. It only knows I want to add the column Image to the table plants because that is what’s in the name. and then I add the columnname and the type.
After you do this. YOu will see there is an extra file in the migrate folder. Something like this.
class AddImageToPlants < ActiveRecord::Migration
def change
add_column :plants, :image, :binary
end
end```
And the name of that file could be this; 20110731074732\_add\_image\_to\_plants. The timestamp is important because it tells rails what it already has in the database and what to do if it does not yet have that. We can now run rake db:migrate or in rubymine just right-click on the file and select run db:migrate.
We should now see that schema.rb has gone from this
ActiveRecord::Schema.define(:version => 20110731074732) do
create_table “plants”, :force => true do |t| t.string “name” t.integer “height” t.string “color” t.datetime “created_at” t.datetime “updated_at” end
end``` to this.
ActiveRecord::Schema.define(:version => 20110731074732) do
create_table "plants", :force => true do |t|
t.string "name"
t.integer "height"
t.string "color"
t.datetime "created_at"
t.datetime "updated_at"
t.binary "image"
end
end```
Woohoo one added field.
Now let’s show that on the screen.
## Image upload
This is the point where Google was of not so much help but I found that several sites had several pieces of the puzzle.
The most help I got [from a forumpost][2] at the railsforum. But it was in no way copy/paste. So here it goes.
First I changed the listing to show me the image and a link to upload the image. Which is the index.html.erb file in views/plants
```ruby
<h1>Listing plants</h1>
<table>
<tr>
<th>Name</th>
<th>Height</th>
<th>Color</th>
<th>Image</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @plants.each do |plant| %>
<tr>
<td><%= plant.name %></td>
<td><%= plant.height %></td>
<td><%= plant.color %></td>
<td><%= image_tag("/plants/picture_path/#{plant.id}", :alt => plant.name) %></td>
<td><%= link_to 'Show', plant %></td>
<td><%= link_to 'Edit', edit_plant_path(plant) %></td>
<td><%= link_to 'Upload image', upload_plant_path(plant) %></td>
<td><%= link_to 'Destroy', plant, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New Plant', new_plant_path %>
First I added an image tag <%= image_tag(“/plants/picture_path/#{plant.id}“, :alt => plant.name) %>
and as you can see I refer to a controlleraction called picture_path.
You can add this action to the plants_controller.rb.
def picture_path
@plant = Plant.find(params[:id])
send_data @plant.image, :filename => @plant.name + '.jpg', :type => 'image/jpeg', :disposition => 'inline'
end```
Which will give you this.
<div class="image_block">
<a href="/wp-content/uploads/users/chrissie1/ruby/migrate3.png?mtime=1312113231"><img alt="" src="/wp-content/uploads/users/chrissie1/ruby/migrate3.png?mtime=1312113231" width="359" height="147" /></a>
</div>
This was before I added the link it will show you the alt text since we don’t have an image in the database yet.
And I also added the link. like this.
```ruby
<%= link_to 'Upload image', upload_plant_path(plant) %>```
And I added an upload action to the plans controller.
```ruby
# GET /plants/1/upload
def upload
@plant = Plant.find(params[:id])
end```
If you now run this than you will notice that this does not work. You get this.
<div class="image_block">
<a href="/wp-content/uploads/users/chrissie1/ruby/migrate2.png?mtime=1312112937"><img alt="" src="/wp-content/uploads/users/chrissie1/ruby/migrate2.png?mtime=1312112937" width="726" height="112" /></a>
</div>
This because the scaffolding does not set the routing to allow another route which the upload is.
So go to routes.rb and change the
```ruby
resources :plants do
to this
resources :plants do
member do
get 'upload'
end
end```
And now it will work.
<div class="image_block">
<a href="/wp-content/uploads/users/chrissie1/ruby/migrate4.png?mtime=1312113731"><img alt="" src="/wp-content/uploads/users/chrissie1/ruby/migrate4.png?mtime=1312113731" width="440" height="157" /></a>
</div>
Before I can click the upload image link I should also add a view for this action. So just add a file called upload.html.erb and then add this code to it.
```ruby
<h1>Upload image</h1>
<%= form_for(@plant, :url=>{:action=>'update'}, :html=>{:multipart=>true}) do |f| %>
<%= f.file_field :file %>
<%= f.submit 'Upload' %>
<% end %>
<%= link_to 'Show', @plant %> |
<%= link_to 'Back', plants_path %>
and if I now click the link I get this.
I can now upload the picture.
Well, not really since I first have to change my model a little.
class Plant < ActiveRecord::Base
def file=(input_data)
self.image = input_data.read
end
end
where I tell the model to put the data from the fileupload routine into the image field.
And that is it. I have no uploaded the file, put it in the database and shown it on the screen.
Conclusion
The next steps might be to add the image to the show view. And other things but I can work with it from here. I hope I did not forget a step somewhere when making this post since nothing went right the first time, most things didn’t even work the fifth time. So I might have forgotten some things from time to time.