简体   繁体   English

Ruby on Rails 删除带有外键的设备

[英]Ruby on Rails deleting fixtures with foreign keys

I'm having trouble setting up the tests with fixtures that use foreign keys!我在使用使用外键的装置设置测试时遇到问题! And I would be much appreciated if someone could help me understand this.如果有人能帮助我理解这一点,我将不胜感激。

Let's say for example the :user_type model has a reference to the :role model, when tests get executed, and all the data in the test db is deleted to be again re-inserted, Rails deletes the data from the role model first, instead of deleting the data first from the :user_type and only then from :role .例如,假设:user_type模型引用了:role模型,当测试执行时,测试数据库中的所有数据都被删除重新插入,Rails 首先从角色模型中删除数据,而不是首先从:user_type中删除数据,然后再从:role中删除数据。

The fixtures:固定装置:

#roles.yml
technical:
  name: 'Technical'
  obs: 'User Role for technical / maintenance users!'


#user_types.yml
technic:
  role: technical
  name: 'Technic'
  is_admin: true

The tests:测试:

#app/test/models/role_test.rb
require 'test_helper'

class RoleTest < ActiveSupport::TestCase
  fixtures :roles

  test 'save Role without name' do
    data = Role.new()
    data.valid?
    assert data.errors.added?(:name, :blank), 'Role saved without name!'
  end
end


#app/test/models/user_type_test.rb
require 'test_helper'

class UserTypeTest < ActiveSupport::TestCase
  fixtures :user_types

  test 'save User Type without role_id' do
    data = UserType.new(:name => 'public')
    data.valid?
    assert data.errors.added?(:role_id, :blank), 'User Type saved without role'
  end
end

When the tests ran for the first time, everything goes ok.第一次运行测试时,一切正常。 Rails cleans the database (in this point is still empty, so there is no constraint violations), then the data from the fixtures gets inserted, and the tests run fine. Rails 清理数据库(此时仍为空,因此没有违反约束),然后插入来自设备的数据,并且测试运行良好。 The next time I try to run the tests they will fail because when rails starts to delete the data from the database, it starts with the role model/table instead of the user_type!下次我尝试运行测试时,它们将失败,因为当 rails 开始从数据库中删除数据时,它以角色模型/表而不是 user_type 开始! Because foreign keys where defined on these models a violation will occur because user_type is still referring to data in the model table!因为在这些模型上定义的外键会发生违规,因为 user_type 仍然引用模型表中的数据!

How should this be done properly?这应该如何正确完成? Is there any mechanism to tell rails the order to destroy the fixture data?是否有任何机制可以告诉 rails 销毁夹具数据的顺序? I am using RubyOnRails 4 and Firebird 2.5 by the way.顺便说一下,我正在使用 RubyOnRails 4 和 Firebird 2.5。

I have been struggling with this for a few days and I haven't been able to do it right!我已经为此苦苦挣扎了几天,但我一直无法做到正确!

Thank you in advance!先感谢您!

I encountered a similar problem, but I use PostgreSQL.我遇到了类似的问题,但我使用的是PostgreSQL。 I am posting my solution with the hope that an analogous solution exists for Firebird (which I have not personally used).我发布了我的解决方案,希望 Firebird 存在类似的解决方案(我没有亲自使用过)。

As you mentioned, when Rails executes the test suite, it starts by deleting all of the data in the database tables.正如您提到的,当 Rails 执行测试套件时,它首先删除数据库表中的所有数据。 This is tricky in the presence of foreign key constraints.在存在外键约束的情况下,这很棘手。 In theory, one way to handle these constraints would be to figure out an appropriate order in which to delete records.理论上,处理这些约束的一种方法是找出删除记录的适当顺序。

However, at least for PostgreSQL, Rails actually sidesteps the issue by temporarily disabling the triggers which would otherwise prevent violation of these foreign key constraints.然而,至少对于 PostgreSQL,Rails 实际上通过暂时禁用触发器来回避这个问题,否则会阻止违反这些外键约束。 It does this with the following (probably vendor-specific) pair of SQL commands, executed before and after the DELETE commands:它使用以下(可能是供应商特定的)SQL 命令对执行此操作,在 DELETE 命令之前和之后执行:

ALTER TABLE tablename DISABLE TRIGGER ALL
ALTER TABLE tablename ENABLE TRIGGER ALL

