I have a database structured like this:
Groups has_many Packages
Packages has_many Rooms
Rooms has_and_belongs_to_many Clients
Ok. Every room can be 'Quadruple', 'Triple', 'Double', etc. And I can access all the rooms belonging to a Group by doing Group.rooms.
I want to be able to get how many clients per type of room the Group has.
For example:
Quad: 16 clients
Double: 10 clients
*etc.
I managed to get the ammount of Rooms per type, like this:
Group.rooms.group('type').count
Any ideas?
UPDATE 1 - Models
Quarto.rb (Rooms)
class QuartoContratado < ActiveRecord::Base
belongs_to :pacote
has_and_belongs_to_many :clientes, :join_table => :acomodacoes
Pacote.rb (Packages)
class Pacote < ActiveRecord::Base
belongs_to :grupo
has_many :passageiros
has_many :clientes, :through => :passageiros
has_many :quartos, :class_name => "QuartoContratado"
Grupo.rb (Groups)
class Grupo < ActiveRecord::Base
has_many :pacotes
has_many :clientes, :through => :pacotes, :conditions => { :pacotes => { :cancelado => false } }
has_many :quartos, :class_name => "QuartoContratado", :through => :pacotes, :conditions => { :pacotes => { :cancelado => false } }
Client.rb (Clients)
class Cliente < ActiveRecord::Base
has_many :passageiros
has_many :pacotes, :class_name => "Pacote", :through => :passageiros
has_many :grupos, :through => :pacotes, :conditions => { :pacotes => { :cancelado => false } }
This is how I managed to get what I wanted... it looks ugly! Any better way to write it?
group = Grupo.first
types = group.quartos.group_by { |t| t.type }
types.each do |key, value|
print "#{key} - "
c=0
value.each do |v|
c= c+v.clientes.count
end
puts "#{c} clients"
end
I have 2 models. User and Project. And there is a many_to_many relation with and extra position field between them. I can't see that extra field on rails_admin edit page.
How can add that field to form?
user.rb
has_many :projects, :through => :works_ons
project.rb
has_many :users, :through => :works_ons
works_on.rb
attr_accessible :position, :project_id, :user_id
belongs_to :user
belongs_to :project
Is it true that your user model has_many :users, :through => :works_ons?
I'm wondering if you need
user.rb
has_many :projects, :through => :works_ons
This is the closest I found. You should probably add a custom field below the association and enter the extra fields.
https://github.com/sferik/rails_admin/wiki/Has-many-%3Athrough-association
I have Group model that has_many institutions, and schools.
class Group
has_many :institutions
has_many :schools
end
However I need my Group to have_and_belongs_to_many :users but I want all the users that are associated through the institutions and schools like this:
class Group
has_many :institutions
has_many :users, :through => :instiutions
has_many :schools
has_many :users, :through => :schools
end
class School
belongs_to :group
has_many :educations
has_many :users, :through => :educations
end
class Institution
belongs_to :group
has_many :institutional_educations
has_many :users, :through => :institutional_educations
end
Obviously this isn't possible so what should I do?
Just offhand, have you considered using single table inheritance, so that school is a type of institution?
Your School class would inherit from Institution ("class School < Institution"). Plus, I guess you'd call it something else but you could have "class GenericInstitution < Institution" as well. Then your Group class could look like this:
class Group
:has_many :school_users, :through => :schools,
:has_many :generic_institution_users, :through => :generic_institutions
# if you need all of the users at once:
:has_many :users, :through => :institutions
end
You'd probably have to specify a foreign key or two to get this to work for you.
Also, I can't quite figure see what an :institutional_education is, but if you really need it you could do the same thing there (maybe "class institutional_education < education", or maybe the other way around.
I'm having a little trouble with querying multiple tables with different types of association. Can someone please point me in the right direction?
class Sale < ActiveRecord::Base
has_many :items, :dependent => :destroy
end
class Item < ActiveRecord::Base
belongs_to :sale, :dependent => :destroy
has_many :images, :dependent => :destroy
end
class Image < ActiveRecord::Base
belongs_to :item, :dependent => :destroy
end
What would be the query to get all items relating to a sale with the ID 1, and then loop through all the images relating to each item returned?
Thanks for your help.
You can define :through => :something in a has_many association
class Sale < ActiveRecord::Base
has_many :items, :dependent => :destroy
has_many :images, :through => :items
end
and then simply query
Sale.find(1).images
I have badges (sorta like StackOverflow).
Some of them can be attached to badgeable things (e.g. a badge for >X comments on a post is attached to the post). Almost all come in multiple levels (e.g. >20, >100, >200), and you can only have one level per badgeable x badge type (= badgeset_id).
To make it easier to enforce the one-level-per-badge constraint, I want badgings to specify their badge by a two-column foreign key - badgeset_id and level - rather than by primary key (badge_id), though badges does have a standard primary key too.
In code:
class Badge < ActiveRecord::Base
has_many :badgings, :dependent => :destroy
# integer: badgeset_id, level
validates_uniqueness_of :badgeset_id, :scope => :level
end
class Badging < ActiveRecord::Base
belongs_to :user
# integer: badgset_id, level instead of badge_id
#belongs_to :badge # <-- how to specify?
belongs_to :badgeable, :polymorphic => true
validates_uniqueness_of :badgeset_id, :scope => [:user_id, :badgeable_id]
validates_presence_of :badgeset_id, :level, :user_id
# instead of this:
def badge
Badge.first(:conditions => {:badgeset_id => self.badgeset_id, :level => self.level})
end
end
class User < ActiveRecord::Base
has_many :badgings, :dependent => :destroy do
def grant badgeset, level, badgeable = nil
b = Badging.first(:conditions => {:user_id => proxy_owner.id, :badgeset_id => badgeset,
:badgeable_id => badgeable.try(:id), :badgeable_type => badgeable.try(:class)}) ||
Badging.new(:user => proxy_owner, :badgeset_id => badgeset, :badgeable => badgeable)
b.level = level
b.save
end
end
has_many :badges, :through => :badgings
# ....
end
How I can specify a belongs_to association that does that (and doesn't try to use a badge_id), so that I can use the has_many :through?
ETA: This partially works (i.e. #badging.badge works), but feels dirty:
belongs_to :badge, :foreign_key => :badgeset_id, :primary_key => :badgeset_id, :conditions => 'badges.level = #{level}'
Note that the conditions is in single quotes, not double, which makes it interpreted at runtime rather than loadtime.
However, when trying to use this with the :through association, I get the error undefined local variable or method 'level' for #<User:0x3ab35a8>. And nothing obvious (e.g. 'badges.level = #{badgings.level}') seems to work...
ETA 2: Taking EmFi's code and cleaning it up a bit works. It requires adding badge_set_id to Badge, which is redundant, but oh well.
The code:
class Badge < ActiveRecord::Base
has_many :badgings
belongs_to :badge_set
has_friendly_id :name
validates_uniqueness_of :badge_set_id, :scope => :level
default_scope :order => 'badge_set_id, level DESC'
named_scope :with_level, lambda {|level| { :conditions => {:level => level}, :limit => 1 } }
def self.by_ids badge_set_id, level
first :conditions => {:badge_set_id => badge_set_id, :level => level}
end
def next_level
Badge.first :conditions => {:badge_set_id => badge_set_id, :level => level + 1}
end
end
class Badging < ActiveRecord::Base
belongs_to :user
belongs_to :badge
belongs_to :badge_set
belongs_to :badgeable, :polymorphic => true
validates_uniqueness_of :badge_set_id, :scope => [:user_id, :badgeable_id]
validates_presence_of :badge_set_id, :badge_id, :user_id
named_scope :with_badge_set, lambda {|badge_set|
{:conditions => {:badge_set_id => badge_set} }
}
def level_up level = nil
self.badge = level ? badge_set.badges.with_level(level).first : badge.next_level
end
def level_up! level = nil
level_up level
save
end
end
class User < ActiveRecord::Base
has_many :badgings, :dependent => :destroy do
def grant! badgeset_id, level, badgeable = nil
b = self.with_badge_set(badgeset_id).first ||
Badging.new(
:badge_set_id => badgeset_id,
:badge => Badge.by_ids(badgeset_id, level),
:badgeable => badgeable,
:user => proxy_owner
)
b.level_up(level) unless b.new_record?
b.save
end
def ungrant! badgeset_id, badgeable = nil
Badging.destroy_all({:user_id => proxy_owner.id, :badge_set_id => badgeset_id,
:badgeable_id => badgeable.try(:id), :badgeable_type => badgeable.try(:class)})
end
end
has_many :badges, :through => :badgings
end
While this works - and it's probably a better solution - I don't consider this an actual answer to the question of how to do a) multi-key foreign keys, or b) dynamic-condition associations that work with :through associations. So if anyone has a solution for that, please speak up.
Seems like it might workout best if you separate Badge into two models. Here's how I'd break it down to achieve the functionality you want. I threw in some named scopes to keep the code that actually does things clean.
class BadgeSet
has_many :badges
end
class Badge
belongs_to :badge_set
validates_uniqueness_of :badge_set_id, :scope => :level
named_scope :with_level, labmda {|level
{ :conditions => {:level => level} }
}
named_scope :next_levels, labmda {|level
{ :conditions => ["level > ?", level], :order => :level }
}
def next_level
Badge.next_levels(level).first
end
end
class Badging < ActiveRecord::Base
belongs_to :user
belongs_to :badge
belongs_to :badge_set
belongs_to :badgeable, :polymorphic => true
validates_uniqueness_of :badge_set_id, :scope => [:user_id, :badgeable_id]
validates_presence_of :badge_set_id, :badge_id, :user_id
named_scope :with_badge_set, lambda {|badge_set|
{:conditions => {:badge_set_id => badge_set} }
}
def level_up(level = nil)
self.badge = level ? badge_set.badges.with_level(level).first
: badge.next_level
save
end
end
class User < ActiveRecord::Base
has_many :badgings, :dependent => :destroy do
def grant badgeset, level, badgeable = nil
b = badgings.with_badgeset(badgeset).first() ||
badgings.build(
:badge_set => :badgeset,
:badge => badgeset.badges.level(level),
:badgeable => badgeable
)
b.level_up(level) unless b.new_record?
b.save
end
end
has_many :badges, :through => :badgings
# ....
end