are there any tutorials out there how to scaffolding a simple model that uses many-to-many relationships?
Rails scaffolding is a quick way to generate some of the major pieces of an application. If you want to create the models, views, and controllers for a new resource in a single operation, scaffolding is the tool for the job.
Static Scaffolding needs user insertion commands to generate data fields, whereas Dynamic Scaffolding generates the entire content at run time.
This tut i have written while creating the below testapp step by step using ruby 1.9.2 on Rails 3.0.5. Also see 'Gemfile' for the gems I used (whole Testapp downloadable, link is at the end in part 15). So here goes:
1) go to a place where you want to create a test app, then
rails new mynewtestapp
cd mynewtestapp
2) then add 2 models that have a has_and_belongs_to_many association
rails g scaffold book title:string author:string
rails g scaffold user name:string age:integer
3) then you need to create the join-table for that asssociation... by default rails will look for a table with the name consisting of the names both associated tables in alphabetical order... so lets create a migration to create such a table
rails g migration createBooksUsers
4) open the generated migration file, which at that point looks like
class CreateBooksUsers < ActiveRecord::Migration
def self.up
end
def self.down
end
end
5) modify that to look like this
class CreateBooksUsers < ActiveRecord::Migration
def self.up
create_table :books_users, :id => false do |t|
t.integer :book_id
t.integer :user_id
end
end
def self.down
drop_table :books_users
end
end
6) add the has_and_belongs_to_many association to the book and user models, as well as the new ids added by the relationship
app/model/book.rb
class Book < ActiveRecord::Base
attr_accessible :title, :author, :user_ids
has_and_belongs_to_many :users
end
app/model/user.rb
class User < ActiveRecord::Base
attr_accessible: :name, :age, :book_ids
has_and_belongs_to_many :books
end
7) now our models and migrations are done ... lets create the tables
rake db:create
rake db:migrate
(well create might not be necessary if you use sqlite3 or if you have created the database to be used manually, this example will work using sqlite therfore i have not added anything related to installing a database-management-system. but as there are plenty and actually all worthy enough to be used are very well documented, you will find any help about that pretty quick)
8) now decide which object shall be assigned to which object.... of course you can do that both ways... i'll keep it simple and demonstrate that to one... lets say you have only a few users and want to assign those to the books...
at this point, i would say lets get some outside help, like binary x suggested... but for simplicity i'd choose the simple_form gem over Formtastic. I guess everyone has their favorites... but simple_form seems to give you more freedom in css-styling the whole output to your wishes... so lets install simple_form at this point, just do
echo "gem 'simple_form', :git => 'git://github.com/plataformatec/simple_form.git'" >> Gemfile
to add simple_form to your Gemfile, then run
bundle install
and install simple form to your application (i. e. generate config, default styles and language files) by
rails g simple_form:install
9) time to modify our books form
the books form right now should look like this
app/views/books/_form.html.erb
01 <%= form_for(@book) do |f| %>
02 <% if @book.errors.any? %>
03 <div id="error_explanation">
04 <h2><%= pluralize(@book.errors.count, "error") %> prohibited this book from being saved:</h2>
05
06 <ul>
07 <% @book.errors.full_messages.each do |msg| %>
08 <li><%= msg %></li>
09 <% end %>
10 </ul>
11 </div>
12 <% end %>
13
14 <div class="field">
15 <%= f.label :title %><br />
16 <%= f.text_field :title %>
17 </div>
18 <div class="field">
19 <%= f.label :author %><br />
20 <%= f.text_field :author %>
21 </div>
22 <div class="actions">
23 <%= f.submit %>
24 </div>
25 <% end %>
Using simple_form, we can just replace some of the above code (lines 1 and 14 - 24) so the whole file would look like this:
01 <%= simple_form_for(@book) do |f| %>
02 <% if @book.errors.any? %>
03 <div id="error_explanation">
04 <h2><%= pluralize(@book.errors.count, "error") %> prohibited this book from being saved:</h2>
05
06 <ul>
07 <% @book.errors.full_messages.each do |msg| %>
08 <li><%= msg %></li>
09 <% end %>
10 </ul>
11 </div>
12 <% end %>
13
14 <%= f.input :title %>
15 <%= f.input :author %>
16 <%= f.association :users %>
17
18 <%= f.button :submit %>
19
20 <% end %>
10) Now you may want to start your application
rails s
add some users, then add a book and and there is your first has_and_belongs_to_many form:
11) Well that might not yet be the most beautiful thing to look at, but a simple addition of a stylesheet will help a bit... create a new file
public/stylesheets/simple_form.css
and paste the following lines into it
/* public/stylesheets/simple_form.css */
.simple_form label {
float: left;
width: 100px;
text-align: right;
margin: 2px 10px;
}
.simple_form div.input {
margin-bottom: 10px;
}
.simple_form div.boolean, .simple_form input[type='submit'] {
margin-left: 120px;
}
.simple_form div.boolean label, .simple_form label.collection_radio, .simple_form label.collection_check_boxes{
float: none;
margin: 0;
}
.simple_form .error {
clear: left;
margin-left: 120px;
font-size: 12px;
color: #D00;
display: block;
}
.simple_form .hint {
clear: left;
margin-left: 120px;
font-size: 12px;
color: #555;
display: block;
font-style: italic;
}
Then reload the page and ... Tadaa ... first strike...
12) And if you don't like multiple-choice-listboxes just go back to the books form
app/views/books/_form.html.erb
and modify line
15 <%= f.input :author %>
slightly to
15 <%= f.input :author, :as => :check_boxes %>
to make check-boxes out of the list-box.... but... ewww.... look at this:
13) something seems slightly wrong... the left to right presentation of the options is known to trouble simple_form greenhorns every now and then, but actually its an easy to fix issue
and on top of that little format issue, you might also want to see the Users age behind his name in braces, like 'Tom (25)'
... so lets do 3 quick fixes
a) uncomment and set 2 options in config/initializers/simple_form.rb in order to wrap each checkbox with a div and to place the set of checkboxes inside a fieldset
# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
config.collection_wrapper_tag = :fieldset
# You can wrap each item in a collection of radio/check boxes with a tag, defaulting to none.
config.item_wrapper_tag = :div
b) modify our simple_form.css stylesheet a little, as in add:
fieldset { border: 0; }
... unless you'd prefer a big ugly border surrounding the fieldset
c) create the method 'to_label' in our user-model, as 'to_label' is by default the first method simple_form looks for in order to get a String-representation to display an object. By a strange incident our model User has a column called 'name'. As name also is a method simple_form looks for in a model we were lucky this app has worked so far. If we had called the name column forename instead, Rails would have listed not the user names but the default-ruby-object representations (e. g. <#User:521369846>). Guess we were lucky ;-)
app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :users
def to_label
"#{name} (#{age})"
end
end
and the edit-form gets a nice look...
14) Now only the show view needs to display the book owners... thats not too hard either, just open the show-view
app/views/books/show.html.erb
and add lines 13-16 to display the bookowners:
01 <p id="notice"><%= notice %></p>
02
03 <p>
04 <b>Title:</b>
05 <%= @book.title %>
06 </p>
07
08 <p>
09 <b>Author:</b>
10 <%= @book.author %>
11 </p>
12
13 <p>
14 <b>Who owns a copy?</b>
15 <%= @book.users.map {|x| x.to_label}.join ', ' %>
16 </p>
17
18 <%= link_to 'Edit', edit_book_path(@book) %> |
19 <%= link_to 'Back', books_path %>
and last but not least ... the show view
15) Well, so much for a quick tutorial to habtm or in words has_and_belongs_to_many associations in rails. I have put my test-app I created while writing this online at https://1drv.ms/u/s!Alpu50oGtUZq7AiJkL08QqBiMAjb
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With