There's a catch: only a superuser role (where "role" basically means "user" in this context) can execute these commands.有一个问题:只有超级用户角色(在此上下文中“角色”基本上意味着“用户”)才能执行这些命令。 Effectively, Rails can't run tests unless it is using a PostgreSQL role with the superuser privilege.实际上,除非使用具有超级用户权限的 PostgreSQL 角色,否则 Rails 无法运行测试。 Maybe Firebird has superusers, too?也许 Firebird 也有超级用户?

Side note for PostgreSQL users with this problem:有此问题的 PostgreSQL 用户的旁注:

To grant superuser to a user (only do this on your test or development database)将超级用户授予用户(仅在您的测试或开发数据库上执行此操作)

Run this SQL statement: ALTER USER myuser WITH SUPERUSER;运行此 SQL 语句: ALTER USER myuser WITH SUPERUSER;

This is the error message that appears as of Rails 4.2.4 when attempting to test without superuser privilege:这是 Rails 4.2.4 在没有超级用户权限的情况下尝试测试时出现的错误消息:

ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "tablename" violates foreign key constraint ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: 错误:更新或删除表“tablename”违反外键约束

In a future version of Rails, this more specific error message will be shown:在 Rails 的未来版本中,将显示以下更具体的错误消息:

WARNING: Rails was not able to disable referential integrity.警告:Rails 无法禁用参照完整性。 This is most likely caused due to missing permissions.这很可能是由于缺少权限造成的。 Rails needs superuser privileges to disable referential integrity. Rails 需要超级用户权限才能禁用参照完整性。

See rails/rails commit 72c1557 and PR #17726 for more details.有关更多详细信息,请参阅 rails/rails commit 72c1557PR # 17726

I had this same issue when working on a Ruby on Rails Application.我在处理Ruby on Rails应用程序时遇到了同样的问题。

I had a user model with a join table to the role and permission model:我有一个带有连接表的user模型到rolepermission模型:

User model用户模型

class User < ApplicationRecord
  has_many :user_permissions, dependent: :destroy
  has_many :permissions, through: :user_permissions, dependent: :destroy
  has_many :user_roles, dependent: :destroy
  has_many :roles, through: :user_roles, dependent: :destroy
end

Role model好榜样

class Role < ApplicationRecord
  has_many :role_permissions, dependent: :destroy
  has_many :permissions, through: :role_permissions, dependent: :destroy
  has_many :user_roles, dependent: :destroy
  has_many :roles, through: :user_roles, dependent: :destroy
end

Permission model权限模型

class Role < ApplicationRecord
  has_many :user_permissions, dependent: :destroy
  has_many :permissions, through: :user_permissions, dependent: :destroy
  has_many :role_permissions, dependent: :destroy
  has_many :permissions, through: :role_permissions, dependent: :destroy
end

But when I try to delete all users , roles and permissions on the application while working in the development using the command:但是,当我在使用以下命令进行开发时尝试删除应用程序上的所有usersrolespermissions时:

Permission.delete_all
Role.delete_all
User.delete_all

I get the error:我收到错误:

rails aborted!轨道中止! ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "permissions" violates foreign key constraint "fk_rails_439e640a3f" on table "role_permissions" DETAIL: Key (id)=(1) is still referenced from table "role_permissions". ActiveRecord::InvalidForeignKey:PG::ForeignKeyViolation:错误:更新或删除表“permissions”违反表“role_permissions”上的外键约束“fk_rails_439e640a3f”细节:键(id)=(1)仍然从表“role_permissions”引用.

Here's how I fixed it :这是我修复它的方法

The issue was that the delete_all method did not call the delete operation on the associated objects since we only called it directly on the individual resources ( Users , Permissions , and Roles ).问题是delete_all方法没有调用关联对象的删除操作,因为我们只直接在单个资源( UsersPermissionsRoles )上调用它。 We also need to call it specifically on the join tables as well.我们还需要在连接表上专门调用它。

I simply modified the delete_all operation to first delete the join table association for each of the models before running the delete_all operation using the commands below:我只是修改了delete_all操作,在使用以下命令运行delete_all操作之前,首先删除每个模型的连接表关联:

RolePermission.delete_all
Permission.delete_all

UserRole.delete_all
Role.delete_all

User.delete_all

This time everything worked fine.这次一切正常。

I hope this helps我希望这有帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM