0

I have a little sample app where there are 3 models: Members, Groups and Subscriptions. The idea is that member can subscribe to groups.

ERD

class Member < ActiveRecord::Base
  has_many :subscriptions, dependent: :delete_all
  has_many :groups, through: :subscriptions

  attr_accessible :email

  validates :email, presence: true
end

class Group < ActiveRecord::Base
  has_many :subscriptions, dependent: :delete_all
  has_many :members, through: :subscriptions

  accepts_nested_attributes_for :subscriptions

  attr_accessible :name, :subscriptions_attributes

  validates :name, presence: true, uniqueness: true
end

class Subscription < ActiveRecord::Base
  belongs_to :group
  belongs_to :member

  attr_accessible :group_id, :introduction

  validates :group_id, presence: true
  validates :introduction, presence: true
end

I'm trying to create a form for new groups, and nest the introduction attribute inside.

My controller methods:

def new
  @group = Group.new
  @group.subscriptions.build
end

def create
  @member = Member.first
  @group = @member.groups.build(params[:group])

  if @group.save
    flash[:success] = "Saved"
    redirect_to group_path(@group)
  else
    render :new
  end
end

But it does not work. It throws the error group_id can't be blank. So I don't know how to assign the new group to the subscription.

Also, the member_id is being created as nil. But as you can see, I'm creating the group from the @member variable, so I think it should be initialized, but it does not.

Anyone can show me the light?

You can see the sample app here: https://github.com/idavemm/nested_form

David Morales
  • 17,816
  • 12
  • 77
  • 105

4 Answers4

0

Make sure all the attributes you're trying to assign are attr_accessible. You may just disable it and see if it works, or see at the warnings in the Rails server log.

Update: you should add accepts_nested_attributes_for to the Member model and use a multimodel form with fields_for.

Oscar Del Ben
  • 4,485
  • 1
  • 27
  • 41
0

I think you're thinking about your models in the wrong way. Will each member have a different introduction for each group. So, for example, will member1 have one introduction and member2 have a different introduction?

The Subscriptions model should store information about the relationship between and member and group. In that case, introduction would be better to have in the group model. The reason you are getting an error is because you are trying to create a subscription(when you set the introduction attribute) for a group that hasn't been made yet.

So, move introduction to the group model and then, if you want the creator of a group to be automatically subscribed to it (which you should), add the code to create a subscription to the controller in the create action after the record is saved. Then, on the subscription model, you can do cool things like having a state machine that tracks a member's status with the group (moderator, newbie, veteran member, etc).

GorrillaMcD
  • 1,884
  • 1
  • 14
  • 22
  • No! Each subscription has an introduction. Each member will have an introduction for each group. It's a classic has_many :through relationship. Introduction can't be moved to the group as it would represent a different thing. – David Morales Jun 18 '12 at 17:32
  • I see. Then who will set the introduction? For example, a group is created and then a few days later a member subscribes to the group, how will the introduction be set for that new member. – GorrillaMcD Jun 18 '12 at 17:42
  • That can be accomplished with another form (in fact, that part is what I have already programmed and works well). The introduction is the nested attribute inserted in the form, please take a look at the code that I linked (github) and you will see how it works. – David Morales Jun 18 '12 at 18:03
  • I looked at the code on github, but didn't see any other forms. I understand you are trying to set the introduction in the group's new.html.erb form. You can't create a new subscription for a group that hasn't been created yet. The group doesn't have an id until you save it to the db and group_id is required for the has_many :through association. – GorrillaMcD Jun 18 '12 at 18:19
  • Yes I know, but isn't that the function of the accepts_nested_attributes_for? Let's see if someone has a solution for this... – David Morales Jun 18 '12 at 18:21
  • The closest you could get to what you're trying to do (I think) would be to do a wizard-style form where the first step creates the group and on submit, redirects to the second step that sets the introduction. You could of course do that with AJAX as well to be more seamless. – GorrillaMcD Jun 18 '12 at 18:22
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/12710/discussion-between-gorrillamcd-and-david) – GorrillaMcD Jun 18 '12 at 18:25
0

After many hours of investigation and frustration, I reported it to Rails devs and I finally have a solution:

Rails 3 is unable to initialize group_id and member_id automatically in the way it is defined in the question.

So, for now, there are two ways to make it work:

  1. Add the member_id as a hidden field in the view.
  2. Change everything so is the Subscription model who has accepts_nested_attributes_for. That way, the new object to be created is the Subscription, and Group will be the nested model.

The first option has an important security hole, so I don't recommend it.

The second option, although not much logical, is the cleaner and supposedly the "Rails way" to fix this problem.

David Morales
  • 17,816
  • 12
  • 77
  • 105
0

I just ran into the same issue, the solution was to remove the presence validation from the related model (based on this question), in your case, remove:

validates :group_id, presence: true

Once that validation it's gone, everything runs like clockwork

Community
  • 1
  • 1
davidmh
  • 1,368
  • 13
  • 24