POST to create through controller with different FK constraint than 'id' fails
Result versus expectation
Following the docs we created a dashboard for the below model, which is the "N" in a 1:N relationship between an OwningModel (1) and an OwnedModel (N). The catch is that the OwnedModel is keyed (FK) on a unique ID that is not owning_model_id
, it's constrained on special_owner_id
.
So, as below, we have
owning_model: Field::BelongsTo.with_options(
class_name: "SomeModule::OwningModel",
foreign_key: 'special_owner_id'
),
When we submit a new "create" through the below dashboard, we get an FK error:
ActiveRecord::InvalidForeignKey (PG::ForeignKeyViolation: ERROR: insert or update on table "owned_model" violates foreign key constraint "fk_rails_e9b0191624"
2022-03-28T18:05:01.013857+00:00 app[web.1]: DETAIL: Key (special_owner_id)=(20) is not present in table "owning_models".
2022-03-28T18:05:01.013858+00:00 app[web.1]: : INSERT INTO "owned_models" ("special_owner_id", "attr_1", "created_at", "updated_at") VALUES ('20', 2500, '2022-03-28 18:05:00.837807', '2022-03-28 18:05:00.837807') RETURNING "id"):
The '20' in that log corresponds to owning_model.id
, which is actually 20, but the default controller method for create
in the administrate controller is POSTing as above with (special_owner_id)=(20)
. We expected it to POST with (special_owner_id)=(SOME_SPECIAL_ID)
, and succeed.
Workaround
One workaround is to modify the OwnedModel's dashboard as follows:
owning_model: Field::BelongsTo.with_options(
class_name: "SomeModule::OwningModel",
foreign_key: 'special_owner_id',
primary_key: 'special_owner_id',
),
However, this is semantically incorrect (that isn't the primary key), and will probably confuse developers later.
Dashboard model
require "administrate/base_dashboard"
class OwnedModelDashboard < Administrate::BaseDashboard
NAMESPACE = 'internal'
# ATTRIBUTE_TYPES
# a hash that describes the type of each of the model's fields.
#
# Each different type represents an Administrate::Field object,
# which determines how the attribute is displayed
# on pages throughout the dashboard.
ATTRIBUTE_TYPES = {
owning_model: Field::BelongsTo.with_options(
class_name: "SomeModule::OwningModel",
foreign_key: 'special_owner_id'
),
id: Field::Number,
attr_1: Field::Boolean,
attr_2: Field::Number,
created_at: Field::DateTime,
updated_at: Field::DateTime,
}.freeze
# COLLECTION_ATTRIBUTES
# an array of attributes that will be displayed on the model's index page.
COLLECTION_ATTRIBUTES = [
:owning_model,
:attr_1,
:attr_2,
].freeze
# SHOW_PAGE_ATTRIBUTES
# an array of attributes that will be displayed on the model's show page.
SHOW_PAGE_ATTRIBUTES = [
:owning_model,
:id,
:attr_1,
:attr_2,
:created_at,
:updated_at,
].freeze
# FORM_ATTRIBUTES
# an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages.
FORM_ATTRIBUTES = [
:owning_model,
:attr_1,
:attr_2,
].freeze
Environment
- Rails 5
- administrate 0.17