Possible DangerousAttributeError

656 views
Skip to first unread message

Brett Blackham

unread,
Feb 21, 2008, 8:02:05 PM2/21/08
to Composite Keys for ActiveRecord
Hello,
I am still fairly new to rails so forgive me should my error not be
related to composite_primary_keys. I am seeing the following error
when playing with my ActiveDirectory models:

/usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/
attribute_methods.rb:88:in `instance_method_already_implemented?':
hash is defined by ActiveRecord
(ActiveRecord::DangerousAttributeError)
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/attribute_methods.rb:64:in `define_attribute_methods'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/attribute_methods.rb:63:in `each'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/attribute_methods.rb:63:in `define_attribute_methods'
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/attribute_methods.rb:60:in `method_missing'
from ./grabfiles.rb:32

Here is where it gets extra strange. If I run ./script/console and run
the same command twice, I get the error the first time, but after that
everything works perfectly:

>> cc[0].contextvalue
ActiveRecord::DangerousAttributeError: hash is defined by ActiveRecord
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/attribute_methods.rb:88:in
`instance_method_already_implemented?'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/attribute_methods.rb:64:in `define_attribute_methods'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/attribute_methods.rb:63:in `each'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/attribute_methods.rb:63:in `define_attribute_methods'
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/attribute_methods.rb:60:in `method_missing'
from (irb):3
>> cc[0].contextvalue
=> "C:\\Documents and Settings\\Brett Blackham\\Recent\
\output.txt.lnk"
>> cc[0].contextvalue
=> "C:\\Documents and Settings\\Brett Blackham\\Recent\
\output.txt.lnk"
>> cc[0].contextvalue
=> "C:\\Documents and Settings\\Brett Blackham\\Recent\
\output.txt.lnk"

I should also warn you that I am using Active Directory against a 3rd
party Postgres DB. Which means I could have easily messed up my
set_table_name, set_primary_key(s), :foreign_key settings. It also
means the Postgress Table names are not standard rails names.

darrin

unread,
Feb 22, 2008, 7:22:54 AM2/22/08
to Composite Keys for ActiveRecord
Is there a column in your table called hash? If so this is Rails
warning you that the dynamic accessor methods that it's going to
define for that column is going to conflict with an already existing
method name. The reason you only get it once is because Rails only
generates the accessor methods the first time you try to access one
and it sees that they are not generated yet.

Brett Blackham

unread,
Feb 22, 2008, 12:04:51 PM2/22/08
to Composite Keys for ActiveRecord
Darrin,
Have you been doing this a long time? I do have a hash attribute in
the table. What can I do to get around this problem (Where should I
look). I googled: 'ActiveRecord hash attribute database
DangerousAttributeError' with no luck. Renaming the postgres DB
attributes is not an option. Can I map the hash DB attribute to
something else in my data model? :hash => :my_hash.

darrin

unread,
Feb 22, 2008, 4:22:52 PM2/22/08
to Composite Keys for ActiveRecord
How long have I been doing what, posing as a Rails guy? Been dabbling
in it for a year or two now.

As for your problem, try adding this to your model...

class << self # Class methods
alias :all_columns :columns

def columns
all_columns.reject {|c| c.name == 'hash'}
end
end

I didn't see any elegant way of aliasing table attributes, but this
hack will trick Rails into not trying to create accessor methods for
'hash'. If you ever need to get/set 'hash', you can use the
read_attribute/write_attribute methods directly.Good luck.

Darrin.

Brett Blackham

unread,
Feb 23, 2008, 11:38:56 PM2/23/08
to Composite Keys for ActiveRecord
Works perfectly. Now I would like to understand a little more about
what it does. Just looking at it I would guess I am overloading the
the all_columns method with a new one called columns. The new columns
will reject adding table attributes if they its name is already
"hash". How close am I? If I am right, and that code overloads
all_columns with columns, why does it call all_columns from the
method?

darrin

unread,
Feb 24, 2008, 8:58:31 AM2/24/08
to Composite Keys for ActiveRecord
The first parameter alias is the new method name, so you are aliasing
the current :columns to :all_columns. Rails will call :columns to get
the list of attributes to create accessors for, it will get our
new :columns method, the new :columns method calls the
original :columns (now :all_columns) to get the actual list of
columns, but removes the hash column before returning them.

Brett Blackham

