Ruby on Rails 如何删除带有外键的测试数据

12

我在设置使用外键的fixtures测试时遇到了麻烦!如果有人能帮助我理解这个问题,我将不胜感激。

例如,假设:user_type模型引用了:role模型,当测试被执行并且测试数据库中的所有数据被删除以重新插入时,Rails首先从:role模型中删除数据,而不是先从:user_type中删除数据再删除:role中的数据。

fixtures:

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


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

测试:

#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
当测试第一次运行时,一切都没问题。Rails清空数据库(此时为空,没有违反约束),然后插入fixture的数据,测试正常运行。 下一次运行测试时,它们将失败,因为当Rails开始从数据库中删除数据时,它以role模型/表而不是user_type开头! 因为在这些模型上定义了外键,所以会发生违规,因为user_type仍在参考模型表中的数据!
应该如何正确地做? 有没有机制告诉Rails销毁fixture数据的顺序?顺便说一下,我正在使用RubyOnRails 4和Firebird 2.5。
我已经苦苦挣扎了几天,但还没有做好!
提前致谢!

我自己没有尝试过,但你可以查看http://api.rubyonrails.org/classes/ActiveRecord/TestFixtures.html并覆盖test/test_helper.rb中的ActiveSupport :: TestCase公共接口,如setup/teardown。如果不起作用,请不要对我进行负面评价 :) - sethi
2个回答

33

我遇到了类似的问题,但是我使用的是PostgreSQL。希望有类似的解决方案适用于Firebird(我个人没有使用过)

正如你提到的,当Rails执行测试套件时,它会首先删除数据库表中的所有数据。但在存在外键约束的情况下处理起来比较棘手。理论上,一种处理这些约束的方式是找出适当的记录删除顺序。

然而,至少对于PostgreSQL,Rails实际上通过暂时禁用触发器来绕开这个问题,否则它们将防止违反这些外键约束。它使用以下(可能是供应商特定的)一对SQL命令,在DELETE命令之前和之后执行:

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

有一个陷阱:只有超级用户角色(在这个上下文中,“角色”基本上是指“用户”)才能执行这些命令。实际上,除非Rails使用具有超级用户特权的PostgreSQL角色,否则无法运行测试。也许Firebird也有超级用户?

针对 PostgreSQL 用户的解决方法:

为用户授予超级用户权限(仅在测试或开发数据库上执行此操作)

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

Rails 4.2.4 中在没有超级用户权限的情况下尝试进行测试时会出现以下错误消息:

ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "tablename" violates foreign key constraint

在未来版本的Rails中,将会显示更具体的错误消息:

WARNING: Rails was not able to disable referential integrity. This is most likely caused due to missing permissions. Rails needs superuser privileges to disable referential integrity.

请参见 rails/rails commit 72c1557 和 PR #17726 获取更多详细信息。


0
我在开发一个Ruby on Rails应用时遇到了同样的问题。
我有一个"user"模型,与"role"和"permission"模型有一个关联表:
用户模型
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

权限模型

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

但是当我尝试在开发过程中使用以下命令删除应用程序上的所有用户、角色和权限时:
Permission.delete_all
Role.delete_all
User.delete_all

我遇到了一个错误:
rails中止! ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: 在表“permissions”上更新或删除违反了表“role_permissions”的外键约束“fk_rails_439e640a3f” 详细信息:表“role_permissions”仍然引用键(id)=(1)。
这是我如何修复它的方法:
问题在于delete_all方法没有调用关联对象上的删除操作,因为我们只直接在各个资源(Users、Permissions和Roles)上调用了它。我们还需要在连接表上调用它。
我只需修改delete_all操作,在运行delete_all操作之前,先删除每个模型的连接表关联,使用以下命令:
RolePermission.delete_all
Permission.delete_all

UserRole.delete_all
Role.delete_all

User.delete_all

这次一切都很顺利。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接