Open Kani999 opened 6 years ago
*HABTM (has and belongs to many) not HMABT (has many and belongs to)
any follow up on this? I am having the same problem with a has_many through... relation
@beeberino
My current configuration looks like this
class User < ApplicationRecord
rolify strict: true
has_many :users_roles
has_many :roles, through: :users_roles, dependent: :destroy
has_associated_audits
class Role < ApplicationRecord
# has_and_belongs_to_many :users, join_table: :users_roles
has_many :users_roles
has_many :users, through: :users_roles, dependent: :destroy
I did not solve HABTM
Just ran into this as well. It looks like this is a limitation of audited: https://github.com/collectiveidea/audited/issues/316
has_associated_audits
is meant to mirror audited associated_with:
, but the latter assumes it is dealing with a belongs_to
relationship. So when it tries to write the audit here, it tries to save what it thinks is the belongs_to
association to the polymorphic associated
on the Audit
model, and that returns an array rather than a record, which ActiveRecord is rightfully very upset about.
I found a solution for my use case, so far, at least.
Thankfully, rolify
passes the after_add
and after_remove
callbacks to the HABTM. See https://github.com/RolifyCommunity/rolify/blob/0c883f4173f409766338b9c6dfc64b0fc8ec8a52/lib/rolify.rb#L28-L30.
Using the HABTM callbacks, which are passed to has_many
, you can insert a new audit record manually.
For example:
class User < ApplicationRecord
rolify after_add: :audit_add_role, after_remove: :audit_remove_role
audited
private
def audit_add_role(role)
audits.create! action: :add_role, associated: role
end
def audit_remove_role(role)
audits.create! action: :remove_role, associated: role
end
end
The solution is not perfect and has its limitations. For instance, calling undo
on the audit record will raise an exception. A workaround might be monkey patching the Audited::Audit
model to handle the undoing of HABTM records.
You could do something like this (this is untested and not perfect, just writing it out here for an example):
module AuditHasAndBelongsToManyFix
def undo
case action
when "add"
# You'll need to find a way to get the relation name.
# One idea is to store it in the `audited_changes`; you'll need to make sure to do this in the HABTM callbacks
public_send(audited_changes['relation']).delete associated
when "remove"
public_send(audited_changes['relation']).push associated
else
super
end
end
end
ActiveSupport.on_load(:active_record) { Audited::Audit.prepend AuditHasAndBelongsToManyFix }
I ended up getting around the problem with has_and_belongs_to_many
by using the callbacks to create manual auditions for my user model.
# Inside my user.rb model:
has_and_belongs_to_many :other_model, after_add: :audit_add, before_remove: :audit_remove
def audit_add(other_model)
audits.create!(
associated_id: other_model.id,
associated_type: other_model.class,
audited_changes: "Included association with #{other_model.class} ID #{other_model.id}."
)
end
def audit_remove(other_model)
audits.create!(
associated_id: other_model.id,
associated_type: other_model.class,
audited_changes: "Removed association with #{other_model.class} ID #{other_model.id}."
)
end
In my case, a legacy application that we didn't want to make too many changes just to have a history of relationship additions or removals, this worked.
I hope it can help some of the folks here.
I've got model
user
androle
When I create a new role, I've got the error:
I don't really now whats the problem, did I wrongly specified something?