unread,
Feb 25, 2008, 10:05:07 AM2/25/08
to Composite Keys for ActiveRecord
Now I have had the good fortune to find a table with 'hash' as a
primary key. The table has composite keys. One key is 'partition', the
other is 'hash'. I can no longer just tell AR to skip the attribute/pk
hash.

How can I use AR with this table?
How would the compositekeys module work with this table? (How would I
have other table/models reference this table/model?) *Look, I'm back
on topic with a compositekeys module question*

darrin

unread,
Feb 25, 2008, 12:17:08 PM2/25/08
to Composite Keys for ActiveRecord


On Feb 25, 9:05 am, Brett Blackham <brett.black...@gmail.com> wrote:
> other is 'hash'. I can no longer just tell AR to skip the attribute/pk
> hash.
>

Why not? Have you tried it?

> How can I use AR with this table?
> How would the compositekeys module work with this table? (How would I
> have other table/models reference this table/model?) *Look, I'm back
> on topic with a compositekeys module question*

Do the same thing you would with any other table. Let us know what you
find.

Brett Blackham

unread,
Feb 25, 2008, 2:14:36 PM2/25/08
to Composite Keys for ActiveRecord
Simplified schema of my tables -
Users Table:
operatorid: integer, pk
gropuid: integer
...

Events Table:
operatorid: integer, pk
colid: integer, pk
contextid: integer, pk
hash: varchar
partition: date
...

Data Table:
hash: varchar, pk
partition: date, pk
data: blob
...



I am trying to pull information for a report using an existing
Postgres DB. I have users, events and data tables. Users are
associated with Data via Events. Enter the first problem.

The relations between Users and Events. The User relation with the
Events table is only via the operatorid. However, the PK for the
Events table include colid and contextid. (Don't ask my why, I just
work here). I setup the Events model with "set_primary_keys" on all
three of the existing PK's in Events because thats what the DB schema
says. I then created the 1:N relation in Events with
"belongs_to :user, :foreign_key => :operatorid". The Users model has a
has_many :events, :foreign_key => :operatorid.

Problem 1: When using irb I tried to grab event User.events but I get
the following error: Incorrect number of primary keys for Events:
[:operatorid, :colid, :contextid]. It looks like the composite key is
trying to enforce the key. (Which I would say is the right thing to
do, but not the most compatible thing to do). Why doesn't is just use
the belongs_to foreign_key assignment? Should I be using :through?

My Solution/Hack 1: I am still very new to Ruby and RoR so I am not
sure if this is the right way, but it did work. I added the following
def to my User model:
<code>
def skip_pk_check_events
Events.find( :all, :conditions => { :operatorid =>
self.opeartorid } )
end
</code>
Now I can access my events from irb with user.skip_pk_check_events[]
I'd rather still have user.events[] work. Is that possible? Or is my
hack the best way?


Now that I have my Event I want to start accessing its data. Enter
Problem 2

Problem 2: The Events table has an attribute called hash. This breaks
AR because hash is a reserved word.
Solution 2: I added the following code to my Event model:
<code>
def my_hash
self['hash']
end

def my_hash=(value)
self['hash'] = value
end

class << self
alias :all_columns :columns

def columns
all_columns.reject {|c| c.name == 'hash'}
end
end
</code>
This stopped AR from dieing with a DangerousAttributeError. It also
allows me to access my table.hash value via my_has (Event.my_hash will
return the orig value)

Now that I have a handle on event, and can access its attributes, I
want to get at the real data in Data.
I added a has_many :data, :foreign_key[:my_hash, :parition] to my
Events model (Note, I tried both my_hash and hash, both give me the
same problems coming up). I created my Datum model with
set_primary_keys :my_hash, :partition #Note I tried both hash and
my_hash. I also added belongs_to :event, :foreign_key =>
[:hash, :partition]. Since Data also has an attribute called hash I
added the same "hack" I used for the Events model.

Problem 1) When I load an event, and try to access data (event.data) I
get the following error:
IndexError: element size differs (3 should be 2)
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/associations.rb:267:in `transpose'
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/associations.rb:267:in
`full_columns_equals'
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/associations.rb:285:in `construct_sql'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/associations/has_many_association.rb:6:in `initialize'

