문제

I am trying to determine if I am approaching the following issue the correct way with SQL and DBIx::Class as I am very new to both.

I have a schema with 3 tables, Device,object and Network

A device can have 0..1 to many objects An object can have 1 network but a network can belong to 0...Many devices.

I have tried to capture this relationship in the schema shown in the picture.

enter image description here

Here is the relavent Schema.

Device

__PACKAGE__->table("Device");

__PACKAGE__->add_columns(
  "devicename",
  { data_type => "varchar", is_nullable => 0, size => 50 },
  "devicetype",
  { data_type => "varchar", is_nullable => 0, size => 20 },
  "deviceid",
  {  data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
);

__PACKAGE__->set_primary_key("deviceid");
__PACKAGE__->has_many(
  "objects",
  "TestApp::Schema::Result::Object",
  { "foreign.device_deviceid" => "self.deviceid" },
  { cascade_copy => 0, cascade_delete => 0 },

Object

__PACKAGE__->table("Object");

__PACKAGE__->add_columns(
  "objectid",
  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
  "objectname",
  { data_type => "varchar", is_nullable => 0, size => 100 },
  "objecttype",
  { data_type => "varchar", is_nullable => 0, size => 20 },
  "device_deviceid",
  {  data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
  "network_networkid",
  {    data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
);

__PACKAGE__->set_primary_key("objectid");

__PACKAGE__->belongs_to(
  "device_deviceid",
  "TestApp::Schema::Result::Device",
  { deviceid => "device_deviceid" },
  { is_deferrable => 1, on_delete => "NO ACTION", on_update => "NO ACTION" },
);

 __PACKAGE__->belongs_to(
  "network_networkid",
  "TestApp::Schema::Result::Network",
  { networkid => "network_networkid" },
  {
    is_deferrable => 1,
    join_type     => "LEFT",
    on_delete     => "NO ACTION",
    on_update     => "NO ACTION",
  },
);

Network

__PACKAGE__->table("network");

__PACKAGE__->add_columns(
   "networkid",
 { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
  "network",
  { data_type => "varchar", is_nullable => 1, size => 15 },
  "netmask",
  { data_type => "varchar", is_nullable => 1, size => 15 },
  "cidr",
   { data_type => "integer", is_nullable => 1 },
 );

__PACKAGE__->set_primary_key("networkid");

__PACKAGE__->has_many(
 "objects",
  "TestApp::Schema::Result::Object",
  { "foreign.network_networkid" => "self.networkid" },
  { cascade_copy => 0, cascade_delete => 0 },
);

For my test data I am inserting the data like so below. Is there a more efficient way?

#get result sets
my $network_rs = $schema->resultset('Network');
my $device_rs = $schema->resultset('Device');

#create a new device
my $new_device = $device_rs->create( { devicename => 'test_device', devicetype => 'test_dt'});

#create a new network if it doesn't exist.
my $new_network = $network_rs->find_or_create({ network => '1.1.1.1', netmask => '255.255.255.0', cidr => '24' });

#Add two objects and set the foreign key for the network table for the newly created network above.
$new_device->objects->create( { objectname => 'networkobj1',network_networkid => $new_network->networkid });
$new_device->objects->create( { objectname => 'networkobj2',network_networkid => $new_network->networkid });

To view the networks assigned to each network I just do the following;

my @deviceObjects = $new_device->objects();

foreach my $object (@deviceObjects) 
{ 
print $object->objectname . " contains the following network:\n";  
print $object->network_networkid->network . "\n";
print $object->network_networkid->netmask . "\n";
print $object->network_networkid->cidr . "\n";
} 
도움이 되었습니까?

해결책

Basically you're doing everything correct. Your object table is usually called a bridge or many-to-many helper table. To get directly from a device to its networks you can add a many_to_many helper method, see the DBIx::Class::Relationship docs. There are some modules for populating your database with (test) data, for example DBIx::Class::Fixtures.

The recommended naming for relationships (and what DBIx::Class::Schema::Loader generates from an existing database) is singular for relationships that return a single row (belongs_to, has_one and might_have) and plural for relationships that return a list of rows (has_many). Using device->objects->network reads much more natural, including the names of the columns storing the foreign key doesn't make much sense.

Beside those little things a great start! Join #dbix-class in irc.perl.org for realtime help.

다른 팁

Re your test data inserting, there is a much more efficient way (to write it), DBIC can do a whole bunch of inserts at once based on the relationships you told it about, we call it "multicreate", so using your example:

# get main device resultset:
my $device_rs = $schema->resultset('Device');

# create all the things:
$device_rs->create({ 
   # Device columns:
   devicename => 'test_device', 
   devicetype => 'test_dt',
   # Objects:
   objects => [
     {
        objectname => 'networkobj1',
        # network for this object:
        network_networkid => {
           network => '1.1.1.1', netmask => '255.255.255.0', cidr => '24',
        }
     },
     {
        objectname => 'networkobj2',
        # network for this object:
        network_networkid => {
           network => '1.1.1.1', netmask => '255.255.255.0', cidr => '24',
        }
     }
   ]
});

Note that specifying the network data shouldn't get you two inserted network rows, it will use find_or_new to create that row, so should find the first one created, for the second one.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top