当我有一个时间戳时,Minitest 会出错 has many through jointable

Getting errors with Minitest when I have a timestamp for a has many through jointable

我有一个连接table,因为一个有很多并且属于很多通过,包含许多其他属性的连接table有一个时间戳,实现明智没有麻烦,

用户

class User < ApplicationRecord

  has_many :affiliations
  has_many :organizations, through: :affiliations

end

组织

class Organization < ApplicationRecord

  has_many :affiliations
  has_many :users, through: :affiliations

end

隶属关系

class Affiliation < ApplicationRecord

  belongs_to :user
  belongs_to :organization

  has_many :xxxxxs
end

从属关系存储的不仅是属于,它本身还包含用户的等级以及组织中不属于的等级等信息。它本身就是一个强大的模型。

对于固定装置,我还没有用于连接的文件table,

user.yml

user1:
  email: aaa@aaa.com
  organizations: org1

organization.yml

org1
  name: foo

但是当我 运行 使用 minitest 进行测试时,它给了我一个错误。

Error:
PublicControllerTest#test_should_get_index:
ActiveRecord::StatementInvalid: Mysql2::Error: Field 'created_at' doesn't have a default value: INSERT INTO `affiliations` (`user_id`, `dominion_id`) VALUES (794918725, 299359653)

奇怪的是,它发生在甚至不使用上述 table、

的测试中
class PublicControllerTest < ActionController::TestCase

  test "should get index" do
    get :index
    assert_response :success
  end
end

此操作完全没有任何作用,此时它只是普通的 html

class PublicController < ApplicationController
  def index
  end
end

在控制器中什么都不做。

当删除时间戳时它们会消失,但记录关联创建时间是必要的信息。有什么我需要在测试中做的吗?

我正在使用 Rails edge (5.0.0rc1) 是否有可能是这导致了错误?

更新 3.

在灯具种子数据中为您的 user1 设置 "organizations: org1" - 似乎这是导致问题的原因,因为用户只能通过您的联合连接到组织 table.

我没有在规范中找到任何明确的内容,但是相关的内容 here

Fixtures bypass the normal Active Record object creation process. After reading them from YAML file, they are inserted into database directly using insert query. So they skip callbacks and validations check. This also has an interesting side-effect which can be used for drying up fixtures.

更新 2.

我错误地假设您不能在由 Rails 管理的 has_and_belongs_to_many jointable 中设置时间戳。事实上,在 HasAndBelongsToMany Rails 内部将为 table - here

创建一个 ActiveRecord::Base class
def through_model
   habtm = JoinTableResolver.build lhs_model, association_name, options
   join_model = Class.new(ActiveRecord::Base) {
     class << self;
     ...

并且ActiveRecord::Base包括时间戳module

因此,您的错误应该是由标准 Rails 关联以外的其他方式在 jointable 中创建条目引起的。


原创.

我不相信您可以在 ActiveRecord 中为 has_and_belongs_to_many 关系自动管理 jointable 中的时间戳字段。这在旧 Rails(例如下面的 3.2 - link)中(有意)不起作用,而且听起来最近也没有改变。

如果你想扩展连接 table,你可以为它创建一个专用的 ActiveRecord 模型并使用 use has_many :through association。这样,如果您将其添加到 table 定义中,它将自动支持时间戳。

有关 HABTM 加入的时间戳,请参阅 https://github.com/rails/rails/issues/4653table

AFAICT Rails 3.1 does not populate timestamps on a join table. The only difference is that in 3.2, when you add timestamps, they are marked as NOT NULL.

@veganstraightedge the timestamps didn't "work" in 3.1 - they just didn't raise an error when the join table was saved with them as null. the difference here is that in 3.2 timestamps are created with a NOT NULL constraint.

基本上,这可能是因为您没有用于连接的 ActiveRecord 模型 classtable(更新 2 - 实际上您有!),时间戳是 ActiveRecord 模型的特征。 Rails 5.0rc1 中的时间戳没有太大变化 - sources - 时间戳是一个扩展 ActiveRecord class.

的模块

顺便说一下,现在建议使用 create_join_table 迁移助手,它将创建 "pure" table(只有两个 ID,没有时间戳): https://github.com/rails/rails/pull/4726

SO 有类似错误的问题 - Rails 3.2 + MySQL: Error: Field 'created_at' doesn't have a default value: INSERT INTO

Rails 3.2 doesn't automatically populate the timestamp fields for join tables of :habtm relationships.


或者(警告 - 理论!),您可以尝试使用关联回调或关联扩展 - http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Association callbacks

Similar to the normal callbacks that hook into the life cycle of an Active Record object, you can also define callbacks that get triggered when you add an object to or remove an object from an association collection.

class Project
  has_and_belongs_to_many :developers, after_add: :evaluate_velocity

  def evaluate_velocity(developer)
    ...
  end
end

Extensions

The extension argument allows you to pass a block into a has_and_belongs_to_many association. This is useful for adding new finders, > creators and other factory-type methods to be used as part of the association.

has_and_belongs_to_many :contractors do
  def find_or_create_by_name(name)
    first_name, last_name = name.split(" ", 2)
    find_or_create_by(first_name: first_name, last_name: last_name)
  end
end

Extensions can refer to the internals of the association proxy using these three attributes of the proxy_association accessor:

proxy_association.owner returns the object that the association is a part of. 
proxy_association.reflection returns the reflection object that describes the association. 
proxy_association.target returns the associated object for belongs_to or has_one, or the collection of associated objects for has_many or has_and_belongs_to_many.

您的模型之间的关系无法用联接表示 table。它们需要由常规模型 table 表示(并且它需要 id,created_at,updated_at)。它们应该都是常规的,ActiveRecord::Base 型号。

在这种情况下,您有一个加入模型、从属关系,而不是一个加入table

所以对于这三个,你需要一个常规的活动记录库table(包含id,created_at,updated_at)

Join tables 仅用于 has_and_belongs_to_many 关联,这些允许 tables 没有 id,created_at, updated_at