Rails - 新用户构建依赖电子邮件并验证两者?

Rails - new User builds dependent Email and validates both?

我正在构建一个快速的 Rails 项目,允许用户管理他们的电子邮件地址。用户可以有很多电子邮件,但是其中一个(并且只有一个)电子邮件必须标记为 'primary'(用于登录),并且没有主电子邮件就不能存在用户。

我一直在努力让它正常工作 - 它对我来说似乎太循环了。我需要建立一个用户,然后是电子邮件,但我不想将用户保存到数据库中,除非电子邮件是有效的,直到用户被保存(因为 validates :user, presence: true约束)。

接受嵌套资源似乎不适用于 .new(适用于 .create),如果我的电子邮件未通过验证,用户仍显示为有效。

在尝试从单一表单中为 building/validating multiple/dependent 模型找到好的资源(或 SO 问题)时遇到了困难。

最 Rails 的方法是什么?

用户

has_many :emails
has_one :primary_email, -> { where(primary: true) }, class_name: "Email"

accepts_nested_attributes_for :primary_email

validates :first_name, presence: true
validates :last_name, presence: true
validates :birthday, presence: true
validates :password_digest, presence: true

电子邮件

belongs_to :user

validates :user, presence: true
validates :address, presence: true, uniqueness: {
  case_sensitive: false
}

用户控制器

def new
  @user = User.new
end

def create
  @user = User.new(user_params)
  if @user.save
    # do something
  else
    # show @user.errors
  end
end

private

def user_params
  params.require(:user).permit(
    :first_name,
    :last_name,
    :birthday,
    :password,
    :password_confirmation,
    :primary_email_attributes => [:address]
  )
end

编辑

Email 模型还包含以下字段:

class User
  user has_many :emails
  user has_one :primary_email, -> { where(primary: true) }, class_name: "Email", autosave: true

  after_initialize {
     build_primary_email if new_record?
  }
end


class Email
    # use gem https://github.com/balexand/email_validator
    validates :my_email_attribute, :email => true
end

因此,在用户初始化其构建 primary_email 之后,该记录已经关联,或者至少在可以保存的情况下将会关联。自动保存功能非常棒——如果由于验证错误而无法保存主电子邮件,则用户也不能。应该开箱即用,我现在在公共汽车上,无法检查。干杯

更多信息:http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html

如果任何关联的验证失败,它们的错误消息将应用于父级。这意味着,父级模型(在您的情况下是用户)有错误,这就是为什么无法保存的原因!这就是你要找的。

我会将主要电子邮件存储为公共字段,并以其他方式存储其他电子邮件。我更愿意将额外的电子邮件存储在另一个字段中,即数组而不是关联的 table。您不应将主要电子邮件存储在另一个 table 中。试想一下,每次您需要授权用户或只是收到他的电子邮件时,您都会对 db 执行额外的请求。

这个月前post。

在不将主要电子邮件存储为用户属性的情况下,使用户和电子邮件在不同模型中保持规范化的解决方案是使用 inverse_of:

User.rb

class User < ActiveRecord::Base
  has_many :emails, inverse_of: :user, dependent: :destroy
  accepts_nested_attributes_for :emails
  validates :emails, presence: true
end

Email.rb

class Email < ActiveRecord::Base
  belongs_to :user, inverse_of: :emails
  validates :user, presence: true
end

这允许使用内存中的对象执行验证,而不是通过数据库调用(即正在验证对象关联,而不是数据库中是否存在 id/record)。因此它们都通过了验证,并且都可以保存在同一个交易中。

参见:https://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations