正确建模谱系

时间:2018-01-15 15:47:12

标签: ruby-on-rails

我正在创建一个代表牲畜血统的应用程序。每个孩子都有一个水坝(例如母羊)和一个公牛(例如公羊)。大坝/父系配对可以有多个孩子(例如羔羊),大坝和母亲可以有更多的孩子独立于另一个。我试图表达这种关系,以便我可以做ewe.children之类的事情,并获得她的后代列表。同样,我希望能够做lamb.ewe这样的事情来让她的母亲或lamb.ewe.ewe去找她的外祖母。

来自schema.rb ...

create_table "parent_child_relationships", force: :cascade do |t|
  t.integer "parent_id"
  t.integer "child_id"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.index ["child_id"], name: "index_parent_child_relationships_on_child_id"
  t.index ["parent_id", "child_id"], name: "index_parent_child_relationships_on_parent_id_and_child_id", unique: true
  t.index ["parent_id"], name: "index_parent_child_relationships_on_parent_id"
end

来自parent_child_relationship.rb ...

class ParentChildRelationship < ApplicationRecord
  belongs_to :sire, class_name: "Animal"
  belongs_to :dam, class_name: "Animal"
  belongs_to :children, class_name: "Animal"
end

来自animal.rb ...

has_one :sire_relationship, class_name:  "ParentChildRelationship",
                        foreign_key: "child_id",
                    dependent:   :destroy
has_one :dam_relationship, class_name:  "ParentChildRelationship",
                        foreign_key: "child_id",
                    dependent:   :destroy
has_many :child_relationships, class_name:  "ParentChildRelationship",
                        foreign_key: "parent_id",
                    dependent:   :destroy

has_one :sire, through: :sire_relationship, source: :child
has_one :dam, through: :dam_relationship, source: :child
has_many :children, through: :child_relationships, source: :parent

在控制台中,我运行以下命令来抓住我想要彼此联​​系的动物......

s = Shepherd.first
ewe = s.animals.find_by(id: 37)
ram = s.animals.find_by(id: 133)
lamb = s.animals.find_by(id: 61)

现在,当我尝试创建sire_relationshipdam_relationship时,我收到了错误,因为它似乎没有看到这种关系是唯一的。 sire_relationship替换为dam_relationship ...

>> lamb.create_sire_relationship(parent_id: ram.id)
   (0.1ms)  begin transaction
  SQL (0.7ms)  INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["parent_id", 133], ["child_id", 61], ["created_at", "2018-01-15 15:33:06.649936"], ["updated_at", "2018-01-15 15:33:06.649936"]]
   (2.5ms)  commit transaction
  ParentChildRelationship Load (0.2ms)  SELECT  "parent_child_relationships".* FROM "parent_child_relationships" WHERE "parent_child_relationships"."child_id" = ? LIMIT ?  [["child_id", 61], ["LIMIT", 1]]
=> #<ParentChildRelationship id: 1, parent_id: 133, child_id: 61, created_at: "2018-01-15 15:33:06", updated_at: "2018-01-15 15:33:06">
>> lamb.create_dam_relationship(parent_id: ewe.id)
   (0.1ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["parent_id", 37], ["child_id", 61], ["created_at", "2018-01-15 15:33:35.045703"], ["updated_at", "2018-01-15 15:33:35.045703"]]
   (1.0ms)  commit transaction
  ParentChildRelationship Load (0.1ms)  SELECT  "parent_child_relationships".* FROM "parent_child_relationships" WHERE "parent_child_relationships"."child_id" = ? LIMIT ?  [["child_id", 61], ["LIMIT", 1]]
   (0.0ms)  begin transaction
  SQL (0.5ms)  DELETE FROM "parent_child_relationships" WHERE "parent_child_relationships"."id" = ?  [["id", 1]]
   (1.2ms)  commit transaction
=> #<ParentChildRelationship id: 2, parent_id: 37, child_id: 61, created_at: "2018-01-15 15:33:35", updated_at: "2018-01-15 15:33:35">

创建children_relationships,我收到这些错误......

>> ewe.child_relationships.create(child_id: lamb.id)
   (0.1ms)  begin transaction
  SQL (0.9ms)  INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["parent_id", 37], ["child_id", 61], ["created_at", "2018-01-15 15:37:11.436086"], ["updated_at", "2018-01-15 15:37:11.436086"]]
   (0.1ms)  rollback transaction
ActiveRecord::RecordNotUnique: SQLite3::ConstraintException: UNIQUE constraint failed: parent_child_relationships.parent_id, parent_child_relationships.child_id: INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)
    from (irb):13
>> ram.child_relationships.create(child_id: lamb.id)
   (0.1ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["parent_id", 133], ["child_id", 61], ["created_at", "2018-01-15 15:37:25.264947"], ["updated_at", "2018-01-15 15:37:25.264947"]]
   (2.5ms)  commit transaction

最后,如果我查看是否可以访问sire的{​​{1}},我会收到另一个错误...

lamb

如果我>> lamb.dam ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :child in model ParentChildRelationship. Try 'has_many :dam, :through => :dam_relationship, :source => <name>'. Is it one of sire, dam, or children? from (irb):21 >> lamb.sire ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :child in model ParentChildRelationship. Try 'has_many :sire, :through => :sire_relationship, :source => <name>'. Is it one of sire, dam, or children? from (irb):22 ewe.children,我会收到类似的错误。

我正在寻找一双额外的眼睛来告诉我自己做错了什么,或者是否有更轻松的方法来实现我所追求的目标。

1 个答案:

答案 0 :(得分:3)

问题是你的动物表中只有一个Class Test2{ } $Object3 = new Test2(); $Object4 = new $Object3();// this will also create new object from the same class ,它只能存储单个父母的ID。这适用于细菌,但不适用于有两个父母的动物。当你设置大坝和父亲时,你的parent_id就会被写入。

有多种方法可以做到这一点,但我认为最简单的方法是在动物表中设置parent_iddam_id

这是创建表的迁移:

sire_id

这就是你的模型看起来像这样。请注意,您需要两个class CreateAnimals < ActiveRecord::Migration[5.1] def change create_table :animals do |t| t.integer :dam_id, index: true t.integer :sire_id, index: true t.timestamps end end end / belongs_to关系:

has_many

请注意同时抓取class Animal < ApplicationRecord belongs_to :dam, class_name: 'Animal' belongs_to :sire, class_name: 'Animal' has_many :children_as_sire, class_name: 'Animal', foreign_key: :sire_id has_many :children_as_dam, class_name: 'Animal', foreign_key: :dam_id def children children_as_dam + children_as_sire end end children的getter方法children_as_dam。这将导致两个不理想的SQL查询。如果您正在跟踪动物的秒,您可以执行以下操作:

children_as_sire

我写了一些规范来证明:

def children?
  case sex
  when 'male'
    children_as_sire
  when 'female'
    children_as_dam
  end
end

请注意,您无法将儿童添加到模型中:

require 'rails_helper'

RSpec.describe Animal, type: :model, focus: true do
  it 'can be created' do
    expect { Animal.create }.to_not raise_error
  end

  it 'can have a dam' do
    animal = Animal.new
    animal.update! dam: Animal.create
    expect(animal.dam).to be_a(Animal)
    expect(animal.sire).to be_nil
  end

  it 'can have a sire' do
    animal = Animal.new
    animal.update! sire: Animal.create
    expect(animal.sire).to be_a(Animal)
    expect(animal.dam).to be_nil
  end

  it 'can have both a dam and a sire and tell the difference' do
    dam = Animal.create
    sire = Animal.create
    child = Animal.create dam: dam, sire: sire

    expect(child.reload.dam).to eq(dam)
    expect(child.reload.sire).to eq(sire)
  end

  it 'grandma' do
    grandma = Animal.create
    dam = Animal.create dam: grandma
    child = Animal.create dam: dam

    expect(child.reload.dam.dam).to eq(grandma)
  end

  it 'has children' do
    sire = Animal.create
    animal = Animal.create sire: sire
    expect(sire.reload.children).to include(animal)
  end
end

相反,你必须手动设置父亲和母亲(这可能是你想要做的事情,因为你正在跟踪)。