Can Rails deal with related attributes in forms?

R

Robert Jones

Hi! I'm exploring whether or not to move to Rails, and the solution to the
following problem would be a big step in the right direction for me:

Suppose that object A has many of object B, and object B has some attribute
c, and that my Rails app has models that hold these associations.

Can Rails then help me to produce an edit form for object A that includes a
list of all the related object B's, and allows me to edit each of their
attribute c's directly (ie not by simply providing a link to a seperate
edit form for each of the object B's)

I've made this happen using PHP/MySql - I'm hoping that Rails can do it 10
times faster!
 
M

Michael Schuerig

Robert said:
Can Rails then help me to produce an edit form for object A that
includes a list of all the related object B's, and allows me to edit
each of their attribute c's directly (ie not by simply providing a
link to a seperate edit form for each of the object B's)

Yes, that's entirely possible.

http://wiki.rubyonrails.com/rails/show/HowToEditObjectArraysUsingForms

That still looks more complicated than it should, IMHO, but I think for
the time being there's no easier solution.

Michael
 
K

Kirk Haines

Suppose that object A has many of object B, and object B has some attribute
c, and that my Rails app has models that hold these associations.

Can Rails then help me to produce an edit form for object A that includes a
list of all the related object B's, and allows me to edit each of their
attribute c's directly (ie not by simply providing a link to a seperate
edit form for each of the object B's)

This doesn't seem to be a matter of "can" Rails do it. The existing
scaffolding may or may not be able to directly support this, but it should be
darn simple to write something in any decent framework, with any decent ORM,
that can.

I use a component in production that could trivially be extended to do exactly
this, generically.


Kirk Haines
 
K

Kirk Haines

Yes, that's entirely possible.

http://wiki.rubyonrails.com/rails/show/HowToEditObjectArraysUsingForms

That still looks more complicated than it should, IMHO, but I think for
the time being there's no easier solution.

Interesting little article.

For comparison, here's the equivalent in my current IOWA version, with
assumption that the list of items comes from somewhere useful, such as a
database query, and is just passed to the List component when the list is
called:

Views:

List.html
-----
<h1>Item#list</h1>

<table border="1" cellspacing="0" cellpadding="0" width="10%">
<repeat oid="item_list"><tr><td>@item.name</td></tr>
</repeat>
</table>
<br />
<a oid="goto_edit">Edit</a>
-----

Edit.html
-----
<h1>Edit itemlist</h1>

<form oid="save_items">
<table border="1" cellspacing="0" cellpadding="0">
<repeat oid="item_list">
<tr><td><input type="text" oid="item.name" size="20">)</td></tr></repeat>
</table>
<br />
<input type='submit' oid="save_items" value='Save' />
</form>
<br />
<a oid="go_back">List</a>
-----

Controllers:

List.iwa
-----
class List < Iowa::Component
attr_accessor :item, :items

def goto_edit
edit_page = page_named('Edit')
edit_page.items = @items
yield edit_page
end
end

<?
item_list {
item = item
list = items
}
?>
-----

Edit.iwa
-----
class Edit < Iowa::Component
attr_accessor :item. :items

def go_back
yield prev_page
end

def save_items
# This really isn't needed unless something special needs to be done; the
# changes automatically get saved.
end
end

<?
item_list {
item = item
list = items
}
?>


Note that this example above doesn't sound like it quite does what the OP was
asking for, which is to have a master record with it's fields, and each of
the associated records with their fields, all displayed in one big form for
editing.

Using my CRUDForm component to do that, the view would end up being something
like this:

-----
<h1>Edit itemlist</h1>

<crudform oid="item_record" />

<br />
<a oid="go_back">List</a>
-----

And the controller something like so:
-----
import 'CRUDForm'
class Edit < Iowa::Component
attr_accessor :item

def go_back
yield prev_page
end
end

<?
item_record {
record = item
cascade = true
}
?>
-----


I'm sure one can do something similarly simple to use with Rails and Nitro, as
well.


Kirk Haines
 
R

Robert Jones

Kirk said:
This doesn't seem to be a matter of "can" Rails do it. The existing
scaffolding may or may not be able to directly support this, but it should
be darn simple to write something in any decent framework, with any decent
ORM, that can.

I use a component in production that could trivially be extended to do
exactly this, generically.


Kirk Haines


I made it happen, pretty easily, but I still think this ought to be built
into Rails. Can't see why it isn't. Maybe it is and I'm missing
something.

I used an example of houses and rooms. Each house has many rooms, and the
rooms have names. In order to produce a form to edit the houses, which
allows direct editing of the names of the rooms in the house, I did the
following:

sql:

-------------------------------------------------------------
CREATE TABLE `houses` (
`id` mediumint(9) NOT NULL auto_increment,
`name` varchar(30) NOT NULL default '',
`description` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

#
# Dumping data for table `houses`
#

INSERT INTO `houses` VALUES (1, 'Briary Bush', 'Lovely little house near a
babbling river');
INSERT INTO `houses` VALUES (2, 'Fawlty Towers', 'A grotty hell hole');

CREATE TABLE `rooms` (
`id` mediumint(9) NOT NULL auto_increment,
`name` varchar(50) NOT NULL default '',
`house_id` mediumint(9) NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

#
# Dumping data for table `rooms`
#

INSERT INTO `rooms` VALUES (1, 'First Floor Front Room', 1);
INSERT INTO `rooms` VALUES (2, 'Penthouse', 1);
INSERT INTO `rooms` VALUES (3, 'Room 1', 2);

-------------------------------------------------------------


house.rb:

-------------------------------------------------------------
class House < ActiveRecord::Base
has_many :rooms

# This extends update_attributes to allow for the updating of the
associated records.
# The "rooms" hash is in the form {"id"=>{"column"=>"value",.......},
"id"=>{"column"=>"value",.....},....}
def update_attributes(attributes)
attributes["rooms"].each do |key,value|
@room=Room.find(key)
@room.update_attributes(value)
end
attributes.delete("rooms")
super(attributes)
end
end

-------------------------------------------------------------


room.rb:

-------------------------------------------------------------
class Room < ActiveRecord::Base
belongs_to :house
end
-------------------------------------------------------------



_form.html partial in views/houses :

-----------------------------------------------------------
<%= error_messages_for 'house' %>

<!--[form:house]-->
<p><label for="house_name">Name</label><br/>
<%= text_field 'house', 'name' %></p>

<p><label for="house_description">Description</label><br/>
<%= text_area 'house', 'description', "rows"=>"5" %></p>
<% for room in @house.rooms %>
<label for="room_<%=room.id %>_name">Room <%=h room. id%></label>
<input id="house_rooms_<%=h room. id%>_name" name="house[rooms][<%=h room.
id%>][name]" type="text" value="<%=h room. name%>" />
<% end %>
<!--[eoform:house]-->

-------------------------------------------------------------


I look forward to someone showing me a neater way to do it :)


The extension of the update_attributes method seems so simple that I can't
believe it would take much work to generalise this and have
update_attributes deal with associated data automatically. Any takers for
this wee Rails development task ? ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,175
Messages
2,570,946
Members
47,497
Latest member
PilarLumpk

Latest Threads

Top