ActiveRecord, has_many and _count ...

Discussion in 'Ruby' started by Éric DUMINIL, Aug 28, 2006.

  1. Hi everybody!

    Here are trunks of models from a little rails app I'm developing:

    ###########################
    class Country < ActiveRecord::Base
    has_many :companies
    ........

    ###########################
    class Category < ActiveRecord::Base
    has_many :companies
    ........

    ###########################
    class Company < ActiveRecord::Base
    has_many :employees , :dependent => true
    belongs_to :country, :counter_cache => true
    belongs_to :category, :counter_cache => true
    ........

    ###########################

    class Employee < ActiveRecord::Base
    belongs_to :company, :counter_cache => true
    ........

    ###########################


    I just would like to know: The right way to create a new company by updating
    Country.find(id).companies.size is by doing
    Country.find(id).companies.create . Right?

    1) But what if I'd like to keep in cache companies.size for the category in
    which the new company is put? I can't create a company simultaneously using
    two parents!

    2) When I edit the properties of a company, let's say moving from Japan to
    Italy, how could I update Japan.companies_count and Italy.companies_count?
    I tried to modify the update action of CompanieController:

    ###########################

    def update
    @company = Company.find(params[:id])
    @oldcountry=@company.country
    if @company.update_attributes(params[:company])
    @newcountry=@company.country
    @oldcountry.companies_count=@oldcountry.companies:)refresh).size
    @newcountry.companies_count=@newcountry.companies:)refresh).size
    flash[:notice] = 'Company was successfully updated.'
    redirect_to :action => 'show', :id => @company
    else
    render :action => 'edit'
    end
    end

    ###########################

    but that did'nt work. :(


    3) Is there a Rails' way to cache the number of Employees directly in
    Company.employees_count and Category.employees_count (i.e. through
    a "has_many_many" relation)? That would prevent iterating through every
    company of a given country/category to get its .employees_count!

    Thanks a lot for your attention,

    Have a good night,

    Eric
     
    Éric DUMINIL, Aug 28, 2006
    #1
    1. Advertising

  2. Éric DUMINIL

    Max Muermann Guest

    I think you want the Rails mailing list...

    Max

    On 8/29/06, =C9ric DUMINIL <> wrote:
    > Hi everybody!
    >
    > Here are trunks of models from a little rails app I'm developing:
    >
    > ###########################
    > class Country < ActiveRecord::Base
    > has_many :companies
    > ........
    >
    > ###########################
    > class Category < ActiveRecord::Base
    > has_many :companies
    > ........
    >
    > ###########################
    > class Company < ActiveRecord::Base
    > has_many :employees , :dependent =3D> true
    > belongs_to :country, :counter_cache =3D> true
    > belongs_to :category, :counter_cache =3D> true
    > ........
    >
    > ###########################
    >
    > class Employee < ActiveRecord::Base
    > belongs_to :company, :counter_cache =3D> true
    > ........
    >
    > ###########################
    >
    >
    > I just would like to know: The right way to create a new company by updat=

    ing
    > Country.find(id).companies.size is by doing
    > Country.find(id).companies.create . Right?
    >
    > 1) But what if I'd like to keep in cache companies.size for the category =

    in
    > which the new company is put? I can't create a company simultaneously usi=

    ng
    > two parents!
    >
    > 2) When I edit the properties of a company, let's say moving from Japan t=

    o
    > Italy, how could I update Japan.companies_count and Italy.companies_coun=

    t?
    > I tried to modify the update action of CompanieController:
    >
    > ###########################
    >
    > def update
    > @company =3D Company.find(params[:id])
    > @oldcountry=
    > if @company.update_attributes(params[:company])
    > @newcountry=
    > @oldcountry.companies_count=:)refresh).size
    > @newcountry.companies_count=:)refresh).size
    > flash[:notice] =3D 'Company was successfully updated.'
    > redirect_to :action =3D> 'show', :id =3D> @company
    > else
    > render :action =3D> 'edit'
    > end
    > end
    >
    > ###########################
    >
    > but that did'nt work. :(
    >
    >
    > 3) Is there a Rails' way to cache the number of Employees directly in
    > Company.employees_count and Category.employees_count (i.e. through
    > a "has_many_many" relation)? That would prevent iterating through every
    > company of a given country/category to get its .employees_count!
    >
    > Thanks a lot for your attention,
    >
    > Have a good night,
    >
    > Eric
    >
    >
    >
    >
    >
     
    Max Muermann, Aug 28, 2006
    #2
    1. Advertising

  3. Éric DUMINIL

    zdennis Guest

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    Éric DUMINIL wrote:

    > I just would like to know: The right way to create a new company by updating
    > Country.find(id).companies.size is by doing
    > Country.find(id).companies.create . Right?


    This is one way of doing it. Although if you have the id of the Country
    you can just say:
    company = Company.create( :company_name=>'ABC Co', :country_id=>1 )
    OR
    company = Company.new( :company_name=>'ABC Co', :country_id=>1 )
    company.save

    Both of these will update the country countries_counter because you have
    defined this in your belongs_to relationship.


    >
    > 1) But what if I'd like to keep in cache companies.size for the category in
    > which the new company is put? I can't create a company simultaneously using
    > two parents!


    This can be solved using create or save on Company itself, rather then
    doing Country.find.companies.create. Since you have defined the
    belongs_to relationship and specified the counter cache ActiveRecord
    will be smart enough to update both of them. The following example will
    work:
    Company.create :company_name=>'ABC', :country_id=>1, :category_id=>1
    >
    > 2) When I edit the properties of a company, let's say moving from Japan to
    > Italy, how could I update Japan.companies_count and Italy.companies_count?


    If you edit the properties of a company and save it ActiveRecord will
    correctly increment/decrement the counter cache fields that you have
    placed on your tables.

    Using :refresh will ensure that you get the most recent data from the
    database.

    >
    > 3) Is there a Rails' way to cache the number of Employees directly in
    > Company.employees_count and Category.employees_count (i.e. through
    > a "has_many_many" relation)? That would prevent iterating through every
    > company of a given country/category to get its .employees_count!


    I don't know about the count via a another relationship, but you can
    always count them at once rather then iterating over each company.

    Category.count(
    :joins=>"INNER JOIN #{Company.table_name} ON " +
    "#{Company.table_name}.category_id=" +
    "#{Category.table_name}.id " +
    "INNER JOIN #{Employee.table_name} ON " +
    "#{Employee.table_name}.company_id=#{Company.table_name}.id",
    :group=>"#{Category.table_name}.id" )

    This will give you an array of arrays, where each internal array is
    employee_count, category_id:
    [ [ 4, 1 ], [ 2, 2 ], [ 30, 3 ] ]

    It doesn't handle categories that do not have any employees although
    that could be fixed.

    This is perhaps not as simple of a solution you're looking for. Perhaps
    someone else knows if you can do counter_cache using :through. ;)

    Zach






    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.2.2 (GNU/Linux)
    Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

    iD8DBQFE86bfMyx0fW1d8G0RAukPAJ9h4COcF11Zrzo0AVqjV1t8Il1hjACfcbuQ
    7Qqh6h2BI4zt9P8WjMq59cg=
    =Nz8h
    -----END PGP SIGNATURE-----
     
    zdennis, Aug 29, 2006
    #3
  4. Éric DUMINIL

    zdennis Guest

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    zdennis wrote:

    >
    > This is one way of doing it. Although if you have the id of the Country
    > you can just say:
    > company = Company.create( :company_name=>'ABC Co', :country_id=>1 )
    > OR
    > company = Company.new( :company_name=>'ABC Co', :country_id=>1 )
    > company.save
    >
    > Both of these will update the country countries_counter because you have

    ^^^^^^^^^^^^^^^
    should be companies_counter. ;)

    Zach
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.2.2 (GNU/Linux)
    Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

    iD8DBQFE86hbMyx0fW1d8G0RAsWHAJ0XsQqlFXZePu0A+xXdsw3FRn4DfQCfUAyX
    xNpxNiX3RqhDAfutQC3Ud1k=
    =P1Yo
    -----END PGP SIGNATURE-----
     
    zdennis, Aug 29, 2006
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    2
    Views:
    117
    Eero Saynatkari
    Dec 12, 2005
  2. treefrog

    Rails: has_many issue

    treefrog, Feb 6, 2006, in forum: Ruby
    Replies:
    1
    Views:
    172
    Eric Hodel
    Feb 6, 2006
  3. inanc
    Replies:
    1
    Views:
    134
    Eric Hodel
    Sep 4, 2006
  4. Sven S.
    Replies:
    1
    Views:
    134
    Sven S.
    Mar 23, 2010
  5. Replies:
    0
    Views:
    714
Loading...

Share This Page