简体   繁体   English

使用另外两个模型通过关联更新has_many中的连接模型

[英]Update join model in has_many through association, using the two other models

I've read through many other topics here ( 1 , 2 , 3 ...) but none really solved my problem. 我经过这里许多其他议题(读123 ...),但没有真正解决我的问题。

Here are my 3 models. 这是我的3个型号。

User
  has_many :memberships
  has_many :accounts, :through => :memberships
  accepts_nested_attributes_for :memberships
end

Account
  has_many :memberships
  has_many :users, :through => :memberships
  accepts_nested_attributes_for :memberships
end

Membership
  attr_accessible :account_id, :url, :user_id
  belongs_to :account
  belongs_to :user
end

As you can see, my join model Membership has an additional attribute: :url . 如您所见,我的联接模型成员资格还有一个属性:url

In my Accounts table, I store names of online services, such as GitHub, Stack Overflow, Twitter, Facebook, LinkedIn.. I have 9 in total. 在我的账户表中,我存储了在线服务的名称,例如GitHub,Stack Overflow,Twitter,Facebook,LinkedIn ..我总共有9个。 It's a fixed amount of accounts that I don't tend to update very often. 这是一个固定数量的帐户,我不经常更新。

In my User form, I'd like to create this: 在我的用户表单中,我想创建这个:

用户嵌套表单

The value entered in any of these field should be submitted in the Memberships table only, using 3 values: 在任何这些字段中输入的值应仅使用3个值在Memberships表中提交:

  • url (the value entered in the text field) url (在文本字段中输入的值)
  • user_id (the id of the current user form) user_id (当前用户表单的id)
  • account_id (the id of the related account, eg LinkedIn is '5') account_id (相关帐户的ID,例如LinkedIn为'5')

I have tried 3 options. 我试过3个选项。 They all work but only partially. 他们都工作但只是部分工作。

Option #1 选项1

<% for account in @accounts %>
  <%= f.fields_for :memberships do |m| %>
    <div class="field">
      <%= m.label account.name %><br>
      <%= m.text_field :url %>
    </div>
  <% end %>
<% end %>

I want to have 9 text field, one for each account. 我希望有9个文本字段,每个帐户一个。 So I loop through my accounts, and create a url field related to my memberships model. 所以我遍历我的帐户,并创建一个与我的会员资格模型相关的网址字段。

It shows my fields correctly on the first time , but the next time it'll display 81 fields: 第一次正确显示我的字段,但下次它将显示81个字段:

循环问题

Option #2 选项#2

<% @accounts.each do |account| %>
  <p>
    <%= label_tag(account.name) %><br>
    <%= text_field_tag("user[memberships_attributes][][url]") %>
    <%= hidden_field_tag("user[memberships_attributes][][account_id]", account.id) %>
    <%= hidden_field_tag("user[memberships_attributes][][user_id]", @user.id) %>
  </p>
<% end %>

I'm trying to manually enter the 3 values in each column of my Memberships tables. 我正在尝试在我的Memberships表的每一列中手动输入3个值。

It works but : 它有效但是:

  • displaying both account and user id's doesn't seem very secure (no?) 显示帐户和用户ID似乎不太安全(不?)
  • it will reset the fields everytime I edit my user 它会在我每次编辑用户时重置字段
  • it will duplicate the values on each submit 它将复制每次提交的值

Option #3 (best one yet) 选项#3(最好的一个)

<%= f.fields_for :memberships do |m| %>
  <div class="field">
    <%= m.label m.object.account.name %><br>
    <%= m.text_field :url %>
  </div>
<% end %>

I'm creating a nested form in my User form, for my Membership model. 我正在我的用户表单中为我的会员模型创建一个嵌套表单。

It works almost perfectly: 几乎完美地工作:

  • exactly 9 fields, one for each account 正好9个字段,每个帐户一个
  • no duplicates 没有重复

But , it only works if my Memberships table is already populated! 但是 ,它只有在我的会员资格表已经填充时才有效! (Using Option #2 for example). (例如,使用选项#2)。

So I tried building some instances using the UsersController: 所以我尝试使用UsersController构建一些实例:

if (@user.memberships.empty?)
  @user.memberships.build
end

But I still get this error for my m.label m.object.account.name line. 但是我的m.label m.object.account.name行仍然出现此错误。

undefined method `name' for nil:NilClass

Anyway, I'm probably missing something here about has_many through models. 无论如何,我可能在这里错过了关于has_many模型的内容。 I've managed to create has_and_belongs_to_many associations but here, I want to work on that join model (Membership), through the first model (User), using information about the third model (Account). 我已经设法创建了has_and_belongs_to_many关联,但在这里,我想通过第一个模型(User),使用有关第三个模型(Account)的信息来处理该连接模型(Membership)。

I'd appreciate your help. 我很感激你的帮助。 Thank you. 谢谢。

I would use the following data-design approach. 我会使用以下数据设计方法。 All users in your system should have the memebership entries for all possible accounts. 系统中的所有用户都应拥有所有可能帐户的会员条目。 The active configurations will have a value for the url field. 活动配置将具有url字段的值。

User
  has_many :memberships
  has_many :accounts, :through => :memberships
  has_many :active_accounts, :through => :memberships, 
            :source => :account, :conditions => "memberships.url IS NOT NULL"  

  accepts_nested_attributes_for :memberships
end

Now 现在

curent_user.active_accounts # will return the accounts with configuration
curent_user.accounts # will return all possible accounts

Add a before_filter to initialize all the memberships that a user can have. 添加before_filter以初始化用户可以拥有的所有成员资格。

class UsersController

  before_filter :initialize_memberships, :only => [:new, :edit]

private

  def initialize_memberships
    accounts =  if @user.accounts.present? 
      Account.where("id NOT IN (?)", @user.account_ids) 
    else
      Account.scoped
    end        
    accounts.each do |account|
      @user.memberships.build(:account_id => account.id)
    end
  end
end    

In this scenario you need to initialize the memeberships before the new action and all the memberships should be saved in the create action ( even the ones without url ). 在这种情况下,您需要在new操作之前初始化成员资格,并且所有成员资格应保存在create操作中(即使是没有url的成员资格)。

Your edit action doesn't need to perform any additional data massaging. 您的编辑操作不需要执行任何其他数据按摩。

Note: 注意:

I am suggesting this approach as it makes the management of the form/data straight forward. 我建议采用这种方法,因为它可以直接管理表单/数据。 It should only be used if the number of Account 's being associated is handful. 只有在关联的Account数量很少时才应该使用它。

in the controller, fetch the list of memberships for a particular user 在控制器中,获取特定用户的成员资格列表

# controller
# no need to make this an instance variable since you're using fields_for in the view
# and we're building additional memberships later
memberships = @user.memberships

then loop through each account and build a membership if the user has no membership for an account yet. 然后循环遍历每个帐户并在用户尚未拥有帐户成员资格的情况下构建成员资格。

# still in the controller
Account.find_each do |account|
  unless memberships.detect { |m| m.account_id == account.id }
    @user.memberships.build account_id: account.id
  end
end

then in your view, you change nothing :) 然后在你看来,你没有改变:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM