我在使用使用外键的装置设置测试时遇到问题!如果有人能帮助我理解这一点,我将不胜感激。
假设
:user_type
模型引用了 :role
模型,当执行测试时,测试数据库中的所有数据都被删除并重新插入,Rails 会从角色模型中删除数据首先,不要先从 :user_type
中删除数据,然后再从 :role
中删除数据。
赛程:
#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 清理数据库(此时仍然是空的,因此不存在违反约束的情况),然后插入来自装置的数据,并且测试运行良好。 下次我尝试运行测试时,它们将失败,因为当 Rails 开始从数据库中删除数据时,它会从角色模型/表而不是 user_type 开始!因为在这些模型上定义的外键会发生违规,因为 user_type 仍然引用模型表中的数据!
这应该如何正确完成? 是否有任何机制告诉 Rails 销毁夹具数据的顺序?顺便说一句,我正在使用 RubyOnRails 4 和 Firebird 2.5。
我已经为此苦苦挣扎了几天,但我一直没能做好!
提前谢谢您!
我遇到了类似的问题,但我使用的是 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: 错误:表“tablename”上的更新或删除违反了外键 约束
在 Rails 的未来版本中,将显示以下更具体的错误消息:
警告:Rails 无法禁用引用完整性。 这很可能是由于缺少权限造成的。 Rails 需要超级用户权限才能禁用引用完整性。
请参阅 Rails/rails commit 72c1557 和 PR #17726 了解更多详细信息。
我在开发 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
榜样
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
但是当我在开发过程中使用以下命令尝试删除应用程序上的所有
users
、roles
和 permissions
时:
Permission.delete_all
Role.delete_all
User.delete_all
我收到错误:
铁轨已中止! ActiveRecord::InvalidForeignKey:PG::ForeignKeyViolation:错误:表“permissions”上的更新或删除违反了表“role_permissions”上的外键约束“fk_rails_439e640a3f” 详细信息:密钥 (id)=(1) 仍然从表“role_permissions”中引用。
这是我修复它的方法:
问题是
delete_all
方法没有对关联对象调用删除操作,因为我们只直接对单个资源(Users
、Permissions
和 Roles
)调用它。我们还需要在连接表上专门调用它。
我只是修改了
delete_all
操作,首先删除每个模型的连接表关联,然后使用以下命令运行 delete_all
操作:
RolePermission.delete_all
Permission.delete_all
UserRole.delete_all
Role.delete_all
User.delete_all
这次一切顺利。