Association problems, :belongs_to and :has_many

Hi everyone, I’m trying to make my first rails app which is a product
catalog. I have finished an administrative back-end and added data but
am currently having problems figuring out how to list the products in
the front end. Each product belongs to a section and type (ie: the
product “western shirt” would be in section “vintage” and type
“shirts”). I also gave the product model a gender type which is just
a hard coded inclusion list. The product to section/typ mode
relationship is belongs_to and :has many. I would like to show lists of
product “types” in a specific section for males and females, basically
like the folowing…

Section - Vintage

 Male Types

    Jackets -> Product 1, Product 2, Product 3
    Jeans
    Shirts

Female Types

    Jackets
    Jeans
    Shirts

Currently, I’m able to show selected products in a section by gender
using the following in my store controller…

def show_section
@sections = Section.find(:all)
@section = Section.find_by_permalink(params[:id])
@products = Product.find(:first)
@male_products =
@section.products.find_all_by_gender([‘male’,‘unisex’])
@female_products =
@section.products.find_all_by_gender([‘female’,‘unisex’])
end

Can anyone point me in the right direction to get my view working in the
way I’ve described above? Any help would be greatly appreciated. I
have a bad feeling I may have been doing this all backwards.

Perhaps something like this works: Parked at Loopia

It’s pretty dirty though…

[email protected] wrote:

Perhaps something like this works: Parked at Loopia

It’s pretty dirty though…

Hmm, I’ll have to look through this more in depth, it does seem sort of
roundabout but thank you. Would a join table between the section and
type models via a :has_many :through relationship work here?

Maybe this isn’t the most efficient method but I think I may be able to
do this by listing all the types like so…

in controller

@section = Section.find_by_permalink(params[:id])
@male_products = 

@section.products.find_all_by_gender([‘male’,‘unisex’])

then in my view

<h3>Male</h3>
<ul>

