I found this recently when trying to do bidirectional relationships in rails
(http://www.dweebd.com/sql/modeling-bidirectional-graph-edges-in-rails/)
class Befriending < ActiveRecord::Base
belongs_to :initiator, :class_name => :User
belongs_to :recipient, :class_name => :User
after_create do |b|
BefriendingEdge.create!(:user => b.initiator, :befriending => b)
BefriendingEdge.create!(:user => b.recipient, :befriending => b)
end
end
class BefriendingEdge < ActiveRecord::Base
belongs_to :user
belongs_to :befriending
end
class User < ActiveRecord::Base
has_many :befriending_edges
has_many :friends, :through => :befriending_edges, :source => :user
has_many :befriendings, :through => :befriending_edges, :source => :befriending
end
But I just don't quite understand how it works. Can anyone helps explain to me. It looks like a double belongs_to. Just not quite understanding this.
Thanks
I'm a user
I have friends
My friends are also users
The way to model this using a graph (http://en.wikipedia.org/wiki/Graph_%28mathematics%29) is with
nodes that represent users/friends
edges that represent friendship links
So yes: in databases terms, "users belong to users" : my friends are also users. But in addition, friendship is bi-directional: if we're friends that means, I'm your friend AND you're my friend.
Also, using a separate model to store edges/relationships allows you potentially store additional information about the friendship (e.g. "friends since").
Related
So I have the following models
User, Course, Order, Line_item
User (seller) has_many :courses (as the instructor, uploading them)
On the other hand...
User (buyer): has_many :orders
Order: has many :line_items
line_item: belongs_to :course
So I want a list of all courses purchased by a buyer, can I at this point use #user.courses to do so? How can I distinguish between buyer.courses and seller.courses?
Any help is appreciated. Thanks!
You need to use self-referential association for this.
#cousre.rb
Class Course < ActiveRecord::Base
belongs_to :sellar, :class_name => 'User', :foreign_key => 'sellar_id'
end
#order.rb
Class Order < ActiveRecord::Base
belongs_to :buyer, :class_name => 'User', :foreign_key => 'buyer_id'
end
I have a model of users and things
Users can have one leader and many followers which are other users. User can also have things
So I have these definitions which use a self join relationship:
class User < ActiveRecord::Base
has_many :things, dependent: :destroy
has_many :followers, :class_name => 'User', :foreign_key => 'leader_id'
belongs_to :leader, :class_name => 'User', :foreign_key => 'leader_id'
end
class Thing < ActiveRecord::Base
belongs_to :user
end
So I can query ask for a list of things that a user has by asking, for example User.first.things. I can also get a list of followers of a user with User.first.followers.
How do I get a list of things that a user's followers have. I think I might need to use a has_many through relationship but I can't seem to figure it out as I'm not sure how to deal with the fact that a Leader can have things through a 'follower' but also directly themselves
Thanks
Something like this:
def all_things
Thing.where(:user_id => followers.map(&:id).push(id))
end
It returns a scope so you should be able to continue the chain, for example:
User.first.all_things.visible
Update
If you are only interested in the followers' things without adding the user's things to the batch it is better is you do it directly with a has_many through:
has_many :followers_things, :through => :followers, :source => :things
Check this other SO thread
How about:
all_things = (your_user.followers.each { |f| f.things }).flatten
This should return an array containing things that belong to all your_user's followers.
Here is what I'm trying to do:
class Cashflow < ActiveRecord::Base
belongs_to from_account, :class_name => 'Account'
belongs_to to_account, :class_name => 'Account'
end
class Account < ActiveRecord::Base
has_many :cashflows
end
where Account::cashflows is obviously a list of all cashflows that either have the account_id stored in from_account or in to_account.
I'm confused. What is the proper way of handling such a case? How bad design is this? What would be the proper way of designing such a relation?
I think you have the right structure as there can only two accounts be involved in a particular transaction/cashflow. if you use many to many association you would need to handle the validation for not involving more or less than 2 accounts. For your current structure you can change your moidel associations to be:
class Cashflow < ActiveRecord::Base
belongs_to from_account, :class_name => 'Account', :foreign_key => :from_account
belongs_to to_account, :class_name => 'Account', :foreign_key => :to_account
end
class Account < ActiveRecord::Base
has_many :debits, :class_name => 'Cashflow', :foreign_key => :from_account
has_many :credits, :class_name => 'Cashflow', :foreign_key => :to_account
def cashflows
transactions = []
transactions << self.debits
transactions << self.credits
transactions.flatten!
## or may be the following commented way
# Cashflow.where('from_account = ? OR to_account = ?', self.id, self.id)
end
end
This way you can keep track of the amount debited/credited in a particular account and also get the accounts involved in a particular transaction/cashflow.
Suggestions on top of my mind
1) Your class (table) cashflows should have two columns from_account and to_account.
2) from_account and to_account should have the id of the account concerned
3) cashflows should belongs_to :account
4) account should has_many :cashflows. Ideally it should be cash_flows
These should be good starting points. Don't they meet your requirements?
I think you should use has and belongs to many association here:
class Account < ActiveRecord::Base
has_and_belongs_to_many :incoming_cashflows, :class_name => 'Cashflow', :join_table => :incoming_cashflows_accounts
has_and_belongs_to_many :outcoming_cashflows, :class_name => 'Cashflow', :join_table => :outcoming_cashflows_accounts
end
class Cashflow < ActiveRecord::Base
has_and_belongs_to_many :from_accounts, :class_name => 'Account', :join_table => :incoming_cashflows_accounts
has_and_belongs_to_many :to_accounts, :class_name => 'Account', :join_table => :outcoming_cashflows_accounts
end
Also you will need some validation code allows to add only one account to Cashflow.
I am having a somewhat too nested database layout, however, I seem to need it. That is, Our website visitors may each have a single account with maintaining multiple users (think of identities) within.
Now they may create tickets, which are grouped by ticket sections, and we have ticket manager (operator) to process the incoming tickets.
Not every ticket manager may see every ticket but only those this manager is a member of the given ticket section for.
Now, I am totally fine in querying via raw SQL statements, but I failed to verbalizing those two special queries the Rails way.
Here is the (abstract) model:
# account system
class Account < ActiveRecord::Base
has_many :users
has_many :tickets, :through => :users
has_many :managing_ticket_sections, ... # TicketSection-collection this account (any of its users) is operate for
has_many :managing_tickets, ... # Ticket-collection this account (any of its users) may operate on
end
class User < ActiveRecord::Base
belongs_to :account
has_many :tickets
has_many :managing_ticket_sections, ... # TicketSection-collection this user is operate for
has_many :managing_tickets, ... # Ticket-collection this user may operate on
end
# ticket system
class Ticket < ActiveRecord::Base
belongs_to :author, :class_name => "User"
belongs_to :assignee, :class_name => "User"
belongs_to :section, :class_name => "TicketSection"
end
class TicketSection < ActiveRecord::Base
has_many :tickets
has_many :operators
end
class TicketSectionManager < ActiveRecord::Base
belongs_to :manager, :class_name => "User"
belongs_to :section
end
I am aware of basic has_many :through-constructs, however, here, I am accessing more than three tables to get the tickets.
Something that actually works for in the User's model is:
class User < ActiveRecord::Base
has_many :managing_relations, :class_name => "TicketSectionManager" # XXX some kind of helper, for the two below
has_many :managing_sections, :class_name => "TicketSection", :through => :managing_relations, :source => :section
has_many :managing_tickets, :class_name => "Ticket", :through => :managing_relations, :source => :section
end
Here I am using a helper relation (managing_relations), which is absolutely never used except by the two has_many relations below.
I were not able to describe a User.managing_sections nor User.managing_tickets relation without this helper, which is, where I need an advice for.
Secondly, the customer is to have a look at all of the tickets he can manage on any User (think of an identity) he has logged in, so what I need, is a way to collect all tickets (/sections) this Account is permitted to manage (identified by being a member of the corresponding TicketSection)
Here I even were not able to express this relation the ruby way, and I had to work around it by the following:
class Account
def managing_tickets
Ticket.find_by_sql("SELECT t.* FROM tickets AS t
INNER JOIN ticket_section_managers AS m ON m.section_id = t.section_id
INNER JOIN users AS u ON u.id = m.user_id
WHERE u.account_id = #{id}")
end
end
I'd appreciate any kind of advice, and
many thanks in advance,
Christian Parpart.
I'm trying to represent a relationship between users in my application where a user can have many followers and can follow other users.
I would like to have something like user.followers() and user.followed_by()
Could you please tell me in details how to represent this using ActiveRecord?
Thanks.
You need two models, a Person and a Followings
rails generate model Person name:string
rails generate model Followings person_id:integer follower_id:integer blocked:boolean
and the following code in the models
class Person < ActiveRecord::Base
has_many :followers, :class_name => 'Followings', :foreign_key => 'person_id'
has_many :following, :class_name => 'Followings', :foreign_key => 'follower_id'
end
and corresponding in the Followings class you write
class Followings < ActiveRecord::Base
belongs_to :person
belongs_to :follower, :class_name => 'Person'
end
You could make the names clearer to your liking (i especially don't like the Followings-name), but this should get you started.
The twitter follower model differs from the friendship model in that you do not need permission from the person to follow them. Here I set up a lazy loading where the relationship is not fully defined in the person object.
/app/models/person.rb
def followers
Followership.where(:leader_id=>self.id).not_blocked
end
def following
Followership.where(:follower_id=>:self.id).not_blocked
end
has_many :followers, :class_name=>'Followership'
has_many :followed_by, :class_name=>'Followership'
/app/models/followership.rb
belongs_to :leader, :class_name=>'Person'
belongs_to :follower, :class_name=>'Person'
#leader_id integer
#follower_id integer
#blocked boolean
scope :not_blocked, :conditions => ['blocked = ?', false]