jjzjj

ruby-on-rails - Ruby on Rails : How to validate nested attributes on certain condition?

coder 2025-06-01 原文

我有这些模型:

class Organisation < ActiveRecord::Base

  has_many    :people
  has_one     :address, :as         => :addressable,
                        :dependent  => :destroy
  accepts_nested_attributes_for :address, :allow_destroy => true

end

class Person < ActiveRecord::Base

  attr_accessible :first_name, :last_name, :email, :organisation_id, :address_attributes

  belongs_to  :user
  belongs_to  :organisation
  has_one     :address, :as         => :addressable,
                        :dependent  => :destroy
  accepts_nested_attributes_for :address, :allow_destroy => true

  # These two methods seem to have no effect at all!
  validates_presence_of :organisation,  :unless => "address.present?"
  validates_associated  :address,       :unless => "organisation.present?"

end

class Address < ActiveRecord::Base

  belongs_to :addressable, :polymorphic => true

  validates_presence_of :line1, :line2, :city, :zip

end

...以及这些观点:

_fields.html.erb :
<%= render 'shared/error_messages', :object => f.object %>
<fieldset>
<div class="left">
    <%= f.label :first_name %><br/>
    <%= f.text_field :first_name %>
</div>
<div>
    <%= f.label :last_name %><br/>
    <%= f.text_field :last_name %>
</div>
<div>
    <%= f.label :email %><br/>
    <%= f.text_field :email %>
</div>
<div>
    <%= f.label :organisation_id %><br/>
    <%= f.select(:organisation_id, current_user.organisation_names, {:include_blank => "--- None ---"}, :id => 'organisation_select') %>
</div>
</fieldset>

<%= f.fields_for :address do |address| %>
  <%= render 'shared/address', :f => address %>
<% end %>

_address.html.erb:
<fieldset id="address_fields">
<div>
    <%= f.label :line1 %>
    <%= f.text_field :line1 %>
</div>
<div>
    <%= f.label :line2 %>
    <%= f.text_field :line2 %>
</div>
<div>
    <%= f.label :zip %>
    <%= f.text_field :zip %>
</div>  
<div>
    <%= f.label :city %>
    <%= f.text_field :city %>
</div>  
</fieldset>

people_controller.rb :
def new
  puts params.inspect
  @person = Person.new(:organisation_id => params[:organisation_id])
  @person.build_address
  @title = "New person"
end

{"action"=>"new", "controller"=>"people"}

def edit
  puts params.inspect
  @title = @person.name
end

{"action"=>"edit", "id"=>"69", "controller"=>"people"}

def create
  puts params.inspect
  if params[:organisation_id]
    @person = current_user.organisations.build_person(params[:person])
  else
    @person = current_user.people.build(params[:person])
  end
  if @person.save
    flash[:success] = "Person created."
    redirect_to people_path
  else
    render :action => "new"
  end
end

{"commit"=>"Create", "action"=>"create", "person"=>{"last_name"=>"Doe", "organisation_id"=>"9", "email"=>"john.doe@email.com", "first_name"=>"John", "address_attributes"=>{"city"=>"Chicago", "zip"=>"12345", "line2"=>"Apt 1", "line1"=>"1 Main Street"}}, "authenticity_token"=>"Jp3XVLbA3X1SOigPezYFfEol0FGjcMHRTy6jQeM1OuI=", "controller"=>"people", "utf8"=>"✓"}

在我的 Person 里面模型我需要确保只有当一个人的organisation_id为空,该人的地址字段必须存在。

我试过这样的事情:
validates :address, :presence => true, :if => "organisation_id.blank?"

但它不起作用。

如何才能做到这一点?

谢谢你的帮助。

最佳答案

首先,我想确定你的意思是blank?而不是 present? .通常,我看到这个:

validate :address, :presence_of => true, :if => 'organisation.present?'

意思是,如果组织也存在,您只想验证地址。

关于,:accepts_nested_attributes_for ,您是通过传入嵌套的表单属性来使用此功能,还是诸如此类?我只是想确保您绝对需要使用此功能。如果您实际上没有处理嵌套的表单属性,则可以使用以下方法实现级联验证:
validates_associated :address

