7

I posted an earlier question about this and was advised to read lots of relevant info. I have read it and tried implementing about 30 different solutions. None of which have worked for me.

Here's what I've got.

I have a Miniatures model. I have a Manufacturers model. Miniatures have many manufacturers THROUGH a Productions model.

The associations seem to be set up correctly as I can show them in my views and create them via the console. Where I have a problem is in letting the Miniatures NEW and EDIT views create and update to the Productions table.

In the console the command @miniature.productions.create(manufacturer_id: 1) works, which leads me to believe I should be able to do the same in a form.

I THINK my problem is always in the Miniatures Controller and specifically the CREATE function. I have tried out a ton of other peoples solutions there and none have done the trick. It is also possible that my field_for stuff in my form is wrong but that seems less fiddly.

I've been stuck on this for days and while there are other things I could work on, if this association isn't possible then I'd need to rethink my entire application.

The form now creates a line in the Productions table but doesn't include the all important manufacturer_id.

Any help VERY much appreciated.

My New Miniature form

<% provide(:title, 'Add miniature') %>
<h1>Add a miniature</h1>

<div class="row">
  <div class="span6 offset3">
    <%= form_for(@miniature) do |f| %>
      <%= render 'shared/error_messages', object: f.object %>
      <%= f.label :name %>
      <%= f.text_field :name %>
       <%= f.fields_for :production do |production_fields| %>
      <%= production_fields.label :manufacturer_id, "Manufacturer" %>
      <%= production_fields.select :manufacturer_id, options_from_collection_for_select(Manufacturer.all, :id, :name) %>
      <% end %>
      <%= f.label :release_date %>
      <%= f.date_select :release_date, :start_year => Date.current.year, :end_year => 1970, :include_blank => true %>

      <%= f.submit "Add miniature", class: "btn btn-large btn-primary" %>
    <% end %>
  </div>
</div>

Miniatures controller

class MiniaturesController < ApplicationController
   before_action :signed_in_user, only: [:new, :create, :edit, :update]
   before_action :admin_user,     only: :destroy

  def productions
    @production = @miniature.productions
  end

  def show
    @miniature = Miniature.find(params[:id])
  end

  def new
    @miniature = Miniature.new 
  end

  def edit
    @miniature = Miniature.find(params[:id])
  end

  def update
    @miniature = Miniature.find(params[:id])
    if @miniature.update_attributes(miniature_params)
      flash[:success] = "Miniature updated"
      redirect_to @miniature
    else
      render 'edit'
    end
  end
  def index
    @miniatures = Miniature.paginate(page: params[:page])
  end

  def create
    @miniature = Miniature.new(miniature_params)
    if @miniature.save
      @production = @miniature.productions.create
      redirect_to @miniature
    else
      render 'new'
    end
  end

  def destroy
    Miniature.find(params[:id]).destroy
    flash[:success] = "Miniature destroyed."
    redirect_to miniatures_url
  end

private
    def miniature_params
      params.require(:miniature).permit(:name, :release_date, :material, :scale, :production, :production_attributes)
    end

    def admin_user
      redirect_to(root_url) unless current_user.admin?
    end

    def signed_in_user
      unless signed_in?
        store_location
        redirect_to signin_url, notice: "Please sign in."
      end
    end
end

Miniature model

class Miniature < ActiveRecord::Base
  has_many :productions, dependent: :destroy
  has_many :manufacturers, :through => :productions
  accepts_nested_attributes_for :productions

    validates :name, presence: true, length: { maximum: 50 }
    validates :material, presence: true
    validates :scale, presence: true
    validates_date :release_date, :allow_blank => true

  def name=(s)
    super s.titleize
  end

end

Production model

class Production < ActiveRecord::Base
    belongs_to :miniature
    belongs_to :manufacturer



end

Manufacturer model

class Manufacturer < ActiveRecord::Base
    has_many :productions
    has_many :miniatures, :through => :productions
    validates :name, presence: true, length: { maximum: 50 }
    accepts_nested_attributes_for :productions
end
Ossie
  • 1,103
  • 2
  • 13
  • 31
  • 1
    Haven't looked through this extensively, but you have an issue on your create action where the relation is being build on `@Miniature` (with a capital 'm') instead of `@miniature` (lower case 'm' with matches your instance variable @miniature)... – Helios de Guerra Oct 07 '13 at 16:21
  • Ah thanks for spotting that. I corrected it and it threw up an error along the lines of "Cannot create until after the parent is saved", so I moved the line down below the "if @miniature.save" line. Now it creates the line in the Productions table but doesn't fill in the manufacturer_id field. I have been here before unfortunately. – Ossie Oct 07 '13 at 16:40

1 Answers1

10

