2015-04-19 37 views
0

我試圖建立一個數據庫,其相關位如下所示。我在Arch Linux上使用SQLite3(3.8.8.3-1),並使用DBIx :: Class 0.082820。DBIx :: Class has_one <-> might_have relationship

它是一個簡單的簿記系統的一部分。發票行has_one交易,但僅交易might_have對應的發票行(因爲可以創建一些沒有發票的交易)。

我無法獲取DBIx :: Class以便一次插入發票行及其相應的事務。錯誤信息也在下面。

我做錯了嗎?或者做一些沒有道理的事情?

爲什麼它從搜索具有相同描述的現有事務開始?

這裏是我的簡化得多的測試用例的血淋淋的細節:

InvoiceLine.pm:

package Test::DB::Schema::Result::InvoiceLine; 

use strict; 
use warnings; 

use base 'DBIx::Class::Core'; 
__PACKAGE__->table("invoice_lines"); 
__PACKAGE__->add_columns(

    "id", 
    { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, 

    "txn_id", 
    { data_type => "integer", is_foreign_key => 1, is_nullable => 0, 
    is_deferrable => 1 }, # tried this, but it doesn't help 

    'details', 
    { data_type => 'text', is_nullable => 0 }, 

); 
__PACKAGE__->set_primary_key("id"); 

# Invoice line has an associated transaction 
__PACKAGE__->has_one(
    "txn", 
    "Test::DB::Schema::Result::Transaction", 
    'id', 
); 

# Experimental -- this doesn't work either 
#__PACKAGE__->belongs_to(
# "txn", 
# "Test::DB::Schema::Result::Transaction", 
# "txn_id", 
#); 

1; 

Transaction.pm:

use utf8; 
package Test::DB::Schema::Result::Transaction; 

use strict; 
use warnings; 

use base 'DBIx::Class::Core'; 
__PACKAGE__->table("transactions"); 
__PACKAGE__->add_columns(
    "id", 
    { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, 

    'description', 
    { data_type => 'text', is_nullable => 0 }, 

    # Invoice line 
    # Null if no associated invoice 
    'invoice_line_id', 
    {data_type => 'integer', is_nullable => 1 }, 
); 
__PACKAGE__->set_primary_key("id"); 

# Some transactions have a single corresponding 
# invoice line 
__PACKAGE__->might_have(
    "invoice_line", 
    "Test::DB::Schema::Result::InvoiceLine", 
    'id', 
    { cascade_copy => 0, cascade_delete => 0 }, 
); 
# EXPERIMENTAL == this doesn't work either 
# might_have isn't working, so try has_many (where many can be 0): 
#__PACKAGE__->has_many(
# 'invoice_lines', 
# "Test::DB::Schema::Result::InvoiceLine", 
# 'txn_id', 
#); 

1; 

Test.pl

#!/usr/bin/perl 
# Test.pl 
# Testing might_have <-> has_one relationship 
use Test::DB::Schema; 
my $schema = Test::DB::Schema->connect(
    "dbi:SQLite:dbname=dbic_test.db", '', '', {} 
); 
$schema->deploy({ add_drop_table => 1 } , '.'); 
$schema->storage->debug(1); 
my $data1 = { 
    details => 'abc', 
    txn => { 
     description => 'xyz', 
    } 
}; 
my $new1 = $schema->resultset('InvoiceLine')->create($data1); 

結果的運行Test.pl是:

BEGIN WORK 
SELECT me.id, me.description, me.invoice_line_id FROM transactions me WHERE (me.description = ?): 'xyz' 
INSERT INTO transactions (description) VALUES (?): 'xyz' 
INSERT INTO invoice_lines (details, id) VALUES (?, ?): 'abc', '1' 
DBIx::Class::Storage::DBI::_dbh_execute(): DBI Exception: DBD::SQLite::st execute failed: NOT NULL constraint failed: invoice_lines.txn_id [for Statement "INSERT INTO invoice_lines (details, id) VALUES (?, ?)"] at ./Test.pl line 16 
DBIx::Class::Storage::TxnScopeGuard::DESTROY(): A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or error. Rolling back. at /usr/share/perl5/site_perl/DBIx/Class/Exception.pm line 77 
ROLLBACK 

回答

1

錯誤的關係定義。使用此:

# InvoiceLine.pm 
__PACKAGE__->might_have(
    "txn", 
    "Test::DB::Schema::Result::Transaction", 
    "invoice_line_id", 
); 

# Transaction.pm 
__PACKAGE__->belongs_to(
    "invoice_line", 
    "Test::DB::Schema::Result::InvoiceLine", 
    "invoice_line_id", 
); 
+0

感謝您的回答 - 我會嘗試明天。 –

+0

我試過了,它的工作原理!幾乎。請參閱下面的答案。 –

0

感謝Denis Ibaev的回答,我已經修改了問題,並找到了一個很好的解決方案。

其實,我需要發票行有HAS_ONE關係,而不是might_have,但這只是一個細微的變化。

might_have改變關係的交易的身邊belongs_to的是最重要的一點。

我還必須在發票行中手動輸入txn_id。

這是我的新代碼:

InvoiceLine.pm:

package Test::DB::Schema::Result::InvoiceLine; 
use strict; 
use warnings; 
use base 'DBIx::Class::Core'; 
__PACKAGE__->table("invoice_lines"); 
__PACKAGE__->add_columns(
    "id", 
    { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, 
    "txn_id", 
    { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, # because , is_deferrable => 1 }, doesn't work with SQLite 
    'details', 
    { data_type => 'text', is_nullable => 0 }, 
); 
__PACKAGE__->set_primary_key("id"); 
# Invoice line has an associated transaction 
__PACKAGE__->has_one(
    "txn", 
    "Test::DB::Schema::Result::Transaction", 
    'invoice_line_id', 
); 
1; 

Transaction.pm:

package Test::DB::Schema::Result::Transaction; 
use strict; 
use warnings; 
use base 'DBIx::Class::Core'; 
__PACKAGE__->table("transactions"); 
__PACKAGE__->add_columns(
    "id", 
    { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, 
    'description', 
    { data_type => 'text', is_nullable => 0 }, 
    # Invoice line 
    # Null if no associated invoice 
    'invoice_line_id', 
    {data_type => 'integer', is_nullable => 1 }, 
); 
__PACKAGE__->set_primary_key("id"); 
__PACKAGE__->belongs_to(
    "invoice_line", 
    "Test::DB::Schema::Result::InvoiceLine", 
    'invoice_line_id', # our_fk_column 
); 
1; 

Test.pl:

my $schema = Test::DB::Schema->connect(
    "dbi:SQLite:dbname=dbic_test.db", '', '', {} 
); 
$schema->deploy({ add_drop_table => 1 } , '.'); 
$schema->storage->debug(1); 
my $data1 = { 
    details => 'abc', 
    txn => { 
     description => 'xyz', 
    } 
}; 
$schema->txn_do(sub { 
    my $new1 = $schema->resultset('InvoiceLine')->create($data1); 
    # add the reverse link 
    $new1->txn_id($new1->txn->id); 
    $new1->update; 
}); # end of txn_do 
# Add another one with the same data to make sure 
# they end up as separate rows 
$schema->txn_do(sub { 
    my $new2 = $schema->resultset('InvoiceLine')->create($data1); 
    $new2->txn_id($new2->txn->id); 
    $new2->update; 
}); # end of txn_do 

運行測試。PL生成並運行該SQL:

BEGIN WORK 
INSERT INTO invoice_lines (details) VALUES (?): 'abc' 
INSERT INTO transactions (description, invoice_line_id) VALUES (?, ?): 'xyz', '1' 
UPDATE invoice_lines SET txn_id = ? WHERE (id = ?): '1', '1' 
COMMIT 
BEGIN WORK 
INSERT INTO invoice_lines (details) VALUES (?): 'abc' 
INSERT INTO transactions (description, invoice_line_id) VALUES (?, ?): 'xyz', '2' 
UPDATE invoice_lines SET txn_id = ? WHERE (id = ?): '2', '2' 
COMMIT 

而且現在的表包含正確的價值觀:

Invoice Lines  
1|1|abc 
2|2|abc 

Transactions 
1|xyz|1 
2|xyz|2 
+0

總結一下這個教給我的:has_many,has_one和might_have關係(幾乎)總是適用於父 - >子方向; belongs_to適用於其他方向。 –

相關問題