【问题标题】:Create two depending models at the same time同时创建两个依赖模型
【发布时间】:2014-07-26 08:26:00
【问题描述】:

我正在尝试为每个用户可能有许多电子邮件(至少一个)的系统建模。

遵循良好的规范化规则,我创建了两个迁移(为简洁起见删除了一些字段):

create_table :users do |t|
end

create_table :user_emails do |t|
  t.integer :user_id, null: false
  t.string :email, null: false
end

add_index :user_emails, :email, :unique => true
add_foreign_key :user_emails, :users, dependent: :delete

以及以下导轨型号:

class User < ActiveRecord::Base
  has_many :emails, class_name: 'UserEmail', dependent: :destroy

  def self.find_by_email(email)
    UserEmail.find_by(email: email).try(:user)
  end

  validate do
    if emails.count < 1
      errors.add(:emails, "is empty")
    end
  end
end

class UserEmail < ActiveRecord::Base
  belongs_to :user

  validates_presence_of :user_id, :email
  validates_uniqueness_of :email
end

现在我无法创建任何这些。无法创建用户,因为它至少需要一个 UserEmail。同时,不能预先创建 UserEmail,因为它需要一个 user_id。

我相信我已经尝试过任何我能想到的@user.emails.build@user.emails &lt;&lt; e 的组合。

如何在不放弃数据一致性的情况下解决这个非常简单的问题(一个被保存而另一个不被保存)?

P.s.:我认为放松验证和使用事务可能会始终如一地解决问题。但是我从未在 Rails 中使用过事务,因此非常感谢任何帮助。

谢谢

【问题讨论】:

  • 你试过@user_email.build_user
  • 在 user_id 为空时仍然报告验证错误:(

标签: ruby-on-rails validation rails-activerecord


【解决方案1】:

validates_presence_of :user_id 只检查任何可能不是有效用户的有效整数。

但是如果你使用validates_presence_of :uservalidates_presence_of :emails,他们会检查用户和电子邮件关联是否有效且不为空。

此外,当您尝试创建用户和关联的电子邮件时,您可以在应用程序级别执行以下代码。

 user = User.new(name: 'user name')
 user.emails.build(email_address: 'email address')  #build(email: '') for your case.

然后,你可以保存到数据库

 user.save!

下面是我测试的代码。

class User < ActiveRecord::Base
  has_many :emails, class_name: 'UserEmail', dependent: :destroy
  validates_presence_of :emails, :message => 'User should have at least one email address.'
end

class UserEmail < ActiveRecord::Base
  belongs_to :user
  validates_presence_of :user
  validates_presence_of :email_address
end


[40] pry(main)> u = User.create!(name: 'doh')
   (0.1ms)  begin transaction
   (0.0ms)  rollback transaction
ActiveRecord::RecordInvalid: Validation failed: Emails User should have at least one email address.

[1] pry(main)> u = User.new(name: 'nice')
=> #<User id: nil, name: "nice", created_at: nil, updated_at: nil>
[2] pry(main)> u.emails.build(email_address: 'hello@world.blah')
=> #<UserEmail id: nil, user_id: nil, email_address: "hello@world.blah", created_at: nil, updated_at: nil>
[3] pry(main)> u.save!
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "users" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  [["created_at", "2014-07-26 10:14:52.184245"], ["name", "nice"], ["updated_at", "2014-07-26 10:14:52.184245"]]
  SQL (0.2ms)  INSERT INTO "user_emails" ("created_at", "email_address", "updated_at", "user_id") VALUES (?, ?, ?, ?)  [["created_at", "2014-07-26 10:14:52.189757"], ["email_address", "hello@world.blah"], ["updated_at", "2014-07-26 10:14:52.189757"], ["user_id", 5]]
   (0.7ms)  commit transaction
=> true
[4] pry(main)> u.emails.count
   (0.2ms)  SELECT COUNT(*) FROM "user_emails"  WHERE "user_emails"."user_id" = ?  [["user_id", 5]]
=> 1
[5] pry(main)> u.id
=> 5
[6] pry(main)> u.name
=> "nice"
[7] pry(main)> u.emails
=> [#<UserEmail id: 4, user_id: 5, email_address: "hello@world.blah", created_at: "2014-07-26 10:14:52", updated_at: "2014-07-26 10:14:52">]
[8] pry(main)> u
=> #<User id: 5, name: "nice", created_at: "2014-07-26 10:14:52", updated_at: "2014-07-26 10:14:52">


[19] pry(main)> UserEmail.create!(email_address: 'test@test.org')
   (0.1ms)  begin transaction
   (0.1ms)  rollback transaction
ActiveRecord::RecordInvalid: Validation failed: User can't be blank

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    我刚刚意识到我在存储层上添加一个 NOT NULL 约束就足够了。我可以删除 :user_id 存在验证并具有相同的一致性行为,但它现在可以工作。

    但是,从 Rails POW 来看,这看起来很老套。任何更好的解决方案仍然非常感谢......

    【讨论】:

      猜你喜欢
      • 2016-03-01
      • 1970-01-01
      • 2018-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-18
      相关资源
      最近更新 更多