Finishing a Basic Administration Interface

Today’s task is to finish up the admin interface for Notes on Rails. If this post had a subtitle, it would be “Ooops!”


No more “columns” or “rows”

I accidentally used reversed names in naming some of the entities yesterday, and I found that out in mid-stream. So a good portion of the work today is undoing stuff previously done. Still, the work ends pretty well. The admin interface is functionally complete after today. Tomorrow should be working on student interface.


A lot of work was done yesterday, but the main goal today is to use the databse knowledge we added to help with administration. For instance, if there is a field in the condition table for the experiment it belongs to, why not populate the new-condition field with a list of experiments already in the database.

A lot of what’s happening here is from 6.4 “Prettier Listings,” which beings on page 85 of Agile Web Development with Rails, 2nd Edition.

However, first I need to change something from yesterday. I created all the foreign keys to be text fields, but I want them to be numeric fields. This is because the Name of an Experiment, Condition, etc, can change, but rails internally will hold an id that makes it unique.

In rals, all database work is done through migrations. So enter

ruby script/generate migration alter_foreign_keys

Then open db/migrate/006_alter_foreign_keys.rb. Recall code for altering tables is on (79). Alter 006_alter_foreign-keys.rb to read:

class AlterForeignKeys < ActiveRecord::Migration
def self.up
remove_column :conditions, :experiment_name
remove_column :rows, :condition_name
remove_column :columns, :condition_name

add_column :conditions , :experiment_id, :integer
add_column :rows , :condition_id, :integer
add_column :columns , :condition_id, :integer
end

def self.down
remove_column :conditions, :experiment_id
remove_column :rows, :condition_id
remove_column :columns, :condition_id

add_column :conditions , :experiment_name, :string
add_column :rows , :condition_name, :string
add_column :columns , :condition_name, :string
end
end

then rake db:migrate.

Unlike last time, when we generated a dynamic scaffold with “ruby script/generate model condition” this time create a static scaffold with (86)

ruby script/generate scaffold condition manage_conditions

and enter “y” when it asks you to overwrite the work done yesterday

Ruby on Rails’ MVC (Model, View, Controller) philosophy means that code is split up between model, view, and controller logic. The model we created yesterday is in app/models/condition.rb while the controller was just regenerated by our command and is 50 lines long. Ruby automatically split up the view into many sub-pages: for now, open for editing _form.rhtml.

Jumping ahead to 486 we see basic logic for working with selection lists /dropdown boxes. I had trouble understanding this bit at first, but railsforum helped out. Replace the single line in app/views/manage_conditions/_form.rhtml

<%=

@experiments = Experiment.find(:all, :order => "name" ).map {|u| [u.name, u.id] }
select :condition, :experiment_id, @experiments

%>

While we’re at it, edit /app/models/condition.rb to contain the line

validates_presence_of :name, :description, :experiment_id

Next up, do a similar thing with the rows.

ruby script/generate scaffold

ruby script/generate scaffold row manage_rows

Open up app/views/manage_rows/_form.rhtml.

Add the following two lines:

<p><label for="row_condition_id">Condition ID</label><br/>

<%=

@conditions = Condition.find(:all, :order => "name" ).map {|u| [u.name, u.id] }
select :row, :condition_id, @conditions

%></p>

Add the following line to app/models/row.rb (the Row Model)

validates_presence_of :name, :description, :condition_id

And then identical work for manage_columns

+ generate the static scaffold
+ add to _form.rhtml
+ add to Column Model

Can you get this to work? I can’t.

OOPS!

It looks like “Columns” is a special name in Ruby (or at least SQL).

(What an interesting system we’re coming up with!)

So let’s destroy columns and create a replacement called “Field.” Our method (from DreamInCode and wiki.rubyonrails.org is

ruby script/destroy scaffold column manage_columns

And as long as we’re renaming one thing, let’s rename the other too. Get rid of the row scaffold. The row concept only makes sense in relations to columns, and if we’re getting rid of one, just as well get rid of the other

ruby script/destroy scaffold row manage_rows

Now add code to drop them from the database

ruby script/generate migration drop_columns
ruby script/generate migration drop_rows

008_drop_rows.rb looks like:

class DropRows < ActiveRecord::Migration
def self.up
drop_table :rows
end

def self.down
create_table :rows do |t|
t.column :name, :string
t.column :description, :text
t.column :condition_id, :integer
end
end
end

and 007-drop_columns.rb is similar. Then rake db:migrate.

Now let’s see if we can create these objects correctly, as notes_record and notes_field.

ruby script/generate model notes_record
ruby script/generate model notes_field

Edit 010_create_notes_fields.rb and 009_create_notes_records.rb to add the following three lines where appropriate:

t.column :name, :string
t.column :description, :text
t.column :condition_id, :integer

Then rake db:migrate, of course.

Now for some housekeeping.

In app/models/condition.rb change has_many :rows | has_many :columns to has_many :notes_records | has_many :notes_fields

In the same folder, open up notes_field.rb so it reads

class NotesField < ActiveRecord::Base
validates_presence_of :name, :description, :condition_id
validates_uniqueness_of :name

belongs_to :condition
end

Now add the controllers

ruby script/generate controller manage_notes_records
ruby script/generate controller manage_notes_fields

Now create some static scaffolds

ruby script/generate scaffold notes_record manage_notes_records
ruby script/generate scaffold notes_field manage_notes_fields

Now we’ll drop recreate them as notes_record and notes_field.

Phew! Now back to adding in the drop-down list. Let’s start with app/views/manage_notes_fields/_form.rhtml

Add the following:

<p><label for="notes_field_condition_id">Condition ID</label><br/>

<%=

@conditions = Condition.find(:all, :order => "name" ).map {|u| [u.name, u.id] }
select :notes_field, :condition_id, @conditions

%></p>

Then app/views/manage_notes_records/_form.rhtml

<p><label for="notes_record_condition_id">Condition ID</label><br/>

<%=

@conditions = Condition.find(:all, :order => "name" ).map {|u| [u.name, u.id] }
select :notes_record, :condition_id, @conditions

%></p>

Nearly done. The “edit” functionality of the manage_ now works because it uses the same _form.rhtml logic. What we need to do, though, is to edit the show functionality. Edit this file so it looks like:

<p>
<br /><b>Name</b>: <%= @notes_record_name %>
<br /><b>Description</b>: <%= @notes_record_description %>
<br /><b>Condition</b>: <%= @notes_record_condition %>
</p>

<%= link_to ‘Edit’, :action => ‘edit’, :id => @notes_record %> |
<%= link_to ‘Back’, :action => ‘list’ %>

And in app/controllers/manage_notes_records_controller.rb, replace the def show module like so:

def show
@notes_record = NotesRecord.find(params[:id])

@notes_record_name = @notes_record[‘name’]
@notes_record_description = @notes_record[‘description’]
condition = Condition.find(@notes_record[‘condition_id’])
@notes_record_condition = condition.name
end

Now do the same thing with manage_notes_fields_controller.rb and its related show.rhtml view. (We won’t mess with generalizing this over to the list command, though we could do that in a pretty straightforward manner.)

We’re on a role, so let’s add this same basic logic to manage_conditions.rb and its show.

def show
@condition = Condition.find(params[:id])

@condition_name = @condition[‘name’]
@condition_description = @condition[‘description’]
experiment = Experiment.find(@condition[‘experiment_id’])
@condition_condition = experiment.name
end

And the show.rhtml for the condition view is

<p>
<br /><b>Name</b>: <%= @condition_name %>
<br /><b>Description</b>: <%= @condition_description %>
<br /><b>Condition</b>: <%= @condition_condition %>
</p>

<%= link_to ‘Edit’, :action => ‘edit’, :id => @condition %> |
<%= link_to ‘Back’, :action => ‘list’ %>

You should now have four locations at localhost:3000/ where you can add and edit information. The show module in manage_conditions_controller.rb is now:

  • manage_experiments : still using the dynamic scaffolding
  • manage_conditions : now using the dynamic scaffolding
  • manage_note_records: now using the dyanamic scaffolding
  • manage_note_fields: now using the dynamic scaffolding

Try them!

Leave a Reply

Your email address will not be published. Required fields are marked *