手动实例化多态子的多个实例

时间:2014-07-30 01:50:54

标签: ruby-on-rails rails-activerecord polymorphic-associations

我的模型中有多个社交网络:

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

我想手动声明使用的种类。也许我需要替换fields_for?

<%= 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 %>

但我只保存了一个值并显示了所有四个ID。

enter image description here

当对每个单独的项目执行fields_for时,我得到repeated kinds and repeated values

注意:此配置文件表单中只应有一种与此类型相关联。


我相信我需要使用像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方法?

2 个答案:

答案 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)

Rails中的手动多态创建

好吧我发现了解决方案。这就是我所拥有的。

<强>模型/ 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 然后将对象交还给我们进行写入/更新。

这为创建和更新多态儿童维护了一个视图。因此,它允许在您的个人资料和社交关系中使用各种视图。