Best way to show nested children

119 views
Skip to first unread message

mikel

unread,
Jun 19, 2007, 2:58:04 AM6/19/07
to ActiveScaffold : Ruby on Rails plugin
I have the following models:

class Book; has_many :sections; end
class Section; has_many :documents; end

What I want to do is have a nested button that shows all of the
documents in order belonging to the book.

Something like:

"config.nested.add_link("Documents", [:documents])"

This doesn't work otherwise I wouldn't be posting.

I'm working on something else right now so I thought I would just
throw it to the list to see the best method. The two options I see
are making a helper method to pull these records out, or making a
has_many through association.

What would be the cleanest?

Regards

Mikel

Daniel E

unread,
Jun 19, 2007, 8:57:23 AM6/19/07
to ActiveScaffold : Ruby on Rails plugin
What exactly do you mean by a nested button?
For free, AS will turn the abbreviated list of documents in your list
view into a hyperlink that will display a nested list of them when you
add :documents to the list columns.

Note that you might need to explicitly specify that Book
has_many :documents, :through => :sections
in order to be ablet o add :documents to the list columns.
If you want a seperate action link in addition to the hyperlink, you
need to set the
:type => :record
property on the link you add.

mikel

unread,
Jun 19, 2007, 7:53:19 PM6/19/07
to ActiveScaffold : Ruby on Rails plugin
Thanks for the reply Daniel,

I haven't got this working yet. What i am after is a link to the
right of each book that allows me to show all nested documents, just
like I have a link called "sections" that allows me to see all nested
sections. Here is the setup:

My models look like this:

class Book
has_many :sections


has_many :documents, :through => :sections

end

class Section
belongs_to :book
has_many :documents
end

class Document
belongs_to :section
delegate :original, :original=, :to => :section
end

In the console, I can do:

>> document.book
=> #<Book:0x3c8e954 @attributes={"updated_at"=>"2007-06-19 22:48:31",
"title"=>"My Book", "revision"=>"May 2007", "id"=>"1",
"created_at"=>"2007-06-18 16:57:41"}>
>> book.documents
=> [#<Document:0x3c84170 @attributes={"job_key"=>nil,
"size"=>"120795", "updated_at"=>"2007-06-19 22:47:02",
"title"=>"Chapter 2", "section_id"=>"1", "imported"=>"1", "id"=>"1",
"progress"=>"100", "saved"=>"1", "position"=>"3",
"file"=>"CHAPTER_2.htm", "created_at"=>"2007-06-18 16:57:59"}>,
#<Document:0x3c84148 @attributes= <<< ETC SNIP >>>

So that works as expected.

Then on the controller side, I have:

class BooksController < ApplicationController
active_scaffold :books do |config|
config.nested.add_link("Sections", [:sections])


config.nested.add_link("Documents", [:documents])

end
end

class DocumentsController < ApplicationController
active_scaffold :document do |config|
config.columns[:book]
end
end


Then I go to the book list view, click on "documents" and I get a
ActiveScaffold::MalformedConstraint in Vms documentsController#table

"Malformed constraint `originals'. If this column is legitimate,
please double-check that it exists in the config.columns set. If not,
and you are using a nested scaffold, please specify or double-check
the reverse association name."

>From what I can tell the associations are correct... at least, that
is what console is telling me.

any ideas?

regards

Mikel

mikel

unread,
Jun 19, 2007, 8:23:58 PM6/19/07
to ActiveScaffold : Ruby on Rails plugin
Further, if you go into the Book list view, it shows a column called
"documents" that is populated with the correct names. Similarly in
the show view it lists the first few documents as expected.

It is only when you try to view it as a nest does it break.

I think I am missing a setting in the documents controller....

regards

Mikel

Daniel E

unread,
Jun 19, 2007, 9:53:58 PM6/19/07
to ActiveScaffold : Ruby on Rails plugin
Ugh. looking at actions/nested.rb, I see a comment that it doesn't
support nested through associations. I don't really understand what
the limitation is, so I'm not sure how to work around it. Maybe Lance
or someone will have a better answer for you. :/

Lance Ivy

unread,
Jun 19, 2007, 10:12:53 PM6/19/07
to actives...@googlegroups.com
Because of how nesting works (embedded scaffold with constraints) it needs some kind of searchable attribute of the nested model. This is where the reverse association comes in handy. The problem with through associations is finding the reverse ...

mikel

unread,
Jun 19, 2007, 11:54:18 PM6/19/07
to ActiveScaffold : Ruby on Rails plugin
On Jun 20, 12:12 pm, "Lance Ivy" <l...@cainlevy.net> wrote:
> Because of how nesting works (embedded scaffold with constraints) it needs
> some kind of searchable attribute of the nested model. This is where the
> reverse association comes in handy. The problem with through associations is
> finding the reverse ...

Lance, the thing that throws me on that though is that in the show and
index views activescaffold correctly shows the contents of the
documents method.... ie it IS finding the association and displaying
correctly (at least the first 50 or so characters) So I see two
options:

1) Is there any way to explicitly give activescaffold a model to list
with said constraints? even if I have to be really careful of what
constraints to apply? :)

2) My only other option is creating my own custom list page I guess
that just iterates through all the documents and shows them...

(1) or (2) what do you think?

Thanks :)

Mikel

Lance Ivy

unread,
Jun 20, 2007, 1:04:55 AM6/20/07
to actives...@googlegroups.com
Constraints are tricky because they do more than just create ActiveRecord conditions. I've described them in previous posts ... all I'll say here is that if you stop caring about whether new records created in the nested scaffold are automatically scoped, you have a lot more options.

mikel

unread,
Jun 26, 2007, 7:16:18 AM6/26/07
to ActiveScaffold : Ruby on Rails plugin
Dear Lance,

Thanks for this, I went away and thought about it all for a bit before
posting another reply...

<heavy middle west accent>
But there is som'thn I just dunna git!
</heavy accent>

I have a model (books) that has many sections, and has many documents
through sections.

If I go to the active scaffold for books, and click on the EDIT tag of
that book, up pops a full edit table and it correctly shows ALL the
documents that belong to the sections belonging to the book. In
otherwords, the has_many through association works 100% through the
Edit view.

Also, if I click show, same deal, voila, all the paragraphs.

Also, if I click the custom nested button that I have made (sections)
I get all the documents as well.

So, me, thinking I am all smart and all, make a custom nested link
like so:

class BooksController < ApplicationController
active_scaffold :books do |config|
config.nested.add_link("Sections", [:sections])
config.nested.add_link("Documents", [:documents])
end
end

I reload the page, see my sparkling new link, click it with all the
hope of generations or ActiveScaffold newbies... and voila!

CRASH

I get the error "Malformed constraint `books'. If this column is


legitimate, please double-check that it exists in the config.columns

set. If not, and you are using a nested scaffold, please... blah".

Should I send it to a controller method first to find all the
documents and then render a different view? Or do I just need to put
something in my model that handles it?

Sorry for the pain in the arse factor... i have a feeling this has
been answered before, a few times... but I can't get my head around
it.

Once I find out how I'll document it for the WIKI! :)

Regards

Mikel

Lance Ivy

unread,
Jun 26, 2007, 10:47:04 AM6/26/07
to actives...@googlegroups.com
It's all about perspective - what's the current model?. ActiveScaffold does everything with a single model class. In order to get nested scaffolds, you need separate controllers because the rule is one model per controller. So when you click on Show and Update, you're on the BooksController and it's easy to get from a book to its documents (`book.documents`). And when you've opened the nested sections for a book, you're on the SectionsController and it's easy to get from a section to its documents (`section.documents`).

The malformed constraint error, though, is because when you try and nest directly from the BooksController to the DocumentsController, ActiveScaffold isn't quite sure how to link back. What it's trying to do is something like `document.books` or `document.book`, which just doesn't work because there's no association that goes directly from a document to a book. And remember, this is now on the DocumentsController so everything is from the perspective of a document.

-Lance

mikel

unread,
Jun 28, 2007, 7:26:32 AM6/28/07
to ActiveScaffold : Ruby on Rails plugin
OK, so the problem is really that the called model doesn't really know
in what scope to reply to the request.

So, then, has this been solved before?

I guess the long term handling would be a config option that says
something like:

config.nested.add_link("SubSubModel", [:subsubmodel], :scope
=> :top_object_id)

or something?

With the models set up as they are now, I can open console and type
this:

>> document = Document.find(:all).first
=> #<Document:0x3b99bfc @attributes={"job_key"=>nil, "size"=>"66667",
"updated_at"=>"2007-06-27 18:51:26", "title"=>"Chapter One",
"section_id"=>"4", "imported"=>"1", "id"=>"33", "progress"=>"100",
"saved"=>"1", "position"=>"1", "file"=>"_01_01_CHAPTER_1.htm",
"created_at"=>"2007-06-27 18:50:44"}>

>> document.book
=> #<Book:0x3b165a4 @attributes={"updated_at"=>"2007-06-27 18:50:06",
"title"=>"Title One", "revision"=>"May 2007", "id"=>"7",
"created_at"=>"2007-06-27 18:50:06"}>
>>

Which is fine, but then I can see the problem when you try and do
something like:

>> documents = Document.find(:all, :conditions => { :book_id => 7 } )
ActiveRecord::StatementInvalid: Mysql::Error: #42S22Unknown column
'documents.book_id' in 'where clause': SELECT *

So then we get smart and add a class method which looks this up by
getting the original_id and looping down through sections pushing the
records into an array to support this and then we get:

>> Document.find_all_by_book_id(7)
=> [[#<Document:0x2d6039c @attributes={"job_key"=>nil,
"size"=>"66667", "updated_at"=>"2007-06-27 18:51:26",
"title"=>"Chapter One", "section_id"=>"4", "imported"=>"1",
"id"=>"33", "progress"=>"100", "saved"=>"1", "position"=>"1",
"file"=>"_01_01_BOOK_1_CHAPTER_1.htm", SNIP ARRAY >>

So wouldn't the trick be... what is the class method that
ActiveScaffold is trying to call on Documents in order to pull up the
association for Books?

That is something I haven't figured out yet... I am going to dive into
the code and find out... but this can't be too hard to fix I am
sure.............?

Or am I dreaming? :)

Regards

Mikel

Lance Ivy

unread,
Jun 28, 2007, 11:17:02 AM6/28/07
to actives...@googlegroups.com
It's actually hoping there's an association that supports eager loading so it can do something like:

Document.find(:all, :include => :book, :conditions => ['book_id = ?', 6])

Lance Ivy

unread,
Jun 28, 2007, 1:58:07 PM6/28/07
to actives...@googlegroups.com
Woo! Revision 567 in trunk adds some new constraint enhancements that lets nesting work for has_many :through associations. It's not perfect, since half of the constraints functionality (making sure new and existing records stay in the context) is still somewhat, um, lacking.

WARNING: I've only tested this with a has_many :through a join model (a model with two belongs_to relationships).

mikel

unread,
Jun 28, 2007, 7:17:05 PM6/28/07
to ActiveScaffold : Ruby on Rails plugin
Lance, works 100% here...

Actually... works better than that. more like 120%!

On a has_many => through belongs_to one... you don't even have to
specify the book id!

You also don't have to do ANY CONFIGURATION in the controllers!

I just sandboxed this with the following code:

class Book < ActiveRecord::Base


has_many :sections
has_many :documents, :through => :sections
end

(title:string)

class Section < ActiveRecord::Base
has_many :documents
belongs_to :book
end
(title:string book_id:integer)

class Document < ActiveRecord::Base
belongs_to :section
end
(title:string section_id:integer text:text)

Then all the controllers just have "active_scaffold".. nothing else

That gives you the documents as a subset of books... click it and you
get a nice beautiful menu of all the documents!

WOOOOOHOOOOO!!!!!!

Lance, you rock :) I'll play with it some more and see if I can break
it and even maybe possibly fix it :)

Regards

Mikel

Reply all
Reply to author
Forward
0 new messages