Problem 2) When I load the a datum, and try to access the event
(datum.event) I get the following error:
RuntimeError: [nil, Wed, 20 Feb 2008]: Incorrect number of primary
keys for Event: [:operatorid, :colid, :collectioncontextindex]
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/base.rb:250:in `find_from_ids'
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/base.rb:245:in `each'
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/base.rb:245:in `find_from_ids'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/base.rb:504:in `find'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/associations/belongs_to_association.rb:44:in
`find_target'


It looks like these errors are again the result of composite keys
trying to enforce the PK. So.. should I solve this problem like I did
with User::Events? (Create my own def to handle the query)


darrin

unread,
Feb 26, 2008, 9:05:25 AM2/26/08
to Composite Keys for ActiveRecord
Here's what I came up with...

class User < ActiveRecord::Base
set_primary_key :operatorid

has_many :events, :foreign_key => :operatorid
end

class Event < ActiveRecord::Base
class << self # Class methods
alias :all_columns :columns

def columns
all_columns.reject {|c| c.name == 'hash'}
end
end

set_primary_keys :operatorid, :colid, :contextid

belongs_to :user, :foreign_key => :operatorid

belongs_to :event_datum, :foreign_key => [:hash, :partition]
end

class EventDatum < ActiveRecord::Base
class << self # Class methods
alias :all_columns :columns

def columns
all_columns.reject {|c| c.name == 'hash'}
end
end

set_primary_keys :hash, :partition

has_one :event, :foreign_key => [:hash, :partition]
end

Looking at your tables I didn't see how you can have many event data
associated with one event so that's why there is a has_one/belongs_to.

Loading development environment (Rails 2.0.2)
>> User.find(:first).events
=> [#<Event operatorid: 1, colid: 1, contextid: 1, partition:
"partition1">, #<Event operatorid: 1, colid: 2, contextid: 2,
partition: "partition2">]
>> User.find(:first).events[0].event_datum
=> #<EventDatum partition: "partition1", data: "data1">

Brett Blackham

unread,
Feb 26, 2008, 11:20:49 AM2/26/08
to Composite Keys for ActiveRecord
I created the tables exactly as I summarized earlier and I used your
models and they worked great for read access on the postgres DB. Now,
I just have to figure out why the real (more complex) tables aren't
playing nicely. Its the *same schema, just more attributes... aarg!
I'll keep digging. I'll post my results once I find out what is
breaking it.

*same schema: a single event can have multiple data files associated
with it, so it should be a has_many. I doubt that is what is causing
my heart burn, but I'll test it and report.

Thanks again.

Brett Blackham

unread,
Feb 26, 2008, 12:43:39 PM2/26/08
to Composite Keys for ActiveRecord
I rewrote my models based of yours. But I still get the error below:

>> vc = ViolationCollection.find(:first)
=> #<ViolationCollection violationid: "{myid}", sessionid: 1,
collectionid: 1, partition: "2008-01-30", rowcreatedon: 3234234>
>> vc.collection_contexts
IndexError: element size differs (4 should be 3)
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/associations.rb:267:in `transpose'
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/associations.rb:267:in
`full_columns_equals'
from /usr/lib/ruby/gems/1.8/gems/composite_primary_keys-0.9.91/
lib/composite_primary_keys/associations.rb:285:in `construct_sql'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/associations/has_many_association.rb:6:in `initialize'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/associations.rb:1032:in `new'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/associations.rb:1032:in `collection_contexts'
from (irb):3

I even went as far to create my own tables, in case the existing DB
had some strange stored procedures/triggers that might have been
causing me pain. I am including my AR models and SQL for the tables in
this reply. The are the tables/model that resulted in the error above.
(Notice, I took out your :alias hash trick for now. Just to make sure
that wasn't the cause) I don't see how I am doing anything different
from the User::Event::Data example, which we both got to work. In both
cases I am making a relation between models using a "element size
[that] differs". In the User::Event::Data models we accessed the Event
model (which had 3 PKs) from the data (which had 2 PKs) using just 2
FKs. In my failed attempt I am trying to access a model that has 4
PK's using a data model that has 4 PKs using only 3 FKs. Is AR taking
a different logic path since I have 4 PK's in both models? ... I
think it is, I just removed on of the PKs on the ViolationCollection
model, so now it has just 3, and it gave me a different error.

My new error, when I removed the partition PK. Now its almost working,
its pulling the wrong values for the sessionid and parition-
ActiveRecord::StatementInvalid: PGError: ERROR: invalid input syntax
for integer: "{myid}"
: SELECT * FROM mycc WHERE ((mycc.sessionid = E'{myid}') AND
(mycc.collectionid = 1) AND (mycc.partition = 1))
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/connection_adapters/abstract_adapter.rb:150:in `log'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/connection_adapters/postgresql_adapter.rb:407:in
`execute'
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/connection_adapters/postgresql_adapter.rb:782:in
`select_raw'



Here is my exact code. Could you see if you can reproduce the same
errors I am seeing?

My models:
(collection_context.rb)
class CollectionContext < ActiveRecord::Base
set_table_name :mycc

set_primary_keys :sessionid, :collectionid, :collectioncontextindex, :partition

belongs_to :violation_collection, :foreign_key =>
[:sessionid, :collectionid, :partition]
end

(violation_collection.rb)
class ViolationCollection < ActiveRecord::Base
set_table_name :myvc

set_primary_keys :violationid, :sessionid, :collectionid, :partition

has_many :collection_contexts, :foreign_key =>
[:sessionid, :collectionid, :partition ]
end


CREATE TABLE mycc
(
sessionid int8 NOT NULL,
collectionid int4 NOT NULL,
collectioncontextindex int4 NOT NULL,
hash varchar,
recorddate float8,
rowcreatedon int8,
ctcode int4,
sccode int4,
ctvalue varchar,
scvalue varchar,
property bytea,
partition date NOT NULL,
CONSTRAINT mycc_pkey PRIMARY KEY (sessionid, collectionid,
collectioncontextindex, partition)
)
WITHOUT OIDS;
ALTER TABLE mycc OWNER TO c60;



CREATE TABLE myvc
(
violationid varchar NOT NULL,
sessionid int8 NOT NULL,
collectionid int4 NOT NULL,
partition date NOT NULL,
rowcreatedon int8,
CONSTRAINT myvc_pkey PRIMARY KEY (violationid, sessionid,
collectionid, partition)
)
WITHOUT OIDS;
ALTER TABLE myvc OWNER TO username;

darrin

unread,
Feb 26, 2008, 4:52:06 PM2/26/08
to Composite Keys for ActiveRecord
Well, I think you get the runner up trophy for the "I really want to
use Rails and I don't care how convoluted the database schema is"
award. I think the project that I and another fellow did (which
actually introduced us to cpk) was a little worse. We ended up using
find_by_sql a lot which really defeats most of what Rails is good for.
It was so bad he quit programming and took a management job. Because
of the flexibility of the Ruby language you can shoehorn virtually any
project into Rails, but some of it's not going to be very fun.

Back to your problem...the Rails association model assumes that you
are associating a primary_key in one model(:has_many/:has_one) to a
foreign_key(:belongs_to) in another model. It kind of breaks when you
don't use the entire primary key. Maybe CPK needs an option to allow
this, which btw would be a good opportunity to contribute back to the
project:)

You might want to take a look at the :finder_sql option of :has_many
which allows you to write the association sql yourself (not sure if it
does or is supposed to work with preloading). Good Luck.

class ViolationCollection < ActiveRecord::Base
set_primary_keys :violationid, :sessionid, :collectionid, :partition
has_many :collection_contexts, :finder_sql => 'select cc.* from
violation_collections vc, collection_contexts cc where vc.violationid
= #{ids[0]} and vc.sessionid = #{ids[1]} and vc.collectionid =
#{ids[2]} and vc.partition = #{ids[3]} and vc.sessionid = cc.sessionid
and vc.collectionid = cc.collectionid and vc.partition = cc.partition'
end

Brett Blackham

unread,
Feb 26, 2008, 6:15:35 PM2/26/08
to Composite Keys for ActiveRecord
Thanks for your help. I'll start playing around with the cpk source.
It might be a good way to learn AR. (Or kill me trying. Right now I'm
betting on the kill me outcome).

How would you recommend the option work? Would it be an option on the
has_many relations like

has_many :collection_contexts :ignore_strict_pk, :foreign_key =>
[ :key_one, :key_two ]

Or would I override my default pk's for the association?

has_many :collection_contexts :relation_keys =>
[ :this_attrib1, :this_attrib2 ], :foreign_key =>
[ :attrib1, :attrib2 ]
(SQL .. "WHERE this_attrib1 = collection_context.attrib1 AND
this_attrib2 = collection_context.attrib2" )

Would I look at hacking that into cpk or somewhere else in the AR
base?

darrin

unread,
Feb 26, 2008, 9:14:00 PM2/26/08
to Composite Keys for ActiveRecord
I like the second option better since you're going to need to know
which keys to use anyway.

This would probably go in cpk since I think it is unique to composite
keys.

Don't forget the tests :)
Reply all
Reply to author
Forward
0 new messages