我有这些模特:
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?"
但它不起作用。
如何做到这一点?
感谢您的帮助。
答案 0 :(得分:25)
首先,我想确定您的意思是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/:unless
取symbols, strings and procs。您不需要指向foreign_key,但可以通过指向:
:if => "organisation.blank?"
您在地址模型中有其他验证,对吗?地址是否在您不希望时进行验证?或者地址未经过验证?如果你能给我一些额外的细节,我可以帮你在控制台中测试它。
config.active_record.whitelist_attributes = false
我也有一个示例项目。如果您有兴趣,请告诉我。
基本要点:
向Person添加以下内容以确保Org或Address有效:
validates_presence_of :organisation, :unless => "address.present?"
validates_associated :address, :unless => "organisation.present?"
在地址中添加了验证,以便在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])
params[:person][:organisation_id]
所以你永远不会是真的。我更新了要点,对the controller,the model和the form进行了必要的更改。
概述:
accepts_nested_attribute
,因此在:create中,您只关心params[:person]
。此外,在render :new
中,您需要设置部分将使用的任何实例变量。 NOT 会返回:new
操作。 :new
和:edit
操作也需要简化。:reject_if
参数,因为Address字段将以:address_attributes => {:line1 => '', :line2 => '', etc}
的形式返回到:create action。你只想创建关联,如果有任何值。然后,validates_presence_of
:organisation
可以正常使用。您的表单需要将组织ID传递给控制器,而不是组织名称
全部在the gist
概述:
在构建@person后立即将以下内容添加到您的编辑操作中:
@ person.build_address if @ 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
- &gt; params的结果[:person] [:address]
湾.except('id')
- &gt;返回除“id”之外的所有键值
C。 .values
- &gt;将散列中的所有值作为数组返回
d。 .all?
- &gt;数组中的所有元素是否满足以下检查
即&:blank
- &gt;块的红宝石简写,如下所示:all?{ |v| v.blank? }
答案 1 :(得分:0)
你确定你不是故意的:
validates :address, :presence => true, :if => organisation_id.nil?
答案 2 :(得分:0)
更简单的方法可能是添加自定义验证器。这非常容易,而且您不必迷惑语法或尝试弄清楚为什么Rails的魔术无法正常工作。
在我的Person模型中,我需要确保仅当一个人的Organisation_id为空白时,该人的地址字段才必须存在。
class Person < ActiveRecord::Base
...
validate :address_if_organisation_id_is_present
private
def address_if_organisation_id_is_present
return true unless organisation_id
errors.add(:address, "cannot be blank") unless address
end
end
添加到模型的错误将阻止其保存。注意:您可能希望使用address.blank?
或address.empty?
,如在其他答案中所讨论的那样,但是您可以针对所需的行为进行定义。