4

It's been almost a week since I've began to dig deeper in forms , associations , hashes , symbols... But it seems I cannot solve the puzzle without your help .

I am working on a project for displaying different galleries content . The basic idea is when the user sees the names of galleries (names are links ) to be able to click on chosen one. Then all the images ,that belong to this gallery , are displayed . On the bottom there should be a link "Add image in this gallery" .

My models :

 class Gallery < ActiveRecord::Base
attr_accessible  :name
has_many :pictures 
 end


class Picture < ActiveRecord::Base 
attr_accessible  :image 
belongs_to :gallery
 end

I have created index on gallery_id for the 'pictures' table .

My big problem appears here , how to pass the gallery_id to the controller's action 'new' . As I've seen in "Agile web development with Rails" it could be :
<%= link_to 'Add a picture here...',new_picture_path(:gallery_id=>@gallery.id) %>

As it seems in this case the foreign_key :gallery_id is exposed in the URL bar of the browser . The second problem is that :gallery_id is available for the controller 'new' function , but "disappears" for the 'create' function (causing an error " Couldn't find Gallery without an ID ") . The problem is gone when I add a hidden field in the _form for pictures , in my case :

<%= form_for(@picture)   do |f| %>
<div class="field"> 
      <%= f.hidden_field :gallery_id , :value=>params[:gallery_id] %>
<%= f.label :image %><br />
<%= f.file_field :image %>
</div>

<div class="actions">
<%= f.submit "Create" %>
</div>
<% end %>

Here are my definitions in the 'pictures' controller :

def new
@gallery=Gallery.find(params[:gallery_id])
@picture=@gallery.pictures.build 
 end


def create
   @gallery = Gallery.find(params[:gallery_id])  
   @picture = @gallery.pictures.new(params[:picture])
  if @picture.save
     redirect_to(@picture, :notice => 'Picture was successfully created.')
  else    
     redirect_to(galleries ,:notice => 'Picture was NOT created.')
  end

 end

And finaly the link_to definition in show.html.erb for galleries:

<% for picture in selpics(@gallery) %>
 <div id= "thumb" > 
 <%= image_tag picture.image %>
 </div>
<% end %>

 <%= link_to 'Add a picture here...',new_picture_path(:gallery_id=>@gallery.id) %>

Here is the debug output before submitting the image : --- !map:ActiveSupport::HashWithIndifferentAccess gallery_id: "6" action: new controller: pictures

and after submitting the 'create' button (with exception raised ) :

{"utf8"=>"✓",
 "authenticity_token"=>"IGI4MfDgbavBShO7R2PXIiK8fGjkgHDPbI117tcfxmc=",
 "picture"=>{"image"=>"wilsonblx.png"},
 "commit"=>"Create"}

As you see , there is nothing like "gallery_id" in the "pictures" hash .

Summarizing my questions to you :

  1. Is there a way to pass the foreign_key without hidden_field ?

  2. Could I hide somehow passing the foreign key form showing in the URL bar ?

  3. Is there an alternative on passing arguments using 'link_to' ?

Thank you .

R Milushev
  • 4,295
  • 3
  • 27
  • 35
  • Hiding foreign keys is a common pb. A proper routing leads to clear exposure of them. ff you want to hide these, use cache, cookies or sessions. – apneadiving Mar 05 '11 at 23:03
  • Thank you for encouraging me , I was thinking 'I am lost , shoulda give up ...' . So , sessions and cookies are not 'a cannon for a sparrow ' in this case. – R Milushev Mar 06 '11 at 17:27

2 Answers2

7

You may want to consider reading the Rails Guide on nested resources:

http://guides.rubyonrails.org/routing.html#nested-resources

In a nutshell:

routes.rb

resources :galleries do
  resources :pictures do
end
# Generates the routes: /galleries/:gallery_id/pictures

pictures_controller.rb

def new
  @gallery = Gallery.find(params[:gallery_id])
  @picture = Picture.new
end
def create
  @gallery = Gallery.find(params[:gallery_id]) # gallery_id is passed in the URL
  @picture = @gallery.build(params[:picture])
  if @picture.save
  # success
  else
  # fail
  end
end

pictures/new.html.erb

<%= form_for [@gallery, @picture] do |f| %>
  <div class="field"> 
    <%= f.hidden_field :gallery_id , :value=>params[:gallery_id] %>
    <%= f.label :image %><br />
    <%= f.file_field :image %>
  </div>

  <div class="actions">
    <%= f.submit "Create" %>
  </div>

<% end %>

Ok, so the gallery_id is still passed through the URL, but I don't really see anything wrong with that. You have to pass it somewhere, right? You really only have 3 sane choices on where to pass it: a hidden field, as a querystring parameter, or tucked away inside the URL (nested resource). Of the 3, the latter is IMHO the cleanest method.

If you want to make things even easier on yourself, I highly recommend looking into Jose Valim's Inherited Resources gem that takes care of a lot of this boilerplate nastiness for you:

https://github.com/josevalim/inherited_resources

Josh Deeden
  • 1,660
  • 1
  • 12
  • 14
  • Josh , you really made my Sunday shiny !!! Thank you , good man . Really , by pointing me the nested resources and Jose Valim's inherited resources you revealed a whole new universe . I wish I could help people one day (sharing knowledge ) . – R Milushev Mar 06 '11 at 17:33
  • Sweet. I'm glad you found it helpful. I would definitely say get familiar with how nested resources work before plunging into inherited resources. Baby steps! – Josh Deeden Mar 06 '11 at 17:36
  • 1
    I am not very experienced with Rails but I think that the part which says `resources :galleries do resources :pictures do end` should be `resources :galleries do resources :pictures end` (without the second 'do' keyword). – Wilson Canda Apr 14 '14 at 15:12
0

You need not use the numeric ID's in your RESTful routes. Look at permalink_fu, and use the :permalink field rather than the :id to refer to each gallery resource.

/galleries/louvre
/galleries/moma/382

And

... new_picture_path(:gallery_id => @gallery.permalink)

The key here is using a symbolic, unique key that's not the ID, permalink's are pretty good for that.

You can choose to pass the permalink in as :id and update your controller actions to expect that.

aceofspades
  • 7,568
  • 1
  • 35
  • 48
  • Thank you , fullware ! Very useful resource . I am sure , that your post will help to a lot of newbies like me . – R Milushev Mar 06 '11 at 17:37