【问题标题】:Can I validates_uniqueness_of email in two models at once in rails?我可以在 Rails 中同时验证两个模型中的电子邮件吗?
【发布时间】:2017-12-13 23:30:19
【问题描述】:

我有一个客户端模型,当我创建一个新客户端时,它会使用客户端电子邮件创建一个用户。当我创建会员时也会发生同样的事情。我可以同时在客户端和用户中使用 validates_uniqueness_of 电子邮件吗?

或者我应该做一些类似的事情,在保存之前检查是否有用户使用相同的电子邮件,然后打印错误?

我试过了,还是不行

validate :uniqueness_of_user

  private

  def uniqueness_of_user
    @user = User.find_by_email(:email)
    if @user.present?
      errors.add(:email, "Bang!")
    end
  end

编辑:

这是控制器:

def create
    @affiliate = Affiliate.new(affiliate_params)
    respond_to do |format|
      if verify_recaptcha(model: @affiliate) && @affiliate.save
        @user = User.create!(user_parameter)
        pplicationMailer.confirmation(@affiliate).deliver_now

        format.html {redirect_to :back, notice: 'Thanks for your submission, we will be in touch shortly. Check your email for your affiliate number and password.' }
        format.json { render :show, status: :created, location: @affiliate }
      else
        format.html { render :signup, layout: "sign-ups"  }
        format.json { render json: @affiliate.errors, status: :unprocessable_entity }
      end
    end
  end

【问题讨论】:

  • 有一个唯一性验证器。你不需要自己写。此外,如果用户只能通过客户端创建,您也不需要在那里强制执行(无论如何都不会受到伤害)。验证看起来像validates :email, uniqueness: true
  • 如果某些用户已经存在,或者可以不同于客户端创建。我建议您在创建客户端之前在控制器中检查用户没有此电子邮件(并且仍然在两个模型中强制执行唯一性)。还将客户端和用户创建封装到transaction 中,并在同一个块中进行。 (例如在客户端控制器的创建操作中)
  • 仅在客户或附属公司内部验证。客户和附属公司都创建了一个用户。我的问题是一些附属公司会尝试为客户使用相同的电子邮件,并且由于用户已经存在,客户将被保存,但会出现错误,因为用户已经存在。因此,在创建用户或会员之前,我需要检查使用该电子邮件的用户是否存在
  • 可以通过两种方式创建用户。然后检查我的第二个选项。在创建之前,您必须检查用户中是否存在电子邮件。 (或者您不这样做:如果您强制执行唯一性,它将失败,您可以要求访问者提交另一封电子邮件)。只需确保在同一个 transaction 块中创建两个客户端/用户:如果创建用户失败,则不会创建客户端。
  • 我用创建操作编辑了问题

标签: ruby-on-rails validation


【解决方案1】:

我宁愿将 email_address 保存在不同的表中。例如:地址

class Address < ApplicationRecord

   belongs_to :affilat
   belongs_to :client

   validates :email,
              presence: true 
              uniqueness: true
end

并在此处使用嵌套形式保存日期。

【讨论】:

    【解决方案2】:

    查看下面的代码以获取有关此案例的好文章,我复制了代码,但要查看完整的解释,请查看文章链接:

    # app/models/concerns/validate_identifier_uniqueness_across_models.rb
    module ValidateIdentifierUniquenessAcrossModels
      extend ActiveSupport::Concern
    
      @@included_classes = []
    
      included do
        @@included_classes << self
        validate :identifier_unique_across_all_models
      end
    
      private
    
      def identifier_unique_across_all_models
        return if self.identifier.blank?
        @@included_classes.each do |klass|
          scope = klass.where(identifier: self.identifier)
          if self.persisted? && klass == self.class
            scope = scope.where('id != ?', self.id)
          end
          if scope.any?
            self.errors.add :identifier, 'is already taken'
            break
          end
        end
      end
    end
    
    # app/models/product.rb
    class Product < ActiveRecord::Base
      include ValidateIdentifierUniquenessAcrossModels
    end
    
    # app/models/category.rb
    class Category < ActiveRecord::Base
      include ValidateIdentifierUniquenessAcrossModels
    end
    

    参考:
    https://www.krautcomputing.com/blog/2015/03/29/rails-validate-uniqueness-across-models/

    更新:
    解决方案 1 如果同时保存数据,那么最好将一起保存的数据包含在事务中,如下所示:

      def create
        email_validity
        @affiliate = Affiliate.new(affiliate_params)
        respond_to do |format|
          ActiveRecord::Base.transaction do
            if verify_recaptcha(model: @affiliate) && @affiliate.save
    
          @user = User.create!(user_parameter)
          if @user.valid?
            ApplicationMailer.confirmation(@affiliate).deliver_now
    
            format.html {redirect_to :back, notice: 'Thanks for your submission, we will be in touch shortly. Check your email for your affiliate number and password.'}
            format.json {render :show, status: :created, location: @affiliate}
          else
            email_validity = false
            raise ActiveRecord::Rollback
          end
        else
    
          format.html {render :signup, layout: "sign-ups"}
          format.json {render json: @affiliate.errors, status: :unprocessable_entity}
        end
      end
    
      if email_validity == false
        respond_to do |format|
          format.html {redirect_to :back, notice: 'Sorry, email is in use!'}
          # you can add your json return as you like
        end
      end
    end
    

    结束

    参考:
    http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html

    解决方案 2 在保存到关联公司或创建用户之前,请检查两个表中是否存在控制器中的电子邮件。

    【讨论】:

    • 我尝试将标识符更改为电子邮件,将产品/类别更改为客户/附属公司。但它不验证。它仍然会创建会员并给我一个页面错误,说该电子邮件的用户已经存在,因为我也对用户进行了验证。
    • @Allan 你能显示将两个数据保存在一起的代码吗?
    • 使用你的代码我仍然得到一个错误,但后来我改为` if User.find_by_email(user_params[:email]).nil? @user = User.create!(user_parameter) ` 现在我得到了回滚事务,并且会员没有保存。但通知没有显示,我在控制台中得到了这个:No template found for AffiliatesController#create, rendering head :no_content Completed 204 No Content in 3022ms (ActiveRecord: 4.0ms)
    • 我已经更新了答案,一旦提出回滚事务,它就会出块。我添加了一个标志email_validity 来检查并返回正确的响应。
    • 在创建任何人之前,您为什么不尝试检查两个表的电子邮件唯一性?
    猜你喜欢
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    • 1970-01-01
    • 2019-04-09
    • 2010-10-21
    • 2023-03-29
    • 2020-03-09
    • 2019-06-23
    相关资源
    最近更新 更多