0

Help me out with a db migration and model in Rails, I tried searching for self referencing and what not, but I can't make heads or tails.. I'm stuck, so to say..

Basically I want two models, User model and Rule model.

In User model I want to specify an owner of user, which is another user, there can only be one owner, user can be owner of itself.

And a Rule model, in which I also want to specify an owner of a rule (User) (User can be an owner of many rules) and user to which this rule applies (User) (User can have many rules).

So I need two migrations and two models, I'll start and hopefully you'll be able to make out of what I'm trying to do ..

class User < ActiveRecord::Base
  belongs_to :user #?
  has_one :user    # as in owner
  has_many :rules  # rules for given user and rules that are created by this user
  ... #and some more similar entries
end

and

class Rule < ActiveRecord::Base
  belongs_to :user # as in owner of rule and rule for user
end

and I'm totally not sure what to write in migrations..

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :username

      #owner?

      #stuff left out
      t.timestamps
    end
  end
end

and

class CreateRules < ActiveRecord::Migration
  def change
    create_table :rules do |t|
      t.string :title

      #rule for?
      #owner?

      #stuff left out
      t.timestamps
    end
  end
end

How do I implement this?

Artis
  • 47
  • 1
  • 1
  • 8

3 Answers3

0

What you need is Self referential association

Read http://edgeguides.rubyonrails.org/association_basics.html#self-joins and go through this episode on RailsCasts. This demonstrates precisely what you are looking for.

Hope it helps

Good luck. :)

kiddorails
  • 12,961
  • 2
  • 32
  • 41
0
#Models/user.rb
class User < ActiveRecord::Base
  belongs_to :owner, :class_name => 'User', :foreign_key => 'owner_id'
  has_many :subordinates, :class_name => 'User'
  has_many :rules, :through => "owner_rules"
end

#Models/OwnerRule.rb
class OwnerRule < ActiveRecord::Base
    belongs_to :users
    belongs_to :rules
end

#Models/Rule.rb
class Rule < ActiveRecord::Base
    has_many :users, :through => "owner_rules"
end

This rubyonrails guide has a good guideline on this.

So the point is, a user object will have two methods owner and subordinates. If you call user.owner then it will return the user with id equals user.owner_id.
If you call user.subordinates then it will return all users having the user.id in their owner_id column.

Let me know if made it a bit convoluted or not :P

Now, in case of migration scripts you will just create the columns as you named.

UPDATE:

Well if we just reorganize your requirements

  • A user can have many Rules.
  • Many rules can be applied to one user
  • One user can have a Owner who is also a User
  • One owner will have many subordinates who are User too.

Then ive updated my codes above. Please take a look if it helps now.

Samiron
  • 5,169
  • 2
  • 28
  • 55
  • and what about Rule model? do I write it like this? `belongs_to :for, :class_name -> 'User', :foreign_key => 'user_id'` and `belongs_to :user` It references user two times.. as owner and as for (rule for) also do I need to manually (in migration) create these fields? – Artis Oct 02 '12 at 10:24
  • "owner of rule" is a property of the rule (so a `belongs_to`) but a rule can apply to many users, and a user can have many rules applied, so it's a completely different thing. – rewritten Oct 02 '12 at 10:26
  • If you write `belongs_to :user`, it will automatically derive the class name as **User** and `foreign_key` as 'user_id'. But in case of `belongs_to :owner` we have to specify the class name because the derived class name would be "Owner". – Samiron Oct 02 '12 at 10:28
0

You need three tables, because the "rule applied to user" is a many-to-many so it needs a join table.

You also need to rename either the "rule created by user" or the "rule applied to user" because otherwise you'll have name conflicts.

Tables:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      #stuff left out

      t.references :owner

      #stuff left out
    end
  end
end

class CreateRules < ActiveRecord::Migration
  def change
    create_table :rules do |t|
      #stuff left out

      t.references :owner

      #stuff left out
    end
  end
end

class CreateRulesUsers < ActiveRecord::Migration
  def change
    create_table :rules_users do |t|
      t.references :user
      t.references :rule
    end
  end
end

Models

  • User

    has_many :owned_users, :class_name => "User", :foreign_key => "owner_id", :inverse_of => :owner
    belongs_to :owner, :class_name => "User", :foreign_key => "owner_id", :inverse_of => :owned_users
    has_and_belongs_to_many :rules # uses the join table
    has_many :owned_rules, :class_name => "Rule", :foreign_key => "owner_id", :inverse_of => :owner
    
  • Rule

    belongs_to :owner, :class_name => "User", :foreign_key => "owner_id", :inverse_of => :owned_rules
    has_and_belongs_to_many :users # uses the join table, it's the users to which the rule applies
    
rewritten
  • 16,280
  • 2
  • 47
  • 50