Instead of calling:

@production = @miniature.productions.create

Try Rails' "build" method:

def new
  @miniature = Miniature.new(miniature_params)
  @miniature.productions.build
end

def create
  @miniature = Miniature.new(miniature_params)
  if @miniature.save
    redirect_to @miniature
  else
    render 'new'
  end
end

Using the build method uses ActiveRecord's Autosave Association functionality.

See http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html

You also need to update your params method, e.g.

def miniature_params
  params.require(:miniature).permit(:name, :release_date, :material, :scale, productions_attributes: [:manufacturer_id])
end

Also your fields_for should be plural (I think)...

<%= f.fields_for :productions do |production_fields| %>
Helios de Guerra
  • 3,445
  • 18
  • 23
  • Yeah that allows me to shift the line up above the "if @miniature save" line but it works just the same. I get a new line in the table but it doesn't contain the manufacturer_id. It must either be that I need to compose my form differently or I need to force the controller to use that particular parameter. Oof. – Ossie Oct 07 '13 at 17:21
  • I'd tried various combinations in the params bit. This looks great but still doesn't pass the manufacturer_id to productions. It is very possible right? – Ossie Oct 07 '13 at 17:48
  • 1
    Can you tell from your log if its passing the parameter, but it is being filtered out? Or is it not passing it at all? Sorry I haven't been too much help, I try not to use nested forms in favor of using form objects... Check out pattern #3 on Form Objects, here: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ – Helios de Guerra Oct 07 '13 at 17:59
  • Interesting. The log has manufacturer_id under Paramters but before 'begin transaction' it gives 'Unpermitted parameters: production'. So then only reads 'INSERT INTO "productions" ("created_at", "miniature_id", "updated_at") VALUES (?, ?, ?) [["created_at", Mon, 07 Oct 2013 17:44:55 UTC +00:00], ["miniature_id", 89], ["updated_at", Mon, 07 Oct 2013 17:44:55 UTC +00:00]]'. So it isn't passing it. I just can't understand why it plays nice in console but not in forms. Had no idea there was alternative to nested forms. Will investigate. This ought to be workable though right? – Ossie Oct 07 '13 at 18:10
  • After some toggling of production/productions in the form I can report that in plural it doesn't see the manufacturer_id param at all and thusly doesn't throw up the 'Unpermitted parameters: production' error. Probably not progress? Found this [link] (http://karolgalanciak.com/blog/2013/10/06/structuring-rails-applications/?utm_content=bufferfc796&utm_source=buffer&utm_medium=twitter&utm_campaign=Buffer) also reccomending Form Objects but bit daunted. No step by step guides. Thanks so much by the way. – Ossie Oct 07 '13 at 18:53
  • 1
    What do your request params look like in your log w/ the fields_for productions being plural? – Helios de Guerra Oct 07 '13 at 19:12
  • 1
    Also, try `production_attributes` in the miniature params permit method instead of just `productions`... see latest edit – Helios de Guerra Oct 07 '13 at 19:31
  • I switched to production_attributes: and that works just the same. If I try productions plural in the fields_for, my dropdown doesn't appear at all so can't check the log. So I'm back to having 'Unpermitted parameters: production' before 'begin transaction' in the log. Head against wall. – Ossie Oct 07 '13 at 20:12
  • 1
    I think you definitely want `productions` plural in the fields_for. I think its some small issue that we're missing... Maybe the select isn't being properly formed or something. You might try replacing the select :manufacturer_id with a number_field and just type in the id and see what you get. Also, have you reviewed the Rails guide, http://guides.rubyonrails.org/form_helpers.html#building-complex-forms. If all else fails, maybe post a Gist of your current controller and views and I'll work thru it. We're making this harder than it should be somewhere along the line. – Helios de Guerra Oct 07 '13 at 20:40
  • Well productions plural loses my select. I'll have another look through the Rails Guide. The current branch is here https://github.com/WorldOfProper/mini_legions/tree/manufacturers but without that plural right now. – Ossie Oct 07 '13 at 20:47
  • 1
    Alright, I got it working for me... The reason you were losing your select, was that no production was initialized for a new Miniature instance... `@miniature = Miniature.new` needed to be followed by, `@miniature.productions.build`... See latest edit – Helios de Guerra Oct 07 '13 at 21:21
  • OHMIGOD that worked! You're a genius. The addition of the new productions meant that the preductions plural DID show the select and it worked. It works! This means I can go on with my whole project without starting again. You are my hero Helios de Guerra. – Ossie Oct 07 '13 at 23:19
  • 1
    :-) Glad to help. Those little things can be infuriating... Good luck w/ the rest of the project. – Helios de Guerra Oct 07 '13 at 23:25