[英]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 72c1557和PR # 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
模型到role
和permission
模型:
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:但是,当我在使用以下命令进行开发时尝试删除应用程序上的所有users
、 roles
和permissions
时:
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
方法没有调用关联对象的删除操作,因为我们只直接在单个资源( Users
、 Permissions
和Roles
)上调用它。 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.