【问题标题】:Rails: Custom validations using ActiveModelRails:使用 ActiveModel 的自定义验证
【发布时间】:2015-06-23 18:59:53
【问题描述】:

我目前正在尝试使自定义验证与日期输入一起工作,但不幸的是,它似乎不起作用。

应用程序内部有两个页面,索引页面和搜索页面。在索引页面内有一个输入日期的文本字段。我正在使用 Chronic gem 将文本解析为日期。如果日期无效,Chronic 将返回 nil。如果有效,则重定向到搜索页面并显示日期。

到目前为止我写的代码似乎不能正常工作,但我想要实现的是..

1) 验证 Chronic 不返回 nil

2) 验证日期是否大于今天的日期

请注意,我没有使用数据库,我只是希望能够在没有 ActiveRecord 的情况下验证输入的日期。如果有人可以帮助我,我们将不胜感激。

views/main/index.html.erb

<%= form_tag({controller: "main", action: "search"}, method: "get") do %>
    <%= label_tag(:q, "Enter Date:") %>
    <%= text_field_tag(:q) %>
    <%= submit_tag "Explore", name: nil %>
<% end %>

views/main/search.html.erb

&lt;%= @show_date %&gt;

ma​​in_controller.rb

def search

    passed_info = Main.new

    if passed_info.valid_date?
        @show_date = passed_info
    else
        flash[:error] = "Please enter correct date!"
        render :index => 'new'
    end

end

models/main.rb

class Main

  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend  ActiveModel::Naming

  attr_accessor :q

  validates_presence_of :q

  def initialize(params={})
    params.each do |attr, value|
    self.public_send("#{attr}=", value)
    end if params
  end

  def persisted?
    false
  end

  def valid_date?
    require 'chronic'
    if Chronic.parse(q).nil? || Chronic.parse(q) < Time.today
        errors.add(:q, "is missing or invalid")
    end
  end

end

编辑:

这就是问题所在......

本地主机:3000

然后它重定向到..

localhost:3000/main/search?utf8=%E2%9C%93&q=invalid+date+test

没有验证,没有日期,什么都没有..

【问题讨论】:

  • 现在valid_date? 不返回布尔值。如果没有错误,它不应该返回true,如果有则返回false?
  • 你的代码怎么不工作?是否存在您可以复制(并在此处显示)失败的情况?
  • @ABMagil 谢谢,我刚刚用问题的图片更新了我的答案。告诉我这是否有帮助
  • 您的控制器代码与 Rails 标准相差甚远。浏览the guides 可能会很有帮助

标签: ruby-on-rails ruby validation activemodel chronic


【解决方案1】:

问题

对返回值要更加小心。当您尝试使用if valid_date? 保护您的控制器时,您正在做的是检查valid_date? 是否返回false。如果解析失败,则返回值是errors.add 的输出,这又是Array#<< 的输出。相关地,输出不是nilfalse,因此它的计算结果为真,因此if 子句通过并且您继续前进。

潜在解决方案

您可能想让Rails Validation Framework 为您做更多的工作。不要将valid_date? 视为控制器调用的公共方法,而是调用由ActiveModel::Validations 添加的valid? 方法。 valid? 返回一个布尔值,基于所有模型验证是否通过。因此,您可以像 Rails 方式一样,在控制器中调用 if model_instance.valid?

这让您只需在模型中编写验证器方法来表达您尝试编写的逻辑。现在,您在一个方法中拥有所有日期验证逻辑,并带有一条错误消息。相反,您可以放置​​两个方法,这会添加更多描述性的单个错误方法。

class YourClass
  include ActiveModel::Validations

  validate :date_is_valid
  validate :date_not_before_today

  private
  def date_is_valid
    if Chronic.parse(q).nil?
      errors.add(:q, "Date is invalid")
    end
  end
  def date_not_before_today
    if Chronic.parse(q) < Date.today
      errors.add(:q, "Date cannot be before today")
    end
  end
end

【讨论】:

    【解决方案2】:

    正如 ABMagil 正确建议的那样,我想发布我的答案的完整解决方案。事实上,这个答案可以真正适用于任何想要使用 ActiveModel 进行验证的人,无论是否涉及慢性宝石或日期。可以说它可以作为一个有效的模板。

    坦率地说,我的大部分错误都来自于当时我对自己真正想要达到的目标的理解非常差。大多数代码都需要进行重大重构,请参阅下面我必须进行的更新。我试图尽可能地保留代码。

    解决办法:

    views/main/index.html.erb

    <%= form_for @search, url: { action: "search" }, 
                          html: { method: :get } do |f| %>
    
      # Displays error messages if there are any.
      <% if @search.errors.any? %>
        The form contains <%= pluralize(@search.errors.count, "error") %>.<br />
        <% @search.errors.each do |attr, msg| %>
          <%= msg %><br />
        <% end %>
      <% end %>
    
      <%= f.label :q, "Enter Date:" %>
      <%= f.text_field :q %>
      <%= f.submit "Explore", :class => 'submit' %>
    <% end %>
    

    views/main/search.html.erb - 和以前一样

    &lt;%= @show_date %&gt;

    ma​​in_controller.rb

    def index
      # Initializes a string from the form to a variable.
      @search = Search.new
    end
    
    def search
      # Retrieves the input from the form.
      @search = Search.new(params[:search])
    
      # Checks for validity, 
      # If valid, converts a valid string into a date.
      # Redirects to search.html.erb 
      # If not valid, renders a new index.html.erb template.
      if @search.valid?
        @show_date = (Chronic.parse(params[:search][:q])).to_date   
      else
        render :action => 'index'   
      end
    end
    

    models/main.rb

    class Main
      include ActiveModel::Validations
      include ActiveModel::Conversion
      extend  ActiveModel::Naming
    
      # Accepts the passed attribute from the form.
      attr_accessor :q
    
      # If form is submitted blank, then output the prompting message.
      validates_presence_of :q, :message => "This text field can't be blank!"
      # Two custom validations that check if passed string converts to a valid date.
      validate :date_is_valid
      validate :date_not_before_today
    
      # Initializes the attributes from the form.
      def initialize(attributes = {})
        attributes.each do |name, value|
          send("#{name}=", value)
        end
      end
    
      # Checks for persistence, i.e. if it's a new record and it wasn't destroyed. 
      # Otherwise returns false.
      def persisted?
        false
      end
    
      # ABMagil's code used for custom date validations
      private
        require 'chronic'
        def date_is_valid
          if Chronic.parse(q).nil?
            errors.add(:base, "Date is invalid")
          end
        end
        def date_not_before_today
          if !Chronic.parse(q).nil?
            if Chronic.parse(q) < Date.today
              errors.add(:base, "Date cannot be before today")
            end
          end
        end
    end
    

    结果:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-05
      • 2011-06-21
      • 2018-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多