【问题标题】:Conditional model validations dependent on controller依赖于控制器的条件模型验证
【发布时间】:2012-11-24 22:54:58
【问题描述】:

我有一个用户模型,其中存储用户地址等。创建帐户(user_sessions 控制器)时,我只想验证他们的姓名是否存在,地址是可选的。

当他们升级他们的帐户(升级控制器)时,我想验证是否输入了他们的完整地址。

有没有一种优雅的方法可以基于控制器进行条件验证?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 model controller


    【解决方案1】:

    您是否尝试过创建一个继承旧模型的新模型。所以你可以:

    class User < ActiveRecord::Base
      attr_accessible :name, :address
      validates_presence_of :name
    end
    
    class UpgradedUser < User
      validates_precense_of :address
    end
    

    由于它继承自 User 类,因此 UpgradedUser 类对地址和名称都有验证。而且这个解决方案仍然只使用一个数据库表。因为它们仍然是一个表,所以您必须添加 :account_status 列或其他内容来确定帐户是否已升级。

    在您的升级用户控制器中,您将有一个标准的更新方法:

    class upgraded_users_controller   
    
      def update
        @upgraded_user = UpgradedUser.find params[:id]
        respond_to do |format|
          if @upgraded_user.update_attributes params[:user]
            format.html { redirect_to @upgraded_user }
          end
        end
    

    此方法将访问同一个 User 表,就像 users_controller 一样,但它只是通过 UpgradedUser 模型这样做,因此将检查保存时的验证器是否存在名称和地址。

    【讨论】:

    • 您能否详细说明一下,我已经尝试实现它,但正在努力。在我的升级控制器中,如何在通过升级用户添加其他地址详细信息之前检索现有用户详细信息?
    • @pingu 更新了我上面的答案^^。但基本上你对待它的方式与从普通的users_controller 更新完全一样,只是这次你通过UpgradedUser 模型访问表。
    【解决方案2】:

    装饰器/实例扩展

    您可以使用装饰器模式来完成此操作。实例扩展是另一种选择。因为我们使用的是 ruby​​,所以这里的实例扩展方式更有趣:

    class User < ActiveRecord::Base
      validate :decorator_validations
    
      protected
    
      def decorator_validations
        # noop at this point
      end
    end
    
    
    module User::Upgrader
    
      def upgrade!
        # do whatever upgrade operation is needed
        save
      end
    
      protected
    
      def decorator_validations
        super
        self.errors.add(:address, 'needs to be provided') unless self.address.present?
      end
    end
    

    在你的控制器中:

    def upgrade
      @user = User.find(params[:id])
      @user.extend User::Upgrader
      if @user.upgrade!
        # success case
      else
        # failure case
      end
    end
    

    命令模式

    另一种方法是使用命令模式:

    class UserUpgrader
    
     def initialize(user)
       @user = user
     end
    
     def call
       validate_upgrade_state
       return false if @user.errors.present?
       @user.update_attribute(:upgraded, true)
     end
    
     protected
    
     def validate_upgrade_state
       @user.errors.add(:address, 'must be present') unless @user.address.present?
     end
    end
    

    在这种情况下,您的控制器将如下所示:

    @user = User.find(params[:id])
    if UserUpgrader.new(@user).call
     # success case
    else
     # failure case
    end
    

    这两种解决方案的好处是它们非常可测试。

    【讨论】:

      猜你喜欢
      • 2019-03-11
      • 2010-12-28
      • 2021-11-16
      • 2017-09-15
      • 1970-01-01
      • 2017-05-18
      • 2019-01-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多