ON DELETE RESTRICT: Problems with fixtures loading?

Hi all

I’m quite unsure about this topic, so before hacking some stuff I try to
get some help here. :slight_smile:

I use the foreign keys migrations plugin from red hill consulting which
automatically creates foreign keys when using xxx_id in migrations or
specifying them manually.

I set them all to ON DELETE RESTRICT because I want my application to
handle all the dependencies stuff itself and I don’t want it to rely on
the database to delete depending rows. E.g. it would have an
uncontrolled destructive manner if I tried to delete object X that has
dependencies, but MySQL silently kills all dependencies without any
warning. So want to restrict all this and when removing all depending
records the application itself should know about it and do it itself.

But now I have problems with testing. I have a country model and a
dependent music artist model.
The functional tests for the countries only loads fixtures :countries,
so when running them on a completely empty test database the tests work
well.
The functional tests for the music artists load fixtures :countries AND
:music_artists because every music artist has an origin country to
specify. So now I run the functional tests for music artist and they
work well, too.
But after running the tests for music artists I can’t run the tests for
countries anymore, I get errors like:

  1. Error:
    test_show(Admin::CountriesControllerTest):
    ActiveRecord::StatementInvalid: Mysql::Error: Cannot delete or update a
    parent row: a foreign key constraint fails
    (pgbookings_test/music_artists, CONSTRAINT music_artists_ibfk_1
    FOREIGN KEY (origin_country_id) REFERENCES countries (id: DELETE
    FROM countries
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/abstract_adapter.rb:128:in
    log' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/mysql_adapter.rb:243:inexecute’
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/mysql_adapter.rb:258:in
    update' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/abstract/database_statements.rb:47:indelete’
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:285:in
    delete_existing_fixtures' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:256:increate_fixtures’
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:256:in
    each' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:256:increate_fixtures’
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:255:in
    transaction' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:255:increate_fixtures’
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:248:in
    silence' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:248:increate_fixtures’
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:593:in
    load_fixtures' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:538:insetup_with_fixtures’
    /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:575:in
    setup' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/fixtures.rb:574:insetup’

When examining the back trace I saw the method call
“delete_existing_fixtures”. Now I guess because the tests for countries
only use the :countries fixtures it also only tries to remove the
existing fixtures in this table, and because there are still music
artists from the last tests MySQL complains about dependent rows. Is
that possible?
If so, I guess the only solution would be to force Rails to always
empty the whole database tables
for every test before re-populating it
with the wanted fixtures. Might that be?

Thanks a lot for you help, Josh

Well, I found more informations about this issue, but don’t know what
exactly to do with it…

http://dev.rubyonrails.org/ticket/2404

This seems to be a common problem and it’s more than a year old, so I
really ask myself why it has not been fixed in the current release of
Rails?

All I want is that after every test the fixtures I loaded are deleted
again. This can’t be so hard, can it? :frowning:

I tried to apply the specified patch, but that’s the result:

chraftbuech:~/Webwork/pgbookings josh$ patch <
FK_constraint_cleanup.diff
can’t find file to patch at input line 5
Perhaps you should have used the -p or --strip option?
The text leading up to this was:

|Index: vendor/rails/activerecord/lib/active_record/fixtures.rb
|===================================================================
|— vendor/rails/activerecord/lib/active_record/fixtures.rb (revision 2479)

+++ vendor/rails/activerecord/lib/active_record/fixtures.rb (working copy)
File to patch: vendor/rails/activerecord/lib/active_record/fixtures.rb
patching file vendor/rails/activerecord/lib/active_record/fixtures.rb
Hunk #1 FAILED at 219.
Hunk #2 succeeded at 507 with fuzz 2 (offset -3 lines).
Hunk #3 succeeded at 608 with fuzz 1 (offset 71 lines).
1 out of 3 hunks FAILED – saving rejects to file
vendor/rails/activerecord/lib/active_record/fixtures.rb.rej

I’m quite frustrated already because all my cool tests are not running
anymore because of the new dependencies added… :frowning:

Please help. Thanks
Josh

Well, I tested this code from http://dev.rubyonrails.org/ticket/2404 and
it works for me:

class Fixture
attr_reader :class_name
end

class Fixtures
@@inserted_fixture_list ||= {}
alias :original_insert_fixtures :insert_fixtures

def insert_fixtures
return if @@inserted_fixture_list[values[0].class_name]
@@inserted_fixture_list[values[0].class_name] = true
unless ActiveRecord::Base.connection.select_one(“select 1 from
#{fixture_class_to_table_name(values[0].class_name)}”)
original_insert_fixtures
end
end

def delete_existing_fixtures() end

compute a fixture’s table name from its (known) class name

def fixture_class_to_table_name(class_name)
Inflector::tableize(class_name)
end
end

But I hate it to change Rails’ default behavior with code I don’t really
understand and might disturb Rails in a later release… :-/