Skip to content

Unique constraints do not work properly across a belongs_to relationship #20

Description

@dracos

If you have a DB model, something like:

__PACKAGE__->add_columns(
  "id", { data_type => "integer", is_auto_increment => 1, is_nullable => 0, sequence => "roles_id_seq" },
  "body_id", { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
  "name", { data_type => "text", is_nullable => 1 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->add_unique_constraint("roles_body_id_name_key", ["body_id", "name"]);
__PACKAGE__->belongs_to(
  "body", "App::DB::Result::Body", { id => "body_id" },
  { is_deferrable => 0, on_delete => "CASCADE,", on_update => "NO ACTION" },
);

And a matching form (which needs body to work, body_id does not):

has_field 'name' => ( required => 1 );
has_field 'body' => ( type => 'Select', empty_select => 'Select a body', required => 1 );

And your database has three rows:

  1. Body A, name "123"
  2. Body A, name "456"
  3. Body B, name "789"

Then the following happens:

  • Editing row 1 to Body A, name "456" – works fine, you get the "Duplicate value for [_1] unique constraint" error message.
  • Editing row 1 to Body B, name "789" – validation incorrectly passes, it tries to update and dies with a database error.
  • Creating a new entry, Body A, name "123" – validation incorrectly passes, it tries to insert and dies with a database error.

This is because the form column is body but the database field is body_id. Here is the bit of code responsible:

my @values = map {
exists( $value->{$_} ) ? $value->{$_} : undef ||
( $self->item ? $self->item->get_column($_) : undef )
} @columns;

$value will have a body entry, not a body_id entry, from the submitted form. So it always falls back to the item lookup, so on creation that's nothing and on editing the body is the original body not the new one. In both those cases it therefore doesn't find the $value->{body} entry and incorrectly passes validation.

It needs to know how to turn either body_id into body or vice-versa, at the accessor for those columns, not the actual column names, but I'm not sure how best to do that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions