如果已经存在,则不要构建另一个关联

时间:2015-05-01 12:21:45

标签: ruby-on-rails ruby-on-rails-4 activerecord nested-forms has-many

我在使嵌套表单工作时遇到了一些问题。在下面的示例中,我有一个用户可以为帖子定义多个自定义标签。用户应该能够为每个特定标签输入值。

因此,一个帖子可以有多个标签,但每个标签只能有一个值!例如:Post可以有一个名为“Date”的标签,也有一个名为“Mood”的标签。对于两个标签,应该只有一个值。

问题是当用户创建标签-let说“日期”时 - 应该只能为此特定标签的此帖子输入一个值。因此,如果给出了日期值,则表单不应再次为日期构建另一个字段。 (帖子已有日期)

  1. 用户创建自定义标签(可行)
  2. 在帖子的编辑页面上,用户可以看到他在步骤1中创建的标签(此作品)
  3. 用户可以为每个标签输入一个值(这是问题)
  4. 我有以下型号:

    class User < ActiveRecord::Base
      has_many :posts
      has_many :labels
    end
    
    class Post < ActiveRecord::Base
      has_many :label_values
      belongs_to :user
      accepts_nested_attributes_for :label_values, allow_destroy: true
    end
    
    class Label < ActiveRecord::Base
      has_many :label_values
      belongs_to :user
    end
    
    class LabelValue < ActiveRecord::Base
      belongs_to :post
      belongs_to :label
    end
    

    在我的控制器中我有

    class PostsController < ApplicationController
    
      def edit
        @labels = current_user.labels.all
        @post.label_values.build
      end
    
    end
    

    我的表格:

    = simple_form_for @post do |f|
      =f.fields_for :label_values do |s|
        =s.association :label, :include_blank => false
        =s.input :value
     = f.button :submit
    

    像这样,每次用户输入特定标签的值时,下次再次构建新的label_value时,这不是我想要的。对于每个标签,用户应该能够输入一个值。

2 个答案:

答案 0 :(得分:0)

如果您打算使用Label将元数据附加到Post,您可能需要考虑使用如下关系:

class User < ActiveRecord::Base
    has_many :user_labels # Labels which have been used by this user.
    has_many :labels, through: :user_labels
    has_many :posts
end

class Post < ActiveRecord::Base
    has_many :post_labels
    belongs_to :user, as: :author
    has_many :labels, through: :post_labels
    accepts_nested_attributes_for :post_labels

    def available_labels
        Label.where.not(id: labels.pluck(:label_id))
    end
end

class Label < ActiveRecord::Base
    # @attribute name [String] The name of the label
    has_many :user_labels
    has_many :post_labels
    has_many :users, through: :user_labels
    has_many :posts, through: :post_labels
    validates_uniqueness_of :name
end

# Used to store label values that are attached to a post
class PostLabel < ActiveRecord::Base
    belongs_to :post
    belongs_to :label
    validates_uniqueness_of :label, scope: :post # only one label per post
    # @attribute value [String] the value of the label attached to a post
end

# Used for something like displaying the most used labels by a user  
class UserLabel < ActiveRecord::Base
    belongs_to :user
    belongs_to :label
end

在此设置中,标签在Stackoverflow上有点像“标签”。每个单独的标签在系统中都是唯一的,但可能会被许多用户附加到许多帖子上。

请注意validates_uniqueness_of :label, scope: :post验证,以确保这一点。

附加到代码的实际值存储在value的{​​{1}}属性中。这种方法的一个主要缺点是,您真的受到PostLabel PostLabel列类型的限制,可能需要进行类型转换。我真的不会像你的例子那样使用日期,因为很难根据日期进行查询。

value

这只是一些自以为是的建议:

要为帖子添加新标签,我会在已分配的标签下方添加文字输入。 我会使用def PostController def new @available_labels = Label.all() end def edit @post = Post.joins(:labels).find(params[:id]) @available_labels = @post.available_labels end def create @post = Post.new(create_params) if (@post.save) @post.labels.each do |l| @post.user.labels << l end # ... else # ... end end private def create_params params.permit(:post).allow(:post_labels) end end 自动填充。并过滤已存在于表单中的输入。您可以通过向GET /labels发送ajax请求,允许用户动态创建新标签。

答案 1 :(得分:0)

我终于来了。在我创建的Post模型中:

def empty_labels
    Label.where.not(:id => labelValue.select(:label_id).uniq)
end

def used_simfields
    LabelValue.where(post_id: id)
end

然后在视图中我做了:

= simple_form_for @post do |f|

  -@post.used_labels.each do |used_label|
    =f.fields_for :label_values, used_label do |old_label|
      =old_label :value

  -@post.empty_labels.each do |empty_label|
    =empty_label.fields_for :label_values, @post.label_values.new do |new_label|
      =new_label.association :label
      =new_label.input :value

我非常确定有更好的方法来实现这一点,所以非常欢迎新的想法。