用户模型中的父子关系(自联接)

时间:2015-07-24 15:50:28

标签: ruby-on-rails

在rails中,我有这个用户模型:

class User < ActiveRecord::Base
  enum role: [:adult, :child, :admin]
  after_initialize :set_default_role, :if => :new_record?

  # belongs_to :spouse, :foreign_key => :spouse_id, :class_name => 'User', :inverse_of => :spouse 

  def set_default_role
    self.role ||= :adult
  end

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable


  def marry(user)
    self.spouse = user
    user.spouse = self
  end
end

我通过此迁移添加了配偶:

class AddFieldsToUser < ActiveRecord::Migration
  def change
    # for marriages
    add_column :users, :spouse_id, :integer, index: true
  end
end

并且工作正常(虽然我的函数的反函数从未起作用# belongs_to :spouse, :foreign_key => :spouse_id, :class_name => 'User', :inverse_of => :spouse )。我现在正试图用“孩子 - 父母”的关系做另一个自我加入。父母可以有很多孩子,孩子可以有(很多/两个)父母。这是我提出的迁移:

class CreateParentalRelationships < ActiveRecord::Migration
  def change
    create_table :parental_relationships do |t|
      t.references :parent, index: true
      t.references :child
    end
  end
end

我把它添加到模型中:

  has_many :children, :through => :parental_relationships, class_name: "User"
  has_many :parents, :through => :parental_relationships, class_name: "User"

但该关系不适用于以下错误:

[7] pry(main)> u = User.find(3)
  User Load (0.0ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 3]]
=> #<User id: 3, email: ...>

[8] pry(main)> u.children
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :parental_relationships in model Us
er

[9] pry(main)> u.parent
NoMethodError: undefined method `parent' for #<User:0x5e52eb0>
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/activemodel-4.1.8/lib/active_model/attribute_methods.rb:435:in
 `method_missing'

[10] pry(main)> u.parents
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :parental_relationships in model Us
er
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/reflection.rb:690:in `che
ck_validity!'

[11] pry(main)> u2.children
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :parental_relationships in model Us
er
from C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/reflection.rb:690:in `che
ck_validity!'

我错过了什么?

2 个答案:

答案 0 :(得分:4)

设置父子关系需要进行一些调查。

class ParentalRelationship < ActiveRecord::Base
  belongs_to :parent, class_name: 'User'
  belongs_to :child, class_name: 'User'
end
class User < ActiveRecord::Base
  has_many :parent_relationships,
          foreign_key: 'child_id',
          class_name: 'ParentalRelationship'

  has_many :child_relationships,
          foreign_key: 'parent_id',
          class_name: 'ParentalRelationship'

  has_many :parents,
          through: :parent_relationships,
          class_name: 'User'

  has_many :children,
          through: :child_relationships,
          class_name: 'User'

  has_and_belongs_to_many :marriages
  belongs_to :current_marriage, class_name: 'Marriage'

  def marry(spouse)
    marriage = Marriage.create(users: [self, spouse])
    marriage.users.each { |u| u.update(current_marriage: marriage ) }
    marriage
  end

  def spouse
    return nil unless current_marriage
    current_marriage
      .users.where.not('marriages_users.user_id' => id).first
  end

  def birth(child)
    child.parents << self
    child.parents << spouse if spouse
  end
end

请注意,我们需要设置两次与ParentalRelationship的关系,因为我们需要告诉rails它应该为每种类型的关系(用户是父级或子级)查看哪个外键。

因为在这些近代人们实际上可以多次结婚,我们需要一个婚姻模型和一个用户联盟的联接表。

class Marriage < ActiveRecord::Base
  has_and_belongs_to_many :users
  validates :users, length: { minimum: 2, maximum: 2 }
end
rails g migration CreateUsersMarriagesJoinTable users marriages

带有规格的示例应用:https://github.com/maxcal/sandbox/tree/31614819

答案 1 :(得分:1)

我想出了一个解决方案。不确定它是否是最好的:

我的新用户模型:

class User < ActiveRecord::Base
  enum role: [:adult, :child, :admin]
  after_initialize :set_default_role, :if => :new_record?

  belongs_to :spouse, :foreign_key => :spouse_id, :class_name => 'User', :inverse_of => :spouse
  has_many :parental_relationships
  has_many :children, :through => :parental_relationships, class_name: 'User'
  has_many :parents, :through => :parental_relationships, class_name: 'User'

  def set_default_role
    self.role ||= :adult
  end

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable


  def marry(user)
    self.spouse = user
    user.spouse = self
  end

  def birth(user)
    self.children << user
    user.parents << self
    if self.spouse
      self.spouse.children << user
      user.parents << self.spouse
    end

  end

end

我必须编辑一些迁移和模型。

ParentalRelationships迁移

class CreateParentalRelationships < ActiveRecord::Migration
  def change
    create_table :parental_relationships do |t|

      t.references :user, index: true
      t.references :child 
      t.references :parent

    end
  end
end

ParentalRelationship模型:

class ParentalRelationship < ActiveRecord::Base
  belongs_to :user
  # , :class_name => "User"
  # belongs_to :parent, :class_name => "User"
  belongs_to :child, :class_name => "User"
  belongs_to :parent, :class_name => "User"
end

所以,添加关系:

u = User.find(50)
u.birth(User.find(60))