1

I have looked at various answers to similar questions and haven't quite cracked it. A wine model is defined with has_one :register, :dependent => :destroy and rightly or wrongly I have added accepts_nested_attributes_for :register. A register is defined with belongs_to :wine.

The code within wines_controller.rb for create is:

def new
  @wine = Wine.new
  @register = Register.new

def create
  @wine = Wine.new(wine_params)
  @register = @wine.registers.build(register_params)
  respond_to do |format|
    if @wine.save 
    #success
    else
      format.json { render json: @wine.errors, status: :unprocessable_entity }
      format.json { render json: @register.errors, status: :unprocessable_entity }
    end
  end 
end

My form for creating a new wine has the following code:

<%= simple_form_for @wine do |f| %>  
# various working elements
<div class="field">
  <% f.fields_for :register do |r| %>
    <%= r.label :short_name %>
    <%= r.text_field :short_name %>
    <%= r.label :barcode %>
    <%= r.text_field :barcode %>
  <% end %>
</div>

When this form is called up no fields are created from the f.fields_for command but this block is executed because I can add test buttons within it to prove it is accessed.

If I try to create a wine I get the following error message:

undefined method `registers' for #<Wine:0x007f1204375330> Did you mean? register register= register_id

I believe that using .build is there to ensure data integrity: I don't want to create a wine that does not have a corresponding register. I have tried thinking about it nested attributes but that seems to be considered a bad plan by many. This current approach feels correct but I think I am missing some understanding of syntax at the very least.

At a later date it will be necessary to have other models linked to register that will not be associated to wines. I was considering a similar approach but I am happy to be told to rethink!

fatherdamo
  • 165
  • 1
  • 2
  • 13

2 Answers2

2

If I understand you correctly you have 2 issues:

Firstly fields for register aren't being displayed - this is partly because @wine.register is nil.

You should change your new action to:

def new
  @wine = Wine.new
  @wine.register = Register.new

In addition because you are using simple_form_for you will need to use simple_fields_for instead of fields_for

Your second issue that results in the exception tells you everything... you are trying to access @wine.registers, and not @wine.register

Change in your create method to:

  @register = @wine.register.build(register_params)

This will fix that issue ... however ... all you really need to do is build the @wine object from your params - your params should be configured to permit the right nested attributes - if it is set up correctly the register object will also be built when building the @wine object.

Your model is already set to accept_nested_attributes and thus will also validate and save the register object when calling @wine.save - no need to explicitly save the register object.

You should have something like:

def wine_params
    params.require(:wine).permit(
          :attribute1, :attribute2,
          register_attributes: [:id, :short_name, :barcode])
end
David
  • 3,510
  • 3
  • 21
  • 39
  • OK, thanks for the input. The first adjustment you suggest gives rise to ` ActiveModel::MissingAttributeError in WinesController#new ` and `can't write unknown attribute 'wine_id'` – fatherdamo Oct 12 '16 at 20:41
  • That error arises if I try to create a new wine entry – fatherdamo Oct 12 '16 at 20:43
  • could you clarify ... are we talking about the `@wine.register = Register.new` line? If so then check your Register table ... it should have a wine_id reference – David Oct 12 '16 at 20:46
  • The table `wines` does indeed have have a column `wine_id`, automatically created by Rails. The error message is pointing to the line `@wine.register = Register.new` – fatherdamo Oct 12 '16 at 20:49
  • I tried adding `wine_id into the attributes of `register` but happily that didn't fix it. I would only have been confused if it had! – fatherdamo Oct 12 '16 at 20:50
  • @henner66 This [answer](http://stackoverflow.com/questions/26788432/simple-fields-for-not-showing-up-rails-4) seems to suggest the reason that the `simple_forms_for` block is trying to iterate zero times. I've slept on it, tried all manner of combinations (adding `@wine.register.build` to `WinesController > def index ` but still no joy. – fatherdamo Oct 13 '16 at 08:15
  • This [answer that describes how `.build` works] ( http://stackoverflow.com/questions/783584/ruby-on-rails-how-do-i-use-the-active-record-build-method-in-a-belongs-to-rel) suggests to me I should include this line `@wine.register.build = Register.new` but his gives me an arror that states `undefined method 'build=' for nil:NilClass`. This suggests to me iterating zero times. Do you know why my use `.build` is incorrect? – fatherdamo Oct 13 '16 at 08:15
  • You should really ask this as a new question but the short answer is they are effectively equivalent. The question you posted is 7 years old and things have changed over rails versions. The other point is you aren't using it right - it should be just simply `@wine.register.build` – David Oct 13 '16 at 08:37
  • OK, I'll repost as a new question with current code and different versions tried. I understand much more, but clearly not enough. Yet. – fatherdamo Oct 13 '16 at 08:55
0

Try this

Wine and Register models

class Wine < ApplicationRecord
    has_one :register, inverse_of: :wine,  :dependent => :destroy
    accepts_nested_attributes_for :register
end

class Register < ApplicationRecord
    belongs_to :wine, inverse_of: :register
    validates_presence_of :wine
end

Wines Controller

class WinesController < ApplicationController
  def new
    @wine = Wine.new
    @wine.build_register
  end


  def create
    @wine = Wine.new(wine_params)

    if @wine.save
        redirect_to @wine
    else
        render :new
    end
  end

  private
    def wine_params
        params.require(:wine).permit(:name, register_attributes: [:simple_name])
    end
end

My wine_params are specific for

rails g model wine name:string
rails g model register name:string wine_id:integer

Lastly wine form should look like this

<%= form_for @wine do |f|%>
    <p>
        <%= f.label :name%>
        <%= f.text_field :name%>
    </p>

    <%= f.fields_for :register do |r|%>
    <p>
        <%= r.label :simple_name%>
        <%= r.text_field :simple_name%>
    </p>    
    <% end %>

    <%= f.submit %>
<% end %>

So you can modify wine_params and form partial for your application specifics

Emmanuel Mtali
  • 4,383
  • 3
  • 27
  • 53