class Social < ActiveRecord::Base
enum kind: [ :twitter, :google_plus, :facebook, :linked_in, :skype, :yahoo ]
belongs_to :sociable, polymorphic: true
validates_presence_of :kind
validates_presence_of :username
end
<%= f.fields_for :socials do |a| %>
<%= a.hidden_field :kind, {value: :facebook} %> Facebook ID: <%= a.text_field :username, placeholder: "kind" %>
<%= a.hidden_field :kind, {value: :twitter} %> Twitter ID: <%= a.text_field :username, placeholder: "kind" %>
<%= a.hidden_field :kind, {value: :google_plus} %> Google ID: <%= a.text_field :username, placeholder: "kind" %>
<%= a.hidden_field :kind, {value: :linked_in} %> Linked In ID: <%= a.text_field :username, placeholder: "kind" %>
<% end %>
注意:此配置文件表单中只应有一种与此类型相关联。
我相信我需要使用像find_or_create_by
这样的东西,以确保在编辑器中只创建和加载每种类型中的一种,因为fields_for只是按照保存的顺序加载所有内容。也许会展示Rails find_or_create by more than one attribute?如何与kind
一起使用。
我需要确保产品只保存各种以及编辑时;它将按种类正确加载,而不仅仅是belonging to
。
因为在我的示例中,所有四个都会显示编辑页面上第一个字段中保存的内容,但很明显,目前无法确保kind
。
我想在application_controller.rb
def one_by_kind(obj, kind)
obj.where(:kind => kind).first_or_create
end
我如何用这个替换fields_for方法?
答案 0 :(得分:-1)
这里有一些问题: 1 - 当你只能为它选择一个枚举状态时,你将如何定义具有多种类型或类型的社交? 2 - 当然,您不能使用:username作为模型中所有字段的名称。 Rails只会理解最后一个作为有效的一个。所有其他人都将被覆盖。
但你可以解决这个问题,简化你的策略:
忘记将表单中的种类设置为隐藏字段,这真的不会按照您想要的方式工作。
1 - 产品具有社交性,而不是具有多种社交性的产品,而是保留与该模型的社交网络相关的所有数据。
class Product < ActiveRecord::Base
has_one :social
accepts_nested_attributes_for :social
#...
end
2 - 您的表单会更加简单,您可以决定外观的顺序。您也可以在编辑视图中将其重新用作部分:
#your form header with whatever you need here...
<%= f.text_field(:name) %>
<%= f.fields_for :social do |a| %>
Facebook ID: <%= a.text_field :facebook_username %>
Yahoo ID: <%= a.text_field :yahoo_username %>
Linkedin ID: <%= a.text_field :linkedin_username %>
Twitter ID: <%= a.text_field :twitter_username %>
<% end %>
3 - 在新方法中,您需要初始化has_one关系:
def new
@product = Product.new
@product.build_social
end
4 - 如果你正在使用Rails 4,请不要忘记将允许的属性列入白名单:
def product_params
params.require(:product).permit([:name, socials_attributes: [:twitter_username,
:facebook_username, :linkedin_username, :google_username, :skype_username, :yahoo_username] ])
end
5 - 然后在您的控制器中,您可以根据填充的字段为模型分配多种类型。在模型中使用before_save回调。检查你的领域的东西,如
def assign_social_kinds
if !self.twitter_username.blank? #or some more refined validation of it
self.twitter = true
end
if !self.skype_username.blank?
self.skype = true
end
end
答案 1 :(得分:-1)
好吧我发现了解决方案。这就是我所拥有的。
<强>模型/ profile.rb 强>
class Profile < ActiveRecord::Base
has_many :socials, as: :sociable, dependent: :destroy
accepts_nested_attributes_for :socials, allow_destroy: true
end
<强>模型/ social.rb 强>
class Social < ActiveRecord::Base
enum kind: [ :twitter, :google_plus, :facebook, :linked_in, :skype, :yahoo ]
belongs_to :sociable, polymorphic: true
validates_presence_of :kind
validates_presence_of :username
end
<强>控制器/ profiles_controller.rb 强>
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
before_action :set_social_list, only: [:new, :edit]
def new
@profile = Profile.new
end
def edit
end
private
def set_profile
@profile = Profile.find(params[:id])
end
def set_social_list
@social_list = [
["linkedin.com/pub/", :linked_in],
["facebook.com/", :facebook],
["twitter.com/", :twitter],
["google.com/", :google_plus]
]
end
def profile_params
params.require(:profile).permit(
:socials_attributes => [:id,:kind,:username,:_destroy]
)
end
end
我已经缩短了实际文件,因为这里有相关内容。您将需要为您的用例允许的任何其他参数。其余的可以保持不变。
<强>控制器/ application_controller.rb 强>
class ApplicationController < ActionController::Base
def one_by_kind(obj, kind)
obj.where(:kind => kind).first || obj.where(:kind => kind).build
end
helper_method :one_by_kind
end
这就是魔术将要发生的地方。它是在 .where(...)。first_or_create 之后设计的,但使用的是build,所以我们不必声明构建对于profile_controller中的socials对象。
(多态性大多数未记录的方面。)
<强>视图/简档/ _form.html 强>
<% @social_list.each do |label, entry| %>
<%= f.fields_for :socials, one_by_kind(@profile.socials, @profile.socials.kinds[entry]) do |a| %>
<%= a.hidden_field :kind, {value: entry} %><%= label %>: <%= a.text_field :username %>
<% end %>
<% end %>
@social_list 在 profile_controller 中定义,是一个标签&amp;的数组。善良的一对。因此,当每个人通过时,我们在 application_controller 中定义的 one_by_kind 方法会寻找第一个具有正确种类的多态儿童已命名条目。如果找不到数据库记录,则会构建它。 one_by_kind 然后将对象交还给我们进行写入/更新。
这为创建和更新多态儿童维护了一个视图。因此,它允许在您的个人资料和社交关系中使用各种视图。