<% for product in @male_products %>

  • <%=h(product.type.name)%>

  • <% end %>

    However this outputs duplicate types given that there are products in
    @male_products with the same type. So I get something like this…

    Male
    Pants
    Pants
    Jackets
    Jackets
    Jackets
    Shirts

    Is there a way to only show each type once to avoid duplicates?

    On 5/4/07, Raj O. [email protected] wrote:

    @male_products with the same type. So I get something like this…

    Male
    Pants
    Pants
    Jackets
    Jackets
    Jackets
    Shirts

    Is there a way to only show each type once to avoid duplicates?

    Raj,

    In addition to anything else that you do, you may want to change the
    column
    name from “type” to something else. This may be effecting it since the
    type
    column has a special purpose in AR and shouldn’t be used except for STI.

    One option for removing the duplicates would be to specify a distintct
    call
    on the db.
    has_many :products, :select => “SELECT DISTINCT * FROM”

    Cheers
    Daniel

    On 5/4/07, Raj O. [email protected] wrote:

    Thanks for the tip, I changed the typ attribute to kind instead,
    hopefully this is not a reserved word either. I also think I have a
    join table working between sections and kinds. However I am having the
    same problem with duplicate entries, can I use some select attribute for
    this too?

    Raj,

    I’m having a little trouble trying to visualise your data structure.
    Do
    you think you could post the minimal relevant class declerations with
    the
    associtation declerations included.

    Cheers

    Daniel ----- wrote:

    On 5/4/07, Raj O. [email protected] wrote:

    @male_products with the same type. So I get something like this…

    Male
    Pants
    Pants
    Jackets
    Jackets
    Jackets
    Shirts

    Is there a way to only show each type once to avoid duplicates?

    Raj,

    In addition to anything else that you do, you may want to change the
    column
    name from “type” to something else. This may be effecting it since the
    type
    column has a special purpose in AR and shouldn’t be used except for STI.

    One option for removing the duplicates would be to specify a distintct
    call
    on the db.
    has_many :products, :select => “SELECT DISTINCT * FROM”

    Cheers
    Daniel
    Thanks for the tip, I changed the typ attribute to kind instead,
    hopefully this is not a reserved word either. I also think I have a
    join table working between sections and kinds. However I am having the
    same problem with duplicate entries, can I use some select attribute for
    this too?

    On 5/4/07, Raj O. [email protected] wrote:

    validates_length_of :name, :minimum => 4
    end
    “section_id”
    has_many :products, :class_name => “Product”, :foreign_key =>

    want to delete that row from the join table as well. I hope I’m making
    sense.

    Removing the duplicates in this instance should be as easy as changing
    the
    HABTM declerations in your kind and section models by setting the :uniq
    option to true. Any duplicates should then be ignored.

    class Kind < ActiveRecord::Base

    has_and_belongs_to_many :sections, :uniq => true
    end

    and

    class Section < ActiveRecord::Base

    has_and_belongs_to_many :kinds
    end

    Hopefully I’ve understood the issue there :wink:
    Cheers

    Sorry a bit hasty with the go button.

    You will need to add the :uniq => true option to both kind and section
    class
    HABTM’s

    Daniel ----- wrote:

    On 5/4/07, Raj O. [email protected] wrote:

    Thanks for the tip, I changed the typ attribute to kind instead,
    hopefully this is not a reserved word either. I also think I have a
    join table working between sections and kinds. However I am having the
    same problem with duplicate entries, can I use some select attribute for
    this too?

    Raj,

    I’m having a little trouble trying to visualise your data structure.
    Do
    you think you could post the minimal relevant class declerations with
    the
    associtation declerations included.

    Cheers
    sorry for my lack of articulation, here are my classes

    class Product < ActiveRecord::Base
    set_table_name “products”
    set_primary_key “id”

    validates_presence_of :name
    validates_uniqueness_of :name
    validates_length_of :name, :minimum => 4

    validates_inclusion_of :gender, :in => [‘Male’, ‘Female’, ‘Unisex’],
    :message => “please pick a gender”
    validates_presence_of :gender

    validates_presence_of :section_id

    belongs_to :section, :class_name => “Section”, :foreign_key =>
    “section_id”
    belongs_to :kind, :class_name => “Kind”, :foreign_key => “kind_id”
    end

    class Section < ActiveRecord::Base
    set_table_name “sections”
    set_primary_key “id”

    validates_presence_of :name
    validates_uniqueness_of :name
    validates_length_of :name, :minimum => 2

    has_many :products, :class_name => “Product”, :foreign_key =>
    “section_id”
    has_and_belongs_to_many :kinds
    end

    class Kind < ActiveRecord::Base
    set_table_name “kinds”
    set_primary_key “id”

    validates_presence_of :name
    validates_uniqueness_of :name

    has_many :products, :class_name => “Product”, :foreign_key =>
    “kind_id”
    has_and_belongs_to_many :sections
    end

    my join table is named “kinds_sections” and it has “kind_id” and
    “section_id” columns (no index id)

    I’m using this bit of code to append the section and kind id’s to the
    join table when a new product is created or updated in my products
    controller.

    if @kind = Kind.find_by_id(params[:kind_id])
    @product.kind = @kind
    @product.section.kinds << @product.kind
    end

    Maybe append is not the right modifier to use here? I probably need to
    have some check in there to see if that kind and section has already
    been added to the join_table. Plus, I’m not sure how to go about
    deletion of a product in relation to the join table, if a product is
    deleted and is the only product of that section and kind, then I would
    want to delete that row from the join table as well. I hope I’m making
    sense.

    Daniel ----- wrote:

    Sorry a bit hasty with the go button.

    You will need to add the :uniq => true option to both kind and section
    class
    HABTM’s

    Thanks so much for your help, this looks like it could be a better
    solution than what I just came up with, however while I only want one
    kind per section, I do want kinds in multiple sections, meaning…

    the “kind” jackets should be allowed in sections “vintage” and “new” but
    “vintage” and “new” sections should only have one instance of jackets in
    each of them, does that make sense? I just want to avoid the
    following…

    section vintage

     associated kinds
          jackets
          jackets
          pants
          pants
    

    but maybe I’m misunderstanding how the “:uniq => true” attribute works.

    I used the following code to check if a kind already exists in a section
    when creating or updating a product. In the product controller update
    and create actions I added…

    if @kind = Kind.find_by_id(params[:kind_id])
    @product.kind = @kind
    unless @product.section.kinds.find_by_id(params[:kind_id]) ==
    @product.kind
    @product.section.kinds << @product.kind
    end
    end

    I’m still confused about how to delete it from the join table, if I
    delete a jacket product from a section and it is the only jacket in that
    section, than I would want to remove that entry from the “section”
    “kinds” join_table, otherwise it should stay.

    Thank you again for all your help daniel.

    On 5/4/07, Raj O. [email protected] wrote:

    solution than what I just came up with, however while I only want one
    jackets
    if @kind = Kind.find_by_id(params[:kind_id])
    @product.kind = @kind
    unless @product.section.kinds.find_by_id(params[:kind_id]) ==
    @product.kind
    @product.section.kinds << @product.kind
    end
    end

    I’m still confused about how to delete it from the join table, if I

    delete a jacket product from a section and it is the only jacket in that
    section, than I would want to remove that entry from the “section”
    “kinds” join_table, otherwise it should stay.

    Thank you again for all your help daniel.

    I’m sorry my head might be packed with cotton this morning, but I’m
    really
    not understanding what your trying to do.
    It seems that the joining logic your trying to put into these
    controllers
    should exist in the models since they are related to your data
    integrity.
    You would probably be better off using has_many :through instead of
    HABTM
    because you can put your join logic into these fully blown rich join
    models. Adding joining logic to habtm is messy because you need to put
    it
    all over the place. By including it into the join model it keeps all
    the
    joining logic dry. You can also use the :uniq try option where you need
    it
    to.

    Hope that helps
    Daniel