如果您确实需要使用 :accepts_nested_attributes ,请务必查看 :reject_if范围。基本上,如果某些条件适用,您可以完全拒绝添加属性(及其后代):
accepts_nested_attributes_for :address, :allow_destroy => true, :reject_if => :no_organisation

def no_organisation(attributes)
  attributes[:organisation_id].blank?
end

现在,如果以上都不适用,让我们来看看你的语法:

它应该可以工作,:if/:unlesssymbols, strings and procs .您不需要指向foreign_key,但可以通过指向来简化:
:if => "organisation.blank?"

您在 Address 模型中有其他验证,对吗?当您不希望地址被验证时,它会被验证吗?或者地址没有被验证?如果你能给我一些额外的细节,我可以帮你在控制台中测试它。

  • 为了让我自己更轻松:批量分配,我更改了 rails 配置:config.active_record.whitelist_attributes = false
  • 我创建了 a gist for you to follow along
  • 我也有一个示例项目。如果您有兴趣,请告诉我。

    基本点:
  • 将以下内容添加到 Person 以确保 Org 或 Address 有效:
    validates_presence_of :organisation, :unless => "address.present?"validates_associated :address, :unless => "organisation.present?"
  • 添加了对 Address 的验证以在 Org 不存在时触发错误:validates_presence_of :line1, :line2, :city, :zip
    我能够产生您正在寻求的要求。请看 at the gist I created我有一个完整的控制台测试计划。


  • 我加了 a controller file to the previous gist .

    概述:
  • 创建此人所需的全部内容是:@person = current_user.people.build(params[:person])
  • :organisation_id 将始终从 :person 参数节点中找到,如下所示:params[:person][:organisation_id]所以你如果永远不会是真的。


  • 我更新了要点,对 the controller 进行了必要的更改, the modelthe form .

    概述:
  • 您需要清理 Controller 。您正在使用 accepts_nested_attribute ,所以在 :create 中,你只关心 params[:person] .此外,在 render :new ,您需要设置部分将使用的任何实例变量。这是不是 回去通过:new行动。 :new:edit行动也需要简化。
  • 您的 Person 模型需要使用 :reject_if参数,因为地址字段返回到 :create 操作为 :address_attributes => {:line1 => '', :line2 => '', etc} .如果有任何值,您只想创建关联。那么你的 validates_presence_of:organisation会工作得很好。
  • 您的表单需要将组织 ID 传递给 Controller ​​,而不是组织名称

    尽在the gist


  • 应该是 the final gist .

    概述:
  • 在构建@person 后立即将以下内容添加到您的编辑操作中:

    @person.build_address 如果@person.address.nil?
    这确保您有地址输入,即使 @person.address 不存在。它不存在,因为 accepts_nested_attributes 上的 :reject_if 条件
  • 我干了 :reject_if 如下。它有点hacky,但有一些实用性:
    accepts_nested_attributes_for :address, :allow_destroy => true, :reject_if => :attributes_blank?
    
    def attributes_blank?(attrs)  
      attrs.except('id').values.all?(&:blank?)  
    end  
    

    一种。 attrs -> params[:person][:address] 的结果
    .except('id') -> 返回除 'id' 之外的所有键值
    C。 .values -> 将散列中的所有值作为数组返回
    d. .all? -> 数组中的所有元素是否满足以下检查
    e. &:blank -> 块的 ruby​​ 简写,像这样:all?{ |v| v.blank? }
  • 关于ruby-on-rails - Ruby on Rails : How to validate nested attributes on certain condition?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12770018/

    有关ruby-on-rails - Ruby on Rails : How to validate nested attributes on certain condition?的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. Ruby 解析字符串 - 2

      我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

    4. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    5. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    6. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    7. ruby-on-rails - rails : keeping DRY with ActiveRecord models that share similar complex attributes - 2

      这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby​​类,但是我如何得到ActiveRecord关联这个类模型

    8. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    9. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    10. ruby - 如何在 Ruby 中顺序创建 PI - 2

      出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

    随机推荐