Subject: | Deep recursive create problem |
Hello.
What I'm trying to do is to create several related entities with a single "create" call:
$schema->resultset('Account')->create({
sum => 42,
user => {
superuser => 0,
name => 'vadim',
city => {name => 'Moscow'},
},
})
For some reason DBIx::Class tries to find "user" and "city" before creating new ones. I consider this a feature (though I would prefer to also have a pure deep recursive "create"), but there is a still a problem with it.
Do determine whether such user already exists, DBIx::Class searches for {superuser=>0, name=>'vadim'} ignoring the city completely. This is where it happens:
DBIx/Class/Row.pm:134
my $rel_rs = $rsrc->related_source($rel_name)->resultset;
my $new_rel_obj = $rel_rs->new_result($values);
my $proc_data = { $new_rel_obj->get_columns };
We lose {city => ...} here. As a result we can find some another user with some another city or even several such users (which resuls in a warning, because "find" should always find a single row).
So my questions are:
1. Do we really need this "find or create" behavior?
2. How do I turn it off?
3. Is it even possible to find a row according to the data what is going to be inserted? I maybe want to insert user with {name => "..."} and other fields being filled by the database defaults (and that doesn't mean at all any user with name="..." is ok for me).
4. Is this thing I discovered a bug? How do I fix it?
The full working example is here:
use strict;
use warnings;
{
package Local::Schema::City;
use base qw(DBIx::Class);
__PACKAGE__->load_components('Core');
__PACKAGE__->table('city');
__PACKAGE__->add_columns(
id => {
data_type => 'integer',
is_auto_increment => 1,
},
name => {
data_type => 'varchar',
size => '100',
},
comment => {
data_type => 'varchar',
size => '100',
default_value => 'DEFAULT COMMENT',
is_nullable => 1,
},
);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(
users => 'Local::Schema::User',
'city_id'
);
}
{
package Local::Schema::User;
use base qw(DBIx::Class);
__PACKAGE__->load_components('Core');
__PACKAGE__->table('user');
__PACKAGE__->add_columns(
id => {
data_type => 'integer',
is_auto_increment => 1,
},
name => {
data_type => 'varchar',
size => '100',
},
comment => {
data_type => 'varchar',
size => '100',
default_value => 'DEFAULT COMMENT',
is_nullable => 1,
},
superuser => {
data_type => 'bool',
},
city_id => {
data_type => 'integer',
},
);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->has_many(
accounts => 'Local::Schema::Account',
'user_id'
);
__PACKAGE__->belongs_to(
city => 'Local::Schema::City',
'city_id'
);
}
{
package Local::Schema::Account;
use base qw(DBIx::Class);
__PACKAGE__->load_components('Core');
__PACKAGE__->table('account');
__PACKAGE__->add_columns(
id => {
data_type => 'integer',
is_auto_increment => 1,
},
sum => {
data_type => 'integer',
},
user_id => {
data_type => 'integer',
},
);
__PACKAGE__->set_primary_key('id');
__PACKAGE__->belongs_to(
user => 'Local::Schema::User',
'user_id'
);
}
{
package Local::Schema;
use base qw(DBIx::Class::Schema);
__PACKAGE__->load_classes('City', 'User', 'Account');
}
my $schema = Local::Schema->connect(
'dbi:SQLite:dbname=local.sqlite', '', ''
);
$schema->storage->debug(0);
$schema->deploy();
=uncomment to see warnings
my $city = $schema->resultset('City')->create({name => 'C'});
$schema->resultset('User')->create({superuser => 0, name => 'vadim', city => $city});
$schema->resultset('User')->create({superuser => 0, name => 'vadim', city => $city});
$schema->resultset('User')->create({superuser => 0, name => 'vadim', city => $city});
=cut
print $schema->resultset('Account')->create({
sum => 42,
user => {
superuser => 0,
name => 'vadim',
city => {name => 'Moscow'},
},
})->user->id;
print $schema->resultset('Account')->create({
sum => 42,
user => {
superuser => 0,
name => 'vadim',
city => {name => 'London'},
},
})->user->id; # same ID :(
END {
unlink('local.sqlite');
}