我正在创建一个代表牲畜血统的应用程序。每个孩子都有一个水坝(例如母羊)和一个公牛(例如公羊)。大坝/父系配对可以有多个孩子(例如羔羊),大坝和母亲可以有更多的孩子独立于另一个。我试图表达这种关系,以便我可以做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_relationship
和dam_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
,我会收到类似的错误。
我正在寻找一双额外的眼睛来告诉我自己做错了什么,或者是否有更轻松的方法来实现我所追求的目标。
答案 0 :(得分:3)
问题是你的动物表中只有一个Class Test2{
}
$Object3 = new Test2();
$Object4 = new $Object3();// this will also create new object from the same class
,它只能存储单个父母的ID。这适用于细菌,但不适用于有两个父母的动物。当你设置大坝和父亲时,你的parent_id
就会被写入。
有多种方法可以做到这一点,但我认为最简单的方法是在动物表中设置parent_id
和dam_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
相反,你必须手动设置父亲和母亲(这可能是你想要做的事情,因为你正在跟踪)。