Newbie confounded by checkboxes


#1

I’m at the stage in learning Rails (and Ruby) where everything seems
disorienting and awkward.

My first self-assigned project is a links manager. I have a Topic model
and a Link model. Topic is defined as acts_as_tree, while Link is
acts_as_list. I have three tables in a database that I think make sense

  • one is a join table which I will use for links. But I haven’t even
    gotten to that point yet.

A topic can be a parent to other topics or to links, so there are a
number of parent/child relationships going on. I figured the way to
assign a parent to a new topic or a new link would be with check boxes.
Dealing first with topics, when one creates a new Topic one would be
presented with a bunch of checkboxes, one for each existing topic.
Checking a checkbox assigns that topic as the parent to the newly
created topic. (Haven’t even gotten to links yet, but same idea).

There is a “parent_id” column in the topics table. I want to populate
that with the topic id of the checked checkbox.

I have been scratching my head trying to figure out how to write the
check_box form helper in the “create” and “modify” _form partials, and
how to extract the value in the TopicAdmin controller.

In the Topic _form.rhtml partial I have:

<% for topic in @topics %>

  • <%= topic.id %> <%= check_box("topic", "parent_id", {}, topic.id, "0") %> <%= topic.topic_title %>
  • <% end %>

    (I have defined @topics in the controller as Topic.find_all. I’ve got
    zero as the value for nothing checked, as some topics will be top level
    with no parents.)

    Now I don’t know if that’s the right syntax for the check_box helper,
    and if it is I can’t figure out how the controller extracts the value
    and puts it into the parent_id attribute of the topics table.

    The check boxes show up. They appear to carry their id correctly. I’m
    just not succeeding in getting those values inserted into the database
    table.

    Thank you in advance for any suggestions.


    #2

    Try,

    <% @topics.each do |t| %>
    <%= check_box_tag ‘link[topic_ids][]’,
    t.id, @link.topics.include?(t) %>
    <%= t.name %>

    <% end %>

    Have you checked out this tutorial?:
    http://wiki.rubyonrails.com/rails/pages/CheckboxHABTM

    -Steven


    #3

    rooster;

    Thanks a million. I have read you code 6 times and it makes my head hurt
    so it MUST be good! Maybe on the 7th read it will make sense.

    And no. For all the googling I did I missed that wiki article. I will
    read it immediately. Thanks again.


    #4

    Okay - I’m not getting anywhere. The example rooster provided and the
    link he pointed me to both assume I want to add the topic id to the link
    table. I’m not there yet. I’m working at a self referential join. I want
    to establish a link from one topic to another topic. I want to make one
    topic a child of another topic. I’ve got this code in my _form partial:

      <% Topic.find(:all).each do |t| %>
    • <%= t.topic_title %>
    • <% end %>

    Checkboxes show up okay but they aren’t writing the correct value to the
    parent_id field in the topics table. It appears I get a “0” if no
    checkbox is checked, and “1” if any checkbox is checked: in other words
    it appears the default value of the checkbox is being written tot he
    parent_id field, NOT the topic.id value I want.

    Is this a problem with how I have the model set up? Or with how I have
    the checkbox set up? The Topic class so far just has:
    class Topic < ActiveRecord::Base
    has_and_belongs_to_many :topics
    has_many :links, :order => “parent_id, topic_title”
    end

    Thanks for any thoughts.


    #5

    Once again I thank you so much for your interest in this.

    I had a sneaky feeling the Topic model was wrong but nothing I tried
    fixed it.

    I have scoured the Agile Web book (the covers are about to fall off!),
    but Dave’s demonstration adds his Employee/manager/mentor data straight
    through code not dynamically with checkboxes and I just couldn’t
    reconcile the two.

    And I did just tonight read the SelfReferential wiki entry you mention.
    I considered that approach (since he documented it so well there and in
    the sitepoint message board) but I’m planning to use a join table to
    hook up links with topics (if I ever get that far…) and though it
    would get too awkward with two join tables flying around. Especially
    since Dave implied a single table self reference could be done.

    So I’ll give your suggestion a shot and see what new potholes I fall
    into.

    BTW - the topics table is set up like this:

    ±------------±-------------±-----±----±--------±---------------+
    | Field | Type | Null | Key | Default | Extra |
    ±------------±-------------±-----±----±--------±---------------+
    | id | int(11) | | PRI | NULL | auto_increment |
    | parent_id | int(11) | | | 0 | |
    | topic_title | varchar(50) | | | | |
    | topic_descr | varchar(250) | | | | |
    ±------------±-------------±-----±----±--------±---------------+

    Thanks again!


    #6

    Looks like you have your model definition mixed up. You need something
    like this:

    class Topic < ActiveRecord::Base
    belongs_to :parent_topic,
    :class_name => ‘Topic’,
    :foreign_key => ‘parent_id’
    has_many :child_topics,
    :class_name => ‘Topic’,
    :foreign_key => ‘id’
    end

    I’m assuming your topics table is defined as follows:


    topics
    id
    parent_id

    I don’t have much experience doing self referential joins, and I’m not
    positive the above model definition is correct. The Agile Web
    Development w/Rails book has some good info on this. Maybe some other
    rubyists can verify if the above model definition is correct of a self
    referential join.

    There is more info on this here:
    http://wiki.rubyonrails.com/rails/pages/HowToCreateASelfReferentialManyToManyRelationship
    however the examples use a separate join table and it looks like you
    initially just want to get your thing working with the topics table.


    #7

    Actually - I did have the belongs_to part pretty much just as you have
    it, but I didn’t have the has_many part. Maybe that’s the secret.


    #8

    Ok, I got it working,
    do: ‘ruby script/generate scaffold Topic Topic’

    using your topic DDL

    Use the below for _form.rhtml, show.rhtml, topic.rb

    _form.rhtml

    <%= error_messages_for ‘topic’ %>

    Name
    <%= text_field 'topic', 'name' %>

    children topics
    <% Topic.find(:all).each do |t| %> <%= check_box_tag 'topic[child_topic_ids][]', t.id, @topic.child_topics.include?(t) %> <%= t.name %>
    <% end %>

    #####
    show.rhtml

    <% for column in Topic.content_columns %>

    <%= column.human_name %>: <%=h @topic.send(column.name) %>

    Child Topics:
    <% @topic.child_topics.each do |ct| %> <%= ct.name %>
    <% end %>

    <% end %>

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

    topic.rb

    class Topic < ActiveRecord::Base

    validates_uniqueness_of :name

    belongs_to :parent_topic,
    :class_name => ‘Topic’,
    :foreign_key => ‘parent_id’
    has_many :child_topics,
    :class_name => ‘Topic’,
    :foreign_key => ‘parent_id’
    end

    some things to note…
    when you define the ‘has_many’ in the model, rails automatically creates
    a method to get the associated ids. Example:

    class f < ActiveRecord::Base
    has_many :table1
    end

    class f will now have a method called ‘table1_ids’ to retrieve an array
    of ids on the has_many side of the relationship…this is just part of
    the rails magic :slight_smile:
    However, with the self referential join thing, we did some extra stuff
    like defining :child_topics
    so now the method that rails creates is ‘child_topic_ids’…notice how
    in _form.rhtml I call ‘topic[child_topic_ids][]’, then rails figures it
    out from there.

    Actually, the way I have it set up is more along the lines of
    associating child topics with an already existing topic. How is your UI
    flow working?

    [new topic]->[show list of topics]->[associate child topics]

    something like that?


    #9

    rooster: Thank you SO MUCH! What an awesome surprise to find this
    morning. Haven’t had time to try it yet but I most especially appreciate
    the background you gave on the methods Rails builds automatically.
    Extremely helpful to me at this stage.

    Thanks again - I will most likely be back with more questions. Oh -
    quick answer to the UI flow. Your outline is pretty much it so far. I
    need to establish categories (topics) before I can begin adding links
    since the links will all be in categories. And the “public” will never
    see this end - this is strictly admin for me. Ultimately the public will
    see categories and their links but not be allowed to manipulate them.
    Anyway, in the TopicsAdmin one clicks “new topic”, goes to a form with
    input areas for topic title (short text) and topic description (slightly
    longer text), and then all the checkboxes, one for each topic existing
    in the database. This is where the parent assignment occurs. I
    anticipate a similar structure for links with the addition of a URL
    field. In that case I did plan to use a join table and in that case one
    link can be assigned to many parents, so I expect it might get a bit
    tricky there.

    But thanks again for your involvement in this and I’ll let you know what
    happens when I try putting this code into mine.

    Steve


    #10

    Sigh.

    It’s probably pretty obvious I have a “day job” and can only hack away
    at Ruby in the evenings, huh?
    :frowning:
    Okay, turns out the code rooster gave me was effective but for the wrong
    thing. It allowed me to assign children to the newly created topic,
    but my intention was to establish the parent of the new topic. I can’t
    see any other way to do it in the beginning - you need to start the tree
    somewhere and you can’t assign children before there are any parents!

    So. I almost got it. In the _form partial I now have:

      <% Topic.find(:all).each do |t| %>
    • <%= check_box_tag 'topic[parent_id]', t.id %><%= t.topic_title %> <% end %>

    This works to write the id of the topic represented by a checked
    checkbox into the new topic’s “parent_id” field. Yeah!

    I say “almost” because I still can’t get the last argument in the form
    helper (the one that knows to put the check in the proper box when you
    edit a topic), and the children of a topic don’t show up in the “show”
    action.

    In “show.rhtml” I’ve got:

    Topic Children:
    <% @topic.child_topics.each do |ct|%> <%= ct.topic_title %> <% end %>

    but what displays is the topic itself, not the topics that have the
    current topic’s id in their “parent_id” field. What should I be telling
    you folk besides what I have to make it easier to pinpoint what I’d
    doing wrong?

    Argh.

    So - how do you know the stuff you wrote about the “methods that Rails
    creates”? Do you just know it from experience? Do you scour the api doc?
    Is there any means of looking somewhere in your Rails project to find
    the methods, ALL the methods including those generated, hand created AND
    automatically created? Being new to Rails (and Ruby) it’s making me
    absolutely crazy not knowing how to do diagnostics, to find out what
    variable is getting really getting passed, what method is being
    called. I’m sure the tools are in there but I don’t know where to look
    for them, how to employ them.

    Thanks.


    #11

    rooster;

    This finally seems to work just right. THANKS! Boy, I was pecking away
    at every lame thing I could think of (empty? blank?) but I don;t think I
    would have ever stumbled onto eql?

    (Is that a streamlined if x == y sort of conditional?)

    I don’t “get” why Parent_id works as the has_many foreign key, but
    presumably that will sink in after a bit.

    So - I will start scouring the wiki. Thanks for that tip, and thanks for
    your interest and tenacity working this out. Now on to the next hurdle.

    Steve


    #12

    equ? is an alias for ==

    Glad I could help out. Your problem was fun to work on and very
    educational for me too! Good luck with the next step.

    -steven


    #13

    I know how you feel about rails. It kicks ass but a lot intricate
    things are not well documented. Well, compared to some of the php crap
    I’ve used, it has tons of documentation! Anyhoo, I found out about
    those autogenerated methods in the rails wiki. This article:
    http://wiki.rubyonrails.com/rails/pages/CheckboxHABTM

    The wiki is actually loaded with great info. It’s just not organized so
    well. I usually spend a couple of hours a week reading through stuff on
    the wiki…reading things twice doesn’t hurt either :wink:

    Well I think I got your situation solved:

    _form.rhtml

    <%= error_messages_for ‘topic’ %>

    Name
    <%= text_field 'topic', 'name' %>

    parent topics
    <% Topic.find(:all).each do |t| %> <%= check_box_tag 'topic[parent_id]', t.id, @topic.parent_topic.eql?(t) %> <%= t.name %>
    <% end %>

    ####

    show.rhtml

    <% for column in Topic.content_columns %>

    <%= column.human_name %>: <%=h @topic.send(column.name) %>

    Parent Topic: <%= @topic.parent_topic.name unless @topic.parent_topic.nil?%>

    Child Topics:
    <% @topic.child_topics.each do |ct| %> <%= ct.name %>
    <% end %>

    <% end %>

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

    topic.rb

    class Topic < ActiveRecord::Base

    validates_uniqueness_of :name
    

    belongs_to :parent_topic,
    :class_name => ‘Topic’,
    :foreign_key => ‘parent_id’
    has_many :child_topics,
    :class_name => ‘Topic’,
    :foreign_key => ‘parent_id’
    end

    Does this accomplish what you were shooting for?

    -Steven