Rails: Polymorphism, undefined method `primary_key' for String:Class

Viewed 715

I am trying to achieve polymorphic behaviour in my rails application. I have 3 models:

  • Customer
  • Supplier
  • Transaction

Transaction has a Party(either customer or supplier).

I have created a model Party as concern when I am trying to save or update I receive this error.

Error on save

The models are as follows:

party.rb

module Party
  extend ActiveSupport::Concern

  included do
    has_many :transactions, :as => :party
  end
end

customer.rb

class Customer < ApplicationRecord
  include Party
  ....
end

supplier.rb

class Supplier < ApplicationRecord
  include Party
  ....
end

transaction.rb

class Transaction < ApplicationRecord
  belongs_to :party, polymorphic: true

  ....
end

When hit save the params received at server are

Parameters: {"authenticity_token"=>"....", "transaction"=>{"party"=>"4", ...}, "commit"=>"Update Transaction", "id"=>"2"}

Hope someone can figure out the issue. Thanks for your time.

Here is the transactions schemea.

create_table "transactions", force: :cascade do |t|
    t.date "date"
    t.string "transaction_type"
    t.integer "supplier_id"
    t.integer "customer_id"
    t.string "party_type", null: false
    t.bigint "party_id", null: false
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["party_type", "party_id"], name: "index_transactions_on_party_type_and_party_id"
  end
4 Answers

Can you add transactions table schema in question?
I am assuming that the transactions table has party_id and party_type.
So if the columns are present then you need check view so that in params controller should receive like "transaction"=>{"party_id"=>"4", "party_type"=>"custmer"...}

If you want to achieve Polymorphism using Party you will have to change the code as the following:

  1. Create table called "parties" contains resource_id (same as the other models type) and resource_type (String).
  2. declare a relation inside your Party model "belongs_to :resource, polymorphic: true"
  3. Update the Customer and Supplier model to contain "has_many / has_one :party(eis), as: :resource".

Your code treat party as a module which does not implement primary_key method as the activerecord use it to build the associations on create or update.

According to what you exposed here, this issue is caused by the params sent. You need to make sure that "party_id" holds the polymorphic resource id and "party_type" either "customer" or "supplier". Also as you are using rails' polymorphic behaviour, "supplier_id" and "party_id" columns are no longer relevant.

There is problem with params, it contains "party" => "4", for polymorphic association you can have one of the following:

  1. "transaction"=>{"party_type" => "Customer", "party_id" => "4", ... }
  2. "transaction"=>{"party" => <Customer OR supplier object>, ... }

Rails will try transactioin.party.class.primary_key to find out primary_key column, in above case it will get String for transaction.party.class and hence it will raise NoMethodError (undefined method primary_key' for String:Class)`.

You can try assigning transaction.party = "4" in rails console, you will get